feat:跟新组织架构

This commit is contained in:
leilei
2025-09-24 17:51:19 +08:00
parent 293951a610
commit 121fc5ea19
22 changed files with 1418 additions and 13 deletions

9
package-lock.json generated
View File

@@ -10,6 +10,7 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@element-plus/icons-vue": "^2.0.10", "@element-plus/icons-vue": "^2.0.10",
"@msgpack/msgpack": "^3.1.2",
"autoprefixer": "^10.4.21", "autoprefixer": "^10.4.21",
"axios": "^0.27.2", "axios": "^0.27.2",
"code-inspector-plugin": "^0.20.12", "code-inspector-plugin": "^0.20.12",
@@ -602,6 +603,14 @@
"@jridgewell/sourcemap-codec": "^1.4.14" "@jridgewell/sourcemap-codec": "^1.4.14"
} }
}, },
"node_modules/@msgpack/msgpack": {
"version": "3.1.2",
"resolved": "https://registry.npmmirror.com/@msgpack/msgpack/-/msgpack-3.1.2.tgz",
"integrity": "sha512-JEW4DEtBzfe8HvUYecLU9e6+XJnKDlUAIve8FvPzF3Kzs6Xo/KuZkZJsDH0wJXl/qEZbeeE7edxDNY3kMs39hQ==",
"engines": {
"node": ">= 18"
}
},
"node_modules/@nodelib/fs.scandir": { "node_modules/@nodelib/fs.scandir": {
"version": "2.1.5", "version": "2.1.5",
"resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",

View File

@@ -10,6 +10,7 @@
}, },
"dependencies": { "dependencies": {
"@element-plus/icons-vue": "^2.0.10", "@element-plus/icons-vue": "^2.0.10",
"@msgpack/msgpack": "^3.1.2",
"autoprefixer": "^10.4.21", "autoprefixer": "^10.4.21",
"axios": "^0.27.2", "axios": "^0.27.2",
"code-inspector-plugin": "^0.20.12", "code-inspector-plugin": "^0.20.12",

19
src/api/coordinate.js Normal file
View File

@@ -0,0 +1,19 @@
import request from '@/utils/request'
// 获取组织列表
export function getDirectories(data) {
return request({
url: `/api/v1/auth/directories`,
method: 'get',
params:data
})
}
// 获取指定目录下的用户列表
export function getDirectoriesUsers(directory_uuid,data) {
return request({
url: `/api/v1/auth/directories/${directory_uuid}/users`,
method: 'get',
params:data
})
}

View File

@@ -22,7 +22,7 @@ export function login(username, password) {
// 获取用户详细信息 // 获取用户详细信息
export function getInfo(userUid) { export function getInfo(userUid) {
return request({ return request({
url: `/api/v1/auth/user/${userUid}`, url: `/api/v1/auth/users/${userUid}`,
method: 'get' method: 'get'
}) })
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 535 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 939 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 676 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 777 B

View File

@@ -20,6 +20,16 @@ const router = createRouter({
path: "/login", path: "/login",
component: () => import("@/views/login.vue"), component: () => import("@/views/login.vue"),
}, },
{
path: "/coordinate",
children: [
{
path: '',
name: "Coordinate",
component: () => import("@/views/coordinate/personnelList/index.vue")
}
]
},
// 错误页面路由 // 错误页面路由
{ {
path: "/:pathMatch(.*)*", path: "/:pathMatch(.*)*",

View File

@@ -35,11 +35,12 @@ class MQTTClient {
// 消息分发 // 消息分发
this.client.on("message", (topic, payload) => { this.client.on("message", (topic, payload) => {
try { try {
const message = JSON.parse(payload.toString()) // const message = JSON.parse(payload.toString())
// 遍历所有订阅主题,执行通配符匹配 // 遍历所有订阅主题,执行通配符匹配
this.messageHandlers.forEach((handlers, subTopic) => { this.messageHandlers.forEach((handlers, subTopic) => {
if (this.topicMatch(subTopic, topic)) { if (this.topicMatch(subTopic, topic)) {
handlers.forEach((handler) => handler(message, topic)) // handlers.forEach((handler) => handler(message, topic))
handlers.forEach((handler) => handler(payload, topic))
} }
}) })
} catch (err) { } catch (err) {

View File

@@ -99,11 +99,13 @@ service.interceptors.request.use(
service.interceptors.response.use( service.interceptors.response.use(
(response) => { (response) => {
console.log(response,'response')
// 1. 检查响应是否存在 // 1. 检查响应是否存在
if (!response) { if (!response) {
ElMessage.error('无响应数据'); ElMessage.error('无响应数据');
return Promise.reject(new Error('无响应数据')); return Promise.reject(new Error('无响应数据'));
} }
console.log(response.data,'response.data')
// 2. 安全获取响应数据和状态码 // 2. 安全获取响应数据和状态码
const responseData = response.data || {}; const responseData = response.data || {};
const statusCode = response.status; const statusCode = response.status;
@@ -124,6 +126,7 @@ service.interceptors.response.use(
return Promise.resolve(responseData); return Promise.resolve(responseData);
case 401: case 401:
console.log('未授权', responseData)
return handleUnauthorized().then(() => { return handleUnauthorized().then(() => {
return Promise.reject({ code: 401, message: '未授权' }); return Promise.reject({ code: 401, message: '未授权' });
}); });
@@ -143,6 +146,13 @@ service.interceptors.response.use(
console.error('请求错误:', error); console.error('请求错误:', error);
let { message } = error; let { message } = error;
let code = error?.response?.status || -1; let code = error?.response?.status || -1;
console.log(code,'code')
if(code == 401) {
return handleUnauthorized().then(() => {
return Promise.reject({ code: 401, message: '未授权' });
});
}
if (message == 'Network Error') { if (message == 'Network Error') {
message = '后端接口连接异常'; message = '后端接口连接异常';

View File

@@ -1,6 +1,7 @@
import { mqttClient } from "./mqtt"; import { mqttClient } from "./mqtt";
import { getWhiteboardShapes, getWhiteboardHistory } from "@/views/custom/api"; import { getWhiteboardShapes, getWhiteboardHistory } from "@/views/custom/api";
import { useMeterStore } from '@/stores/modules/meter'; import { useMeterStore } from '@/stores/modules/meter';
import { encode, decode } from '@msgpack/msgpack'
const meterStore = useMeterStore(); const meterStore = useMeterStore();
meterStore.initUdid(); meterStore.initUdid();
@@ -43,11 +44,14 @@ export const WhiteboardSync = {
// 订阅当前房间 // 订阅当前房间
const topic = `xSynergy/ROOM/${roomUid}/whiteboard/#`; const topic = `xSynergy/ROOM/${roomUid}/whiteboard/#`;
mqttClient.subscribe(topic, async (shapeData) => { mqttClient.subscribe(topic, async (shapeData) => {
// console.log(shapeData, 'shapeData++格式装换')
const shapeDataNew = decode(shapeData);
// console.log(shapeDataNew, '格式解码')
try { try {
isRemote = true; isRemote = true;
// 如果 shape 来自本地用户,则跳过 // 如果 shape 来自本地用户,则跳过
if (shapeData.user_uid === localUid) return; if (shapeDataNew.user_uid === localUid) return;
const res = await getWhiteboardHistory({ after_timestamp: shapeData.created_at }, roomUid); const res = await getWhiteboardHistory({ after_timestamp: shapeDataNew.created_at }, roomUid);
if (res.meta.code === 200) { if (res.meta.code === 200) {
canvasInstance.addShape(res.data.shapes); canvasInstance.addShape(res.data.shapes);
} else { } else {
@@ -62,7 +66,7 @@ export const WhiteboardSync = {
console.log("✅ 已订阅:", topic); console.log("✅ 已订阅:", topic);
} catch (err) { } catch (err) {
console.log("初始化多人同步失败:",err) console.log("初始化多人同步失败:", err)
// console.error("❌ 连接或订阅失败:", err); // console.error("❌ 连接或订阅失败:", err);
} }
@@ -75,7 +79,6 @@ export const WhiteboardSync = {
if (shape.user_uid && shape.user_uid === localUid) return; if (shape.user_uid && shape.user_uid === localUid) return;
shape.room_uid = roomUid; shape.room_uid = roomUid;
try { try {
await getWhiteboardShapes(shape, roomUid); await getWhiteboardShapes(shape, roomUid);
} catch (err) { } catch (err) {

View File

@@ -0,0 +1,596 @@
<template>
<div>
<!-- v-loading="leftListLoading || loading" -->
<div class="left-list" v-loading="leftListLoading || loading">
<div class="list-tab">
<div
:class="'list-tab-item ' + (leftTab == 1 ? 'pitch-on' : '')"
@click="() => (leftTab = 1)"
>
<img src="@/assets/images/cooponents-tab3.png" v-if="leftTab == 1" />
<img src="@/assets/images/cooponents-tab4.png" v-else />
</div>
<div
:class="'list-tab-item ' + (leftTab == 2 ? 'pitch-on' : '')"
@click="() => (leftTab = 2)"
>
<img src="@/assets/images/cooponents-tab2.png" v-if="leftTab == 2" />
<img src="@/assets/images/cooponents-tab1.png" v-else />
</div>
</div>
<div class="list-content">
<div class="content-top-input">
<el-input
v-model="queryFrom.nickName"
placeholder="搜索成员"
type="text"
prefix-icon="Search"
@change="searchList"
/>
</div>
<div class="content-datapicker" v-if="leftTab == 1">
<el-date-picker
v-model="queryFrom.leftDatePicker"
type="datetimerange"
:shortcuts="shortcuts"
range-separator=""
start-placeholder="开始时间"
end-placeholder="结束时间"
format="YYYY-MM-DD HH:mm"
value-format="YYYY-MM-DD HH:mm"
@change="searchList"
/>
</div>
<el-scrollbar class="left-list-scrollbar" v-if="isShow && leftTab == 1">
<div
class="content-list"
v-infinite-scroll="infinite"
v-if="dataList?.length"
>
<div
v-for="(item, index) in dataList"
:key="index"
class="content-list-item"
@click="updateDetail(item)"
:style="
item.assistanceId == assistanceId
? 'border-color: #409EFF; '
: ''
"
>
<div class="list-item-top">
<span>
{{ parseTime(item.beginTime, '{m}月{d}日') }}
{{ weekName[new Date(item.beginTime).getDay()] }}
</span>
<span>
{{ parseTime(item.beginTime, '{y}年') }}
</span>
</div>
<div class="list-item-content">
<div class="list-item-content-text">
<div style="display: flex; flex-wrap: wrap">
<span
v-for="(items, indexs) in item.assistanceMemberList"
:key="indexs"
>
{{
items.nickName +
(indexs + 1 == item.assistanceMemberList.length
? ''
: '、')
}}
</span>
</div>
<span>
发起人{{ item.initiatorName ? item.initiatorName : '' }}
</span>
<span>
时间{{
parseTime(item.beginTime, '{h}:{i}') +
' ~ ' +
(item.endTime ? parseTime(item.endTime, '{h}:{i}') : '')
}}
</span>
</div>
</div>
</div>
</div>
<div style="text-align: center" v-if="dataList?.length">
<p v-if="more">Loading...</p>
<p v-else>No more</p>
</div>
<div v-else class="list-empty">
<el-empty description="暂无记录" />
</div>
</el-scrollbar>
<div v-if="!isShow && leftTab == 1" class="list-empty">
<el-empty description="暂无记录" />
</div>
<el-scrollbar
class="left-list-scrollbar1"
height="calc(100vh - 120px)"
v-if="leftTab == 2"
>
<el-tree
ref="treeRef"
lazy
:load="HandleLoadNode"
:filter-node-method="filterNode"
highlight-current
:props="treeProps"
style="width: 100%"
@node-click="updateDetail"
>
<template #default="{ data }">
<div class="tree-item">
{{ data.name }}
</div>
</template>
</el-tree>
</el-scrollbar>
</div>
</div>
</div>
</template>
<script setup>
import {
// getAssistanceList,
getDirectories,
getDirectoriesUsers
} from '@/api/coordinate.js'
import { nextTick, reactive, toRefs, watch, onMounted } from 'vue'
// 接收 props
const props = defineProps({
loading: {
type: Boolean,
default: true,
},
})
// 定义 emit
const emit = defineEmits(['updateDetail', 'updateTab'])
// state
const state = reactive({
isFirst: true,
leftTab: 2,
queryFrom: {
pageNum: 1,
pageSize: 10,
nickName: '',
leftDatePicker: null,
},
leftListLoading: true,
loading: false,
dataList: [],
more: false,
isShow: false,
shortcuts: [
{
text: '本周',
value: () => {
const now = new Date()
const nowDaty = now.getDay()
const start = new Date(now)
start.setDate(now.getDate() - nowDaty)
const end = new Date(start)
end.setDate(start.getDate() + 6)
return [start, end]
},
},
{
text: '最近三周',
value: () => {
const now = new Date()
const nowDaty = now.getDay()
let end = new Date(now)
end.setDate(now.getDate() + (6 - nowDaty))
const start = new Date(end)
start.setDate(start.getDate() - 20)
return [start, end]
},
},
{
text: '本月',
value: () => {
const now = new Date()
const start = new Date(now.getFullYear(), now.getMonth(), 1)
const end = new Date(now.getFullYear(), now.getMonth() + 1, 0)
return [start, end]
},
},
],
weekName: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
treeRef: null,
treeProps: {
children: 'users',
label: 'name',
value: 'uid',
isLeaf: (node) => {
if(node.uid) return true
},
},
assistanceId: '',
})
/**
* 树状列表筛选
*/
const filterNode = (value, data) => {
if (!value) return true
return data.name.includes(value)
}
/**
* 搜索框变化
*/
const searchList = () => {
if (state.leftTab == 1) {
state.queryFrom.pageNum = 1
state.dataList = []
getList()
} else {
console.log('treeRef.filter',state.treeRef)
state.treeRef.filter(state.queryFrom.nickName)
}
}
/**
* 变更详情
*/
const updateDetail = (item) => {
if (state.leftTab == 1) {
state.assistanceId = item.assistanceId
emit('updateDetail', item)
} else {
if (item.uid) {
emit('updateDetail', item)
}
}
}
/**
* 触底加载
*/
// const infinite = () => {
// if (state.more) {
// state.queryFrom.pageNum++
// getList()
// }
// }
/**
* 协作记录
*/
const getList = async () => {
try {
state.leftListLoading = true
let query = structuredClone(state.queryFrom)
if (query.leftDatePicker?.length) {
query.beginSignTime = query.leftDatePicker[0]
query.endSignTime = query.leftDatePicker[1]
}
delete query.leftDatePicker
let infoData = await getAssistanceList({ ...query })
state.dataList = infoData.rows.length
? state.dataList.concat(infoData.rows)
: []
if (state.isFirst) {
emit('updateDetail', state.dataList.length ? state.dataList[0] : null)
state.assistanceId = state.dataList.length
? state.dataList[0].assistanceId
: ''
state.isFirst = false
}
state.more = state.dataList.length < infoData.total
state.isShow = Boolean(state.dataList.length)
state.leftListLoading = false
} catch (err) {
console.log(err)
state.leftListLoading = false
}
}
/**
* 通讯录 人员信息树
*/
const HandleLoadNode = async (node, resolve) => {
if(node.level === 0){
loadNode(resolve)
}else if(node.level === 1){
loadNode(resolve,node.data.directory_uid)
}
}
const loadNode = async(resolve,id)=>{
try {
state.leftListLoading = true
if(!id){
let res = await getDirectories({level:1})
resolve(res.data)
}else{
let res = await getDirectoriesUsers(id,{directory_uuid:id})
resolve(res.data)
}
state.leftListLoading = false
} catch (error) {
console.log(error)
state.leftListLoading = false
}
}
// try {
// if (userList.data.sub_units?.length) {
// state.dataList = userList.data.sub_units
// let user = getUser(state.dataList)
// emit('updateDetail', user)
// } else {
// emit('updateDetail', null)
// }
// state.leftListLoading = false
// }
const getUser = (list) => {
for (let i = 0; i < list.length; i++) {
if (list[i].type == 2) {
return list[i]
} else if (list[i].children?.length) {
let user = getUser(list[i].children)
if (user != null) {
return user
}
}
}
}
/**
* 监听 props.loading
*/
watch(
() => props.loading,
(newValue) => {
state.loading = newValue
}
)
/**
* 监听 tab 切换
*/
watch(
() => state.leftTab,
(newValue) => {
emit('updateTab', newValue)
state.dataList = []
state.isFirst = true
state.queryFrom = {
pageNum: 1,
pageSize: 10,
nickName: '',
leftDatePicker: null,
}
if (newValue == 1) {
state.isShow = false
getList()
} else {
HandleLoadNode()
}
}
)
onMounted(() => {
state.dataList = []
state.isFirst = true
// getList()
})
/**
* 暴露给模板
*/
const {
isFirst, leftTab, queryFrom, leftListLoading, loading,
dataList, more, isShow, shortcuts, weekName,
treeRef, treeProps, assistanceId
} = toRefs(state)
</script>
<style lang="scss" scoped>
.flex {
display: flex;
justify-content: center;
align-items: center;
}
.left-list {
display: flex;
height: calc(100vh - 40px);
.list-tab {
@extend .flex;
justify-content: flex-start;
flex-direction: column;
margin-right: 6px;
.list-tab-item {
width: 50px;
height: 50px;
cursor: pointer;
img {
width: 50px;
height: 50px;
}
}
}
.list-content {
@extend .flex;
justify-content: flex-start;
flex-direction: column;
width: calc(100% - 56px);
box-shadow: 0px 5px 15px 0px rgba(153, 153, 153, 0.3);
.content-top-input {
@extend .flex;
width: 100%;
height: 50px;
padding: 6px 20px;
background: #167bff;
}
.content-datapicker {
width: 100%;
}
.left-list-scrollbar {
width: 100%;
height: calc(100vh - 170px);
padding: 15px 0;
}
.left-list-scrollbar1 {
width: 100%;
height: calc(100vh - 120px);
margin: 15px 0;
}
.content-list {
width: 100%;
padding: 0 15px;
}
.content-list-item {
width: 100%;
margin-bottom: 15px;
background: #f5f7fa;
border: 1.5px solid #c9d4e6;
cursor: pointer;
.list-item-top {
@extend .flex;
justify-content: space-between;
width: 100%;
height: 50px;
padding: 0 13px;
background: #c9d4e6;
span {
color: #333333;
font-size: 16px;
}
}
.list-item-content {
@extend .flex;
justify-content: flex-start;
align-items: flex-start;
padding: 13px;
background: #f5f7fa;
img {
width: 27px;
height: 27px;
border-radius: 50%;
margin-right: 10px;
}
.list-item-content-text {
@extend .flex;
flex-direction: column;
align-items: flex-start;
div {
@extend .flex;
justify-content: flex-start;
span {
margin: 0;
color: #333;
font-size: 16px;
}
}
span {
display: inline-block;
margin-top: 6px;
color: #999;
font-size: 14px;
}
}
}
}
}
}
.list-empty {
@extend .flex;
width: 100%;
height: calc(100vh - 170px);
}
.tree-item {
@extend .flex;
justify-content: flex-start;
height: 45px;
.tree-item-img1 {
width: 22px;
height: 22px;
}
.tree-item-text1 {
margin-left: 10px;
color: #333333;
font-size: 18px;
font-weight: 600;
}
.tree-item-text2 {
color: #333333;
font-size: 18px;
}
.tree-item-text3 {
margin-left: 15px;
color: #999999;
font-size: 18px;
}
}
::v-deep .el-tree-node__content {
@extend .flex;
justify-content: flex-start;
height: 45px;
}
::v-deep .list-content .el-input__wrapper {
height: 38px;
border-radius: 19px;
}
::v-deep .content-datapicker .el-date-editor {
width: 100%;
height: 50px;
background: #e6f1ff;
border-radius: 0;
border: none;
box-shadow: none;
}
::v-deep .left-list-scrollbar .el-scrollbar__wrap {
height: calc(100vh - 170px);
}
::v-deep .left-list-scrollbar1 .el-scrollbar__wrap {
height: calc(100vh - 120px);
}
</style>

View File

@@ -0,0 +1,755 @@
<template>
<div class="app-container" v-loading="load" :element-loading-text="loadText">
<el-row :gutter="6" style="padding: 0 10px; background: #fefefe">
<el-col :xs="24" :sm="24" :md="8" :lg="6">
<leftTab
@updateDetail="updateDetail"
@updateTab="updateTab"
:loading="!detail?.appId && !detail?.userId && isShow"
/>
</el-col>
<el-col :xs="24" :sm="24" :md="16" :lg="18">
<div
class="right-content"
>
<!-- v-loading="!detail?.appId && !detail?.userId && isShow" -->
<div class="right-content-title">
{{ tabValue == 1 ? '协作信息' : '员工信息' }}
</div>
<div
class="agency-detail-massage-cont right-content-message"
v-if="isShow && tabValue == 1"
>
<div class="agency-detail-cont-item">
<span class="agency-detail-item-title">发起人</span>
<span class="agency-detail-item-content">
{{ detail.initiatorName }}
</span>
</div>
<div class="agency-detail-cont-item">
<span class="agency-detail-item-title">协作时间</span>
<span class="agency-detail-item-content">
<!-- {{
parseTime(detail.beginTime, '{y}年{m}月{d}日') +
' ' +
weekName[new Date(detail.beginTime).getDay()] +
' ' +
parseTime(detail.beginTime, '{h}:{i}')
}} -->
</span>
</div>
<div class="agency-detail-cont-item">
<span class="agency-detail-item-title">成员</span>
<span
class="agency-detail-item-content"
v-if="detail?.assistanceMemberList?.length"
style="display: flex; flex-wrap: wrap"
>
<span
v-for="(items, indexs) in detail.assistanceMemberList"
:key="indexs"
>
{{
items.nickName +
(indexs + 1 == detail.assistanceMemberList.length
? ''
: '、')
}}
</span>
</span>
</div>
<div class="agency-detail-cont-item">
<span class="agency-detail-item-title">协作时长</span>
<span class="agency-detail-item-content">
<!-- {{ getTime() }} -->
</span>
</div>
</div>
<div class="right-content-file" v-if="isShow && tabValue == 1">
<el-row :gutter="15">
<el-col :xs="24" :sm="24" :md="16" :lg="18">
<div class="content-file-video">
<div class="file-top">协作视频</div>
<div class="file-video-bottom">
<!-- autoplay="autoplay" -->
<video
v-if="
detail.remoteVideoFile?.prefix &&
detail.remoteVideoFile.path
"
:src="
detail.remoteVideoFile.prefix +
detail.remoteVideoFile.path
"
id="videoPlayer"
loop
controls
></video>
<div v-else class="video-null">暂无视频</div>
</div>
</div>
</el-col>
<el-col :xs="24" :sm="24" :md="8" :lg="6">
<div>
<div class="file-top">
附件({{
detail?.fileList?.length ? detail.fileList.length : 0
}}
</div>
<div class="content-file-list">
<el-scrollbar
class="file-list"
height="calc(100vh - 380px)"
>
<div
class="file-list-content"
v-if="detail?.fileList?.length"
>
<div
class="file-list-item"
v-for="(item, index) in detail.fileList"
:key="index"
>
<div class="file-list-item-icon"></div>
<div class="file-list-item-text">
<div class="list-item-text text-out-of-hiding-1">
{{ item.name }}
</div>
<!-- <el-icon
:size="18"
color="#0d74ff"
style="cursor: pointer"
@click="clickDetail(item)"
>
<View />
</el-icon> -->
<el-link
:href="item.prefix + item.path"
type="primary"
target="_blank"
:underline="false"
>
<el-icon
:size="18"
color="#0d74ff"
style="cursor: pointer"
>
<Download />
</el-icon>
</el-link>
<el-icon
:size="18"
color="#FF4646"
style="cursor: pointer"
@click="clickDeleteFile(item)"
>
<Delete />
</el-icon>
</div>
</div>
</div>
</el-scrollbar>
</div>
</div>
</el-col>
</el-row>
</div>
<div
class="message-user"
v-else-if="isShow && tabValue == 2"
style="height: calc(100vh - 90px)"
>
<div class="message-user-card">
<div class="user-card-nickName">
<img v-if="detail.avatar" :src="detail.avatar" />
<img v-else src="@/assets/images/profile.jpg" />
<span>{{ detail.nickName || detail.name || '暂无信息' }}</span>
</div>
<div class="user-card-information">
<div class="user-information-item">
<div class="user-information-title">
<img src="@/assets/images/user-information1.png" alt="" />
<span>性别</span>
</div>
<div class="user-information-text">
<!-- <dict-tag
v-if="detail.sex"
:options="sys_user_sex"
:value="detail.sex"
/>
<div v-else>
{{ '暂无' }}
</div> -->
</div>
</div>
<div class="user-information-item">
<div class="user-information-title">
<img src="@/assets/images/user-information2.png" alt="" />
<span>手机号</span>
</div>
<div class="user-information-text">
{{ detail.phonenumber || '暂无' }}
</div>
</div>
<div class="user-information-item">
<div class="user-information-title">
<img src="@/assets/images/user-information3.png" alt="" />
<span>邮箱</span>
</div>
<div class="user-information-text">
{{ detail.email || '暂无' }}
</div>
</div>
<div class="user-information-item">
<div class="user-information-title">
<img src="@/assets/images/user-information4.png" alt="" />
<span>所属部门</span>
</div>
<div class="user-information-text">
{{ detail.dept?.deptName || '暂无' }}
</div>
</div>
</div>
<div class="user-card-btn">
<el-button type="primary" @click="clickInitiate">
发起协作
</el-button>
</div>
</div>
</div>
<div v-else class="message-null">
<el-empty description="暂无内容" />
</div>
</div>
</el-col>
</el-row>
<!-- <el-dialog
v-model="inviteDialog"
title="远程协作"
width="400px"
:close-on-press-escape="false"
:close-on-click-modal="false"
:show-close="false"
>
<div>
<div style="width: 100%; margin-bottom: 30px; font-size: 20px">
"
{{
socketInformation.data?.inviteNickName
? socketInformation.data?.inviteNickName
: ''
}}
" 邀请您参加远程协作
</div>
<div style="text-align: center">
<el-button
size="large"
type="danger"
style="font-size: 16px"
@click="clickRefuseJoin"
>
拒 绝
</el-button>
<el-button
size="large"
type="primary"
style="font-size: 16px"
@click="clickJoin"
>
加 入
</el-button>
</div>
</div>
</el-dialog> -->
</div>
</template>
<script setup>
import { onActivated, onMounted, reactive, toRefs, watch, getCurrentInstance } from 'vue'
// import {
// getFileList,
// getRemoveSolution,
// getUserStatus,
// refuseAssistance,
// } from '@/api/module/cooperation/v1/index'
// import { getAppId, getModuleId } from '@/utils/index'
import leftTab from './components/leftTab/index.vue'
import { getInfo } from '@/api/login.js'
// import useUserStore from '@/store/modules/user'
// import { getToken } from '@/utils/auth'
// import useWebSocketStore from '@/store/modules/WebSocket'
// import usePermissionStore from '@/store/modules/permission'
// import logo from '@/assets/logo/logo.png'
// import { getRtcSetDetail } from '@/api/conferencingRoom'
const { proxy } = getCurrentInstance()
// const WebSocketStore = useWebSocketStore()
// const { sys_user_sex } = proxy.useDict('sys_user_sex')
const state = reactive({
detail: {},
weekName: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
tabValue: 2,
isShow: true,
load: false,
loadText: '数据加载中',
isLinkKnow: 'F',
socketInformation: null,
inviteDialog: false,
cooperation: import.meta.env.VITE_APP_COOPERATION_TYPE,
})
/** 查看文件详情 */
const clickDetail = (item) => {
// let urls = item.prefix + item.path
// let status = proxy.judgeType(urls)
// if (status == 2 || status == 1) {
// state.imgMp4Ref.showModel('查看', urls)
// } else if (status == 3) {
// state.documentRef.showModel('查看', urls)
// } else if (status == 4) {
// state.model3dRef.showModel('查看', import.meta.env.VITE_APP_FILE_API + item.path)
// }
}
/** 发起协作邀请 */
const clickInitiate = () => {
// state.loadText = '初始化协作房间中'
// state.load = true
// getUserStatus({ userIds: state.detail.userId })
// .then((res) => {
// res.data.forEach((i) => { i.status = 1 })
// if (res.data.every((i) => i.status == 1)) {
// proxy.$modal.msgSuccess('邀请已发送')
// if (state.cooperation == 'skip') {
// getRtcSetDetail().then((res) => {
// const queryParams = new URLSearchParams({
// type: 1,
// userList: JSON.stringify([{ nickName: state.detail.nickName, userId: state.detail.userId }]),
// appid: getAppId(),
// Modinstid: getModuleId(),
// token: getToken(),
// userId: useUserStore().userId,
// }).toString()
// let url = import.meta.env.VITE_APP_ENV == 'development'
// ? `/ar/app/conferencingRoom?${queryParams}`
// : `${res.data.webMeetingUrl}?${queryParams}`
// window.open(url, '_blank')
// })
// state.load = false
// } else {
// proxy.$router.push({
// path: '/conferencingRoom',
// query: {
// type: 1,
// userList: JSON.stringify([{ nickName: state.detail.nickName, userId: state.detail.userId }]),
// appid: getAppId(),
// Modinstid: getModuleId(),
// token: getToken(),
// userId: useUserStore().userId,
// },
// })
// }
// } else {
// proxy.$modal.msgError(
// res.data[0].status == 0 ? '当前用户不在线' : '当前用户正在其他协作中'
// )
// state.load = false
// }
// })
// .catch(() => {
// state.load = false
// proxy.$modal.closeLoading()
// })
}
/** 删除文件 */
const clickDeleteFile = (row) => {
// proxy.$modal
// .confirm('是否确认删除 “ ' + row.name + ' ” 文件?')
// .then(async () => {
// await getRemoveSolution({ assistanceId: state.detail.assistanceId, fileId: row.fileId })
// proxy.$modal.msgSuccess('删除成功')
// getTheFileList(state.detail)
// })
// .catch(() => {})
}
/** 修改展示列表 */
const updateTab = (newValue) => {
state.detail = {}
state.tabValue = newValue
}
/** 修改展示区内容 */
const updateDetail = async (details) => {
if (details) {
console.log(details,'details')
state.detail = {}
if (state.tabValue == 1) {
state.isShow = true
} else {
const res = await getInfo(details.uid)
console.log(res,'res---+++')
// state.detail = proxy.deepClone(res.data)
state.detail = res.data
// getInfo(details.uid)
// .then((res) => {
// console.log(res,'人员详细信息')
// // state.detail = proxy.deepClone(res.data)
// })
// .finally(() => { state.isShow = true })
}
} else {
state.isShow = false
}
}
/** 获取文件列表 */
const getTheFileList = (details) => {
// let detail = proxy.deepClone(details)
// getFileList({ assistanceId: detail.assistanceId }).then((resser) => {
// detail.fileList = resser.data
// state.detail = proxy.deepClone(detail)
// })
}
/** 获取通话时长 */
const getTime = () => {
let begin = new Date(state.detail.beginTime).getTime()
let end = new Date(state.detail.endTime).getTime()
if (begin && end) {
let diff = end - begin
const h = Math.floor(diff / (1000 * 60 * 60))
const m = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60))
const s = Math.floor((diff % (1000 * 60)) / 1000)
return h > 0 ? `${h}小时 ${m}分钟 ${s}秒`
: m > 0 ? `${m}分钟 ${s}秒`
: `${s}秒`
} else {
return '暂无'
}
}
/** 加入会议 */
const clickJoin = () => {
// proxy.$modal.loading('加入会议中,请稍候...')
// if (state.cooperation == 'skip') {
// getRtcSetDetail().then((res) => {
// const queryParams = new URLSearchParams({
// type: 2,
// assistanceId: state.socketInformation.data.assistanceId,
// channelName: state.socketInformation.data.channelName,
// nickName: useUserStore().nickName,
// appid: getAppId(),
// Modinstid: getModuleId(),
// token: getToken(),
// userId: useUserStore().userId,
// }).toString()
// let url = import.meta.env.VITE_APP_ENV == 'development'
// ? `/ar/app/conferencingRoom?${queryParams}`
// : `${res.data.webMeetingUrl}?${queryParams}`
// window.open(url, '_blank')
// })
// } else {
// proxy.$router.push({
// path: '/conferencingRoom',
// query: {
// type: 2,
// assistanceId: state.socketInformation.data.assistanceId,
// channelName: state.socketInformation.data.channelName,
// nickName: useUserStore().nickName,
// appid: getAppId(),
// Modinstid: getModuleId(),
// token: getToken(),
// userId: useUserStore().userId,
// },
// })
// }
// state.inviteDialog = false
// proxy.$modal.closeLoading()
}
/** 拒绝加入 */
const clickRefuseJoin = () => {
// refuseAssistance({
// roomId: state.socketInformation.data.channelName,
// assistanceId: state.socketInformation.data.assistanceId,
// inviteUserId: state.socketInformation.data.inviteUserId,
// }).then(() => {
// proxy.$modal.msgSuccess('已拒绝加入该协作')
// state.inviteDialog = false
// })
}
/** 处理 socket 消息 */
const processingSocket = (message) => {
// if (message) {
// state.socketInformation = JSON.parse(message.data)
// if (state.socketInformation.type == 'invite_join') {
// state.inviteDialog = true
// showNotification()
// }
// if (state.socketInformation.type == 'initiator_out') {
// state.inviteDialog = false
// }
// }
}
/** 浏览器通知 */
const showNotification = () => {
// if ('Notification' in window) {
// Notification.requestPermission().then((permission) => {
// if (permission === 'granted') {
// const notification = new Notification('协作邀请', {
// body: state.socketInformation.data.inviteNickName + '邀请您参加远程协作',
// // icon: logo,
// })
// notification.onclick = () => { clickJoin() }
// }
// })
// }
}
// watch(() => WebSocketStore.messages, (message) => {
// if (state.cooperation != 'web') processingSocket(message)
// })
// onMounted(() => {
// state.isLinkKnow = proxy.getCurrentApplicationConfig('cooperation_link_know')
// state.load = false
// })
// onActivated(() => {})
// 暴露给模板
const { detail, weekName, tabValue, isShow, load, loadText, isLinkKnow, socketInformation, inviteDialog, cooperation } = toRefs(state)
</script>
<style lang="scss" scoped>
.flex {
display: flex;
justify-content: center;
align-items: center;
}
.app-container {
padding: 20px;
// margin: 0 17px;
}
.message-null {
@extend .flex;
height: calc(100vh - 90px);
}
.right-content {
height: calc(100vh - 40px);
background: #f4f9ff;
.right-content-title {
@extend .flex;
justify-content: space-between;
width: 100%;
height: 50px;
padding: 0 20px;
background: #167bff;
color: #fff;
font-size: 18px;
}
.right-content-message {
width: calc(100% - 30px);
height: 150px;
margin: 15px;
background: #fff;
border-radius: 10px;
}
.right-content-file {
width: calc(100% - 30px);
margin: 0 15px 15px;
}
.file-top {
@extend .flex;
justify-content: flex-start;
height: 50px;
padding-left: 20px;
background: #e6f1ff;
color: #333;
font-size: 18px;
}
.content-file-video {
.file-video-bottom {
width: 100%;
height: calc(100vh - 350px);
padding: 15px;
background: #fff;
video {
width: 100%;
height: 100%;
}
}
}
.content-file-list {
background: #fff;
padding: 15px 5px 15px 10px;
.file-list {
width: 100%;
padding-right: 10px;
.file-list-content {
@extend .flex;
flex-direction: column;
width: 100%;
.file-list-item {
@extend .flex;
width: 100%;
margin-bottom: 15px;
.file-list-item-icon {
width: 13px;
height: 13px;
margin-right: 10px;
border-radius: 50%;
background: #89b2ff;
}
.file-list-item-text {
@extend .flex;
justify-content: space-between;
width: calc(100% - 23px);
padding: 13px 6px 13px 6px;
border-radius: 4px;
background: #f4f9ff;
.list-item-text {
display: inline-block;
width: calc(100% - 50px);
margin-right: 2px;
}
}
}
}
}
}
}
.message-user {
@extend .flex;
overflow: auto;
.message-user-card {
@extend .flex;
flex-direction: column;
width: 500px;
height: 95%;
max-height: 550px;
background: #fff;
border-radius: 10px;
overflow: hidden;
.user-card-nickName {
@extend .flex;
flex-direction: column;
width: 100%;
height: 40%;
background: linear-gradient(
180deg,
rgba(13, 116, 255, 0.22) 0%,
rgba(30, 173, 255, 0) 100%
);
img {
width: 100px;
height: 100px;
margin-top: 10%;
margin-bottom: 20px;
border-radius: 50%;
background: #167bff;
flex-shrink: 0;
}
span {
color: #051435;
font-size: 24px;
}
}
.user-card-information {
@extend .flex;
flex-direction: column;
justify-content: space-around;
width: 100%;
height: 45%;
.user-information-item {
@extend .flex;
justify-content: space-between;
.user-information-title {
@extend .flex;
justify-content: flex-start;
width: 150px;
img {
width: 24px;
height: 24px;
margin-right: 20px;
}
span {
color: #999;
font-size: 16px;
}
}
.user-information-text {
width: 130px;
color: #333333;
font-size: 16px;
font-weight: 600;
}
}
}
.user-card-btn {
@extend .flex;
width: 100%;
height: 15%;
}
}
}
.video-null {
color: #999;
font-size: 22px;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
::v-deep .user-card-btn .el-button {
width: 70%;
height: 55%;
border-radius: 25px;
font-size: 18px;
color: #fff;
}
</style>

View File

@@ -112,7 +112,8 @@ function requestNotificationPermission() {
onMounted(async () => { onMounted(async () => {
try { try {
loginView.value = true loginView.value = true
await getInfo("self"); const arr = await getInfo("self");
console.log(arr,'身份信息')
showLogin.value = false; showLogin.value = false;
router.push({ router.push({
path: '/whiteboard', path: '/whiteboard',