Compare commits
No commits in common. "main" and "zhangyuanshan-20250310" have entirely different histories.
main
...
zhangyuans
2
auto-imports.d.ts
vendored
@ -70,6 +70,6 @@ declare global {
|
||||
// for type re-export
|
||||
declare global {
|
||||
// @ts-ignore
|
||||
export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
|
||||
export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
|
||||
import('vue')
|
||||
}
|
||||
|
7
components.d.ts
vendored
@ -8,8 +8,6 @@ export {}
|
||||
/* prettier-ignore */
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
AsyncError: typeof import('./src/components/async-error/index.vue')['default']
|
||||
AsyncLoading: typeof import('./src/components/async-loading/index.vue')['default']
|
||||
AudioMessage: typeof import('./src/components/talk/message/AudioMessage.vue')['default']
|
||||
Avatar: typeof import('./src/components/base/Avatar.vue')['default']
|
||||
AvatarCropper: typeof import('./src/components/base/AvatarCropper.vue')['default']
|
||||
@ -38,20 +36,15 @@ declare module 'vue' {
|
||||
RevokeMessage: typeof import('./src/components/talk/message/RevokeMessage.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
SysGroupAdminMessage: typeof import('./src/components/talk/message/system/SysGroupAdminMessage.vue')['default']
|
||||
SysGroupCancelMutedMessage: typeof import('./src/components/talk/message/system/SysGroupCancelMutedMessage.vue')['default']
|
||||
SysGroupCreateMessage: typeof import('./src/components/talk/message/system/SysGroupCreateMessage.vue')['default']
|
||||
SysGroupDismissed: typeof import('./src/components/talk/message/system/SysGroupDismissed.vue')['default']
|
||||
SysGroupInfoChangeMessage: typeof import('./src/components/talk/message/system/SysGroupInfoChangeMessage.vue')['default']
|
||||
SysGroupJoinMessage: typeof import('./src/components/talk/message/system/SysGroupJoinMessage.vue')['default']
|
||||
SysGroupMemberCancelMutedMessage: typeof import('./src/components/talk/message/system/SysGroupMemberCancelMutedMessage.vue')['default']
|
||||
SysGroupMemberKickedMessage: typeof import('./src/components/talk/message/system/SysGroupMemberKickedMessage.vue')['default']
|
||||
SysGroupMemberMutedMessage: typeof import('./src/components/talk/message/system/SysGroupMemberMutedMessage.vue')['default']
|
||||
SysGroupMemberQuitMessage: typeof import('./src/components/talk/message/system/SysGroupMemberQuitMessage.vue')['default']
|
||||
SysGroupMemberRemovedMessage: typeof import('./src/components/talk/message/system/SysGroupMemberRemovedMessage.vue')['default']
|
||||
SysGroupMutedMessage: typeof import('./src/components/talk/message/system/SysGroupMutedMessage.vue')['default']
|
||||
SysGroupTransferMessage: typeof import('./src/components/talk/message/system/SysGroupTransferMessage.vue')['default']
|
||||
SysPushMessage: typeof import('./src/components/talk/message/system/sysPushMessage.vue')['default']
|
||||
SysTextMessage: typeof import('./src/components/talk/message/system/SysTextMessage.vue')['default']
|
||||
TabbarItem: typeof import('./src/components/x-tabbar/components/tabbar-item/index.vue')['default']
|
||||
TextMessage: typeof import('./src/components/talk/message/TextMessage.vue')['default']
|
||||
|
8
env/.env.dev
vendored
@ -5,10 +5,4 @@ VITE_SHOW_CONSOLE = true
|
||||
# 是否开启sourcemap
|
||||
VITE_SHOW_SOURCEMAP = true
|
||||
# baseUrl
|
||||
# VITE_BASEURL = 'http://172.16.100.93:8503'
|
||||
VITE_BASEURL = 'http://192.168.88.47:9503'
|
||||
#VITE_SOCKET_API
|
||||
# VITE_SOCKET_API = 'ws://172.16.100.93:8504'
|
||||
VITE_SOCKET_API = 'ws://192.168.88.47:9504'
|
||||
# EPRAPI baseUrl
|
||||
VITE_EPR_BASEURL = 'http://114.218.158.24:9020'
|
||||
VITE_BASEURL = 'http://warehouse.szjixun.cn/oa_backend'
|
||||
|
19
env/.env.prod
vendored
@ -1,21 +1,8 @@
|
||||
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
|
||||
NODE_ENV = 'prod'
|
||||
# 是否显示console
|
||||
VITE_SHOW_CONSOLE = false
|
||||
VITE_SHOW_CONSOLE = true
|
||||
# 是否开启sourcemap
|
||||
VITE_SHOW_SOURCEMAP = false
|
||||
|
||||
VITE_SHOW_SOURCEMAP = true
|
||||
# baseUrl
|
||||
VITE_BASEURL = 'https://chat-out.szjixun.cn' #体制外
|
||||
#VITE_SOCKET_API
|
||||
VITE_SOCKET_API = 'wss://chat-out.szjixun.cn' #体制外
|
||||
# EPRAPI baseUrl
|
||||
VITE_EPR_BASEURL = 'https://erpapi-out.szjixun.cn' #体制外
|
||||
|
||||
# # baseUrl
|
||||
# VITE_BASEURL = 'https://chat.szjixun.cn' #体制内
|
||||
# #VITE_SOCKET_API
|
||||
# VITE_SOCKET_API = 'wss://chat.szjixun.cn' #体制内
|
||||
# # EPRAPI baseUrl
|
||||
# VITE_EPR_BASEURL = 'https://erpapi.fontree.cn' #体制内
|
||||
|
||||
VITE_BASEURL = 'https://oa-a.szjixun.cn/api'
|
||||
|
12
env/.env.test
vendored
@ -4,16 +4,10 @@ NODE_ENV = 'test'
|
||||
VITE_SHOW_CONSOLE = true
|
||||
# 是否开启sourcemap
|
||||
VITE_SHOW_SOURCEMAP = true
|
||||
|
||||
# # baseUrl
|
||||
VITE_BASEURL = 'http://172.16.100.93:8503'
|
||||
# #VITE_SOCKET_API
|
||||
VITE_SOCKET_API = 'ws://172.16.100.93:8504'
|
||||
|
||||
# baseUrl
|
||||
# VITE_BASEURL = 'http://192.168.88.21:9503'
|
||||
# VITE_BASEURL = 'https://warehouse.szjixun.cn/oa_backend'
|
||||
VITE_BASEURL = 'http://172.16.100.93:8503'
|
||||
#VITE_SOCKET_API
|
||||
# VITE_SOCKET_API = 'ws://192.168.88.21:9504'
|
||||
|
||||
VITE_SOCKET_API = 'ws://172.16.100.93:8504'
|
||||
# EPRAPI baseUrl
|
||||
VITE_EPR_BASEURL = 'http://114.218.158.24:9020'
|
||||
|
12
package.json
@ -2,16 +2,14 @@
|
||||
"name": "unihelper",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"packageManager": "pnpm@8.14.1",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"dev:h5": "uni --mode dev --port 2468",
|
||||
"test:h5": "uni --mode test --port 2468",
|
||||
"prod:h5": "uni --mode prod",
|
||||
"build:h5:test": "uni build --mode test",
|
||||
"build:h5:prod": "uni build --mode prod",
|
||||
"preview:h5": "uni preview --mode test",
|
||||
"preview:h5:prod": "uni preview --mode prod"
|
||||
"build:h5:prod": "uni build --mode prod"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dcloudio/uni-app": "3.0.0-alpha-4000020240111001",
|
||||
@ -40,9 +38,7 @@
|
||||
"lodash": "^4.17.21",
|
||||
"nzh": "^1.0.13",
|
||||
"pinia-plugin-persistedstate": "^4.1.3",
|
||||
"quill": "^1.3.7",
|
||||
"quill-mention": "^4.1.0",
|
||||
"recorder-core": "^1.3.25011100",
|
||||
"quill-mention": "^6.0.2",
|
||||
"vconsole": "^3.15.1",
|
||||
"vue": "^3.3.8",
|
||||
"vue-i18n": "11.0.0-rc.1"
|
||||
@ -66,7 +62,7 @@
|
||||
"lint-staged": "^15.2.0",
|
||||
"naive-ui": "^2.41.0",
|
||||
"pinia": "2.0.36",
|
||||
"sass": "1.62.1",
|
||||
"sass": "^1.77.8",
|
||||
"simple-git-hooks": "^2.9.0",
|
||||
"typescript": "^5.3.3",
|
||||
"unocss": "^0.58.9",
|
||||
|
5462
pnpm-lock.yaml
23
src/App.vue
@ -1,33 +1,28 @@
|
||||
<script setup>
|
||||
import { useStatus } from '@/store/status'
|
||||
import { useUserStore, useDialogueListStore } from '@/store'
|
||||
import { useUserStore } from '@/store'
|
||||
import { useProvideUserModal } from '@/hooks'
|
||||
import {useAuth} from "@/store/auth";
|
||||
const {token} = useAuth()
|
||||
import ws from '@/connect'
|
||||
import {uniStorage} from "@/utils/uniStorage.js"
|
||||
const { statusBarHeight } = useStatus()
|
||||
const { uid, isShow } = useProvideUserModal()
|
||||
const userStore = useUserStore()
|
||||
const root = document.documentElement
|
||||
root.style.setProperty('--statusBarHeight', `${statusBarHeight.value}px`)
|
||||
const handleWebview = () => {
|
||||
let statusBarHeight_ = window?.plus?.navigator?.getStatusbarHeight()
|
||||
let statusBarHeight = window?.plus?.navigator?.getStatusbarHeight()
|
||||
const webview = plus.webview.currentWebview()
|
||||
// webview.setStyle({
|
||||
// top: statusBarHeight_,
|
||||
// bottom: 0,
|
||||
// })
|
||||
console.log("webview", webview)
|
||||
webview.setStyle({
|
||||
top: statusBarHeight,
|
||||
bottom: 0,
|
||||
})
|
||||
// console.log(webview)
|
||||
token.value = webview.token
|
||||
if(webview?.doClearDialogueList){
|
||||
useDialogueListStore().dialogueList.value = []
|
||||
uniStorage.removeItem('dialogueList')
|
||||
}
|
||||
userStore.loadSetting()
|
||||
ws.connect()
|
||||
}
|
||||
const init = () => {
|
||||
userStore.loadSetting()
|
||||
ws.connect()
|
||||
if (typeof plus !== 'undefined') {
|
||||
handleWebview()
|
||||
} else {
|
||||
|
@ -1,47 +0,0 @@
|
||||
import request from '@/service/index.js'
|
||||
|
||||
// 查询用户是否需要添加好友
|
||||
export const ServeCheckFriend = (data) => {
|
||||
return request({
|
||||
url: '/api/v1/contact/friend/check',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
// 主动添加好友(单向好友)
|
||||
export const ServeAddFriend = (data) => {
|
||||
return request({
|
||||
url: '/api/v1/contact/friend/add',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
// 查询我的好友列表
|
||||
export const ServeQueryFriendsList = (data) => {
|
||||
return request({
|
||||
url: '/api/v1/contact/friend/list',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
// 删除好友(单向好友)
|
||||
export const ServeDeleteFriend = (data) => {
|
||||
return request({
|
||||
url: '/api/v1/contact/friend/delete',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
//添加我的好友时候的搜索接口
|
||||
export const ServeFriendSearch = (data) => {
|
||||
return request({
|
||||
url: '/api/v1/contact/friend/search',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
@ -1,7 +1,5 @@
|
||||
import request from '@/service/index.js'
|
||||
import qs from 'qs'
|
||||
import { useTalkStore, useDialogueStore } from '@/store'
|
||||
import { handleFindWebview } from '@/utils/common'
|
||||
|
||||
// 获取聊天列表服务接口
|
||||
export const ServeGetTalkList = (data) => {
|
||||
@ -40,15 +38,7 @@ export const ServeTopTalkList = (data) => {
|
||||
}
|
||||
|
||||
// 清除聊天消息未读数服务接口
|
||||
export const ServeClearTalkUnreadNum = (data, unReadNum) => {
|
||||
console.log('=======chatApp==UnreadNum', unReadNum)
|
||||
if (
|
||||
!useTalkStore().items[
|
||||
useTalkStore().findTalkIndex(useDialogueStore().index_name)
|
||||
]?.is_disturb
|
||||
) {
|
||||
handleFindWebview(`updateUnreadMsgNumReduce('${unReadNum}')`)
|
||||
}
|
||||
export const ServeClearTalkUnreadNum = (data) => {
|
||||
return request({
|
||||
url: '/api/v1/talk/unread/clear',
|
||||
method: 'POST',
|
||||
@ -68,7 +58,6 @@ export const ServeTalkRecords = (data) => {
|
||||
// 获取转发会话记录详情列表服务接口
|
||||
export const ServeGetForwardRecords = (data) => {
|
||||
return request({
|
||||
// url: '/api/v1/talk/records/forward/v2',
|
||||
url: '/api/v1/talk/records/forward',
|
||||
method: 'GET',
|
||||
data,
|
||||
@ -180,77 +169,13 @@ export const ServeConfirmVoteHandle = (data) => {
|
||||
})
|
||||
}
|
||||
|
||||
export const uploadImg = (data, onProgressFn) => {
|
||||
export const uploadImg = (data,onProgressFn) => {
|
||||
return request({
|
||||
url: '/upload/img',
|
||||
method: 'POST',
|
||||
data: data,
|
||||
baseURL: import.meta.env.VITE_EPR_BASEURL,
|
||||
isFormData: true,
|
||||
onUploadProgress: (progressEvent) =>
|
||||
onProgressFn(progressEvent, data.get('file')),
|
||||
data:data,
|
||||
baseURL:import.meta.env.VITE_EPR_BASEURL,
|
||||
isFormData:true,
|
||||
onUploadProgress:(progressEvent)=>onProgressFn(progressEvent,data.get('file'))
|
||||
})
|
||||
}
|
||||
// 根据msg_id获取消息
|
||||
export const detailGetRecordsContext = (data) => {
|
||||
return request({
|
||||
url: '/api/v1/talk/record/detail',
|
||||
method: 'GET',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
// 获取自己消息已读回执列表
|
||||
export const ServeReadConditionList = (data) => {
|
||||
return request({
|
||||
url: '/api/v1/talk/my-records/read/condition',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
// 获取消息已读未读详情
|
||||
export const ServeMessageReadDetail = (data) => {
|
||||
return request({
|
||||
url: '/api/v1/talk/my-records/read/condition',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
// 语音转文字
|
||||
export const ServeConvertText = (data) => {
|
||||
return request({
|
||||
url: '/api/v1/talk/message/voice-to-text',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
// 获取用户所在群聊列表(我的群聊)
|
||||
export const ServeUserGroupChatList = (data) => {
|
||||
return request({
|
||||
url: '/api/v1/group/user/list',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
//查询聊天助手是否开启
|
||||
export const ServeContactRobotQuery = (data) => {
|
||||
return request({
|
||||
url: '/api/v1/contact/robot/query',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
//开关聊天助手
|
||||
export const ServeContactRobotUpdate = (data) => {
|
||||
return request({
|
||||
url: '/api/v1/contact/robot/update',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -26,22 +26,6 @@ export const departmentV2TreeAll = (data) => {
|
||||
data,
|
||||
})
|
||||
}
|
||||
// 通讯录过滤测试部门
|
||||
export const departmentV2TreeAll2 = (data) => {
|
||||
return request({
|
||||
url: '/api/v1/contact/department/v2/tree/all',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
// 查询是否有权限
|
||||
export const userHasPermission = (data) => {
|
||||
return request({
|
||||
url: '/api/v1/contact/check/erp/rule',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
//获取指定部门下的所有岗位
|
||||
export const v2TreePositionByDepartment = (data) => {
|
||||
@ -61,14 +45,6 @@ export const userV2List = (data) => {
|
||||
data,
|
||||
})
|
||||
}
|
||||
// 过滤测试数据的erp用户接口
|
||||
export const userV2List2 = (data) => {
|
||||
return request({
|
||||
url: '/api/v1/group/erp/users',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
export const groupCreateDept = (data) => {
|
||||
return request({
|
||||
|
@ -4,7 +4,6 @@ import qs from 'qs'
|
||||
// ES搜索聊天记录-主页搜索什么都有
|
||||
export const ServeSeachQueryAll = (data) => {
|
||||
return request({
|
||||
// url: '/api/v1/elasticsearch/query-all/v2',
|
||||
url: '/api/v1/elasticsearch/query-all',
|
||||
method: 'POST',
|
||||
data,
|
||||
@ -14,7 +13,6 @@ export const ServeSeachQueryAll = (data) => {
|
||||
// ES搜索用户数据
|
||||
export const ServeQueryUser = (data) => {
|
||||
return request({
|
||||
// url: '/api/v1/elasticsearch/query-user/v2',
|
||||
url: '/api/v1/elasticsearch/query-user',
|
||||
method: 'POST',
|
||||
data,
|
||||
@ -47,12 +45,3 @@ export const ServeTalkDate = (data) => {
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
//获取会话Id
|
||||
export const ServeGetSessionId = (data) => {
|
||||
return request({
|
||||
url: '/api/v1/talk/session/getId',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
@ -10,9 +10,10 @@ export const ServeGetUserSetting = (data) => {
|
||||
|
||||
export const userInfoApi = (data) => {
|
||||
return request({
|
||||
url: '/api/v1/users/erp/info',
|
||||
url: '/user/info',
|
||||
method: 'POST',
|
||||
data,
|
||||
baseURL:import.meta.env.VITE_EPR_BASEURL,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1,89 +0,0 @@
|
||||
<template>
|
||||
<div class="chat-app-error-page">
|
||||
<div class="error-container">
|
||||
<div class="error-icon">
|
||||
<i class="iconfont icon-wifi"></i>
|
||||
</div>
|
||||
<div class="error-message">
|
||||
<span>您的网络好像波动了一下~</span>
|
||||
</div>
|
||||
<div class="reload-button" @click="handleReload">
|
||||
<span>重新加载</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { onMounted, ref, computed, nextTick } from 'vue'
|
||||
|
||||
const handleReload = () => {
|
||||
location.reload(true);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (typeof plus !== 'undefined') {
|
||||
|
||||
} else {
|
||||
document.addEventListener('plusready', () => {
|
||||
|
||||
})
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.chat-app-error-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
background-image: url('@/static/image/clockIn/z3280@3x.png');
|
||||
background-size: cover;
|
||||
background-position: bottom center;
|
||||
|
||||
.error-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 32px;
|
||||
background: #ffffff;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
||||
max-width: 80%;
|
||||
width: 320px;
|
||||
|
||||
.error-icon {
|
||||
font-size: 48px;
|
||||
color: #ff6b6b;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
font-size: 16px;
|
||||
color: #333333;
|
||||
margin-bottom: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.reload-button {
|
||||
padding: 12px 32px;
|
||||
background: $theme-primary;
|
||||
color: #ffffff;
|
||||
border-radius: 24px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.9;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,81 +0,0 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="loader">
|
||||
<p class="heading">加载中</p>
|
||||
<div class="loading">
|
||||
<div class="load"></div>
|
||||
<div class="load"></div>
|
||||
<div class="load"></div>
|
||||
<div class="load"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.loader {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
background-image: url('@/static/image/clockIn/z3280@3x.png');
|
||||
background-size: cover;
|
||||
background-position: bottom center;
|
||||
}
|
||||
|
||||
.heading {
|
||||
color: black;
|
||||
letter-spacing: 0.2em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.loading {
|
||||
display: flex;
|
||||
width: 5em;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.load {
|
||||
width: 23px;
|
||||
height: 3px;
|
||||
background-color: limegreen;
|
||||
animation: 1s move_5011 infinite;
|
||||
border-radius: 5px;
|
||||
margin: 0.1em;
|
||||
}
|
||||
|
||||
.load:nth-child(1) {
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
.load:nth-child(2) {
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
|
||||
.load:nth-child(3) {
|
||||
animation-delay: 0.6s;
|
||||
}
|
||||
|
||||
@keyframes move_5011 {
|
||||
0% {
|
||||
width: 0.2em;
|
||||
}
|
||||
|
||||
25% {
|
||||
width: 0.7em;
|
||||
}
|
||||
|
||||
50% {
|
||||
width: 1.5em;
|
||||
}
|
||||
|
||||
100% {
|
||||
width: 0.2em;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="avatar-module" :style="[customStyle, { background: avatar ? '#fff' : '' }]">
|
||||
<div class="avatar-module" :style="customStyle">
|
||||
<img :src="avatar" v-if="avatar" />
|
||||
<span v-else :style="customTextStyle">{{ text_avatar }}</span>
|
||||
</div>
|
||||
@ -89,7 +89,6 @@ const text_avatar = computed(() => {
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -90,6 +90,8 @@ const onSubmit = () => {
|
||||
ServeUploadAvatar(form).then((res) => {
|
||||
if (res.code == 200) {
|
||||
emit('success', res.data.avatar)
|
||||
} else {
|
||||
message.warning(res.message)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@ -6,11 +6,7 @@
|
||||
props.subBtnText ? 'apposition-btn-style' : '',
|
||||
]"
|
||||
>
|
||||
<wd-button
|
||||
custom-class="custom-sub-btn-class"
|
||||
v-if="props.subBtnText"
|
||||
@click="clickSubBtn"
|
||||
>
|
||||
<wd-button custom-class="custom-sub-btn-class" v-if="props.subBtnText">
|
||||
{{ props.subBtnText }}
|
||||
</wd-button>
|
||||
<wd-button
|
||||
@ -19,7 +15,6 @@
|
||||
:disabled="props?.disabled"
|
||||
:class="[props?.disabled ? 'custom-btn-class-disabled' : '']"
|
||||
:plain="props?.plain"
|
||||
:loading="props?.isLoading"
|
||||
>
|
||||
{{ props.btnText }}
|
||||
</wd-button>
|
||||
@ -29,22 +24,16 @@
|
||||
import { reactive } from 'vue'
|
||||
import { defineProps, defineEmits } from 'vue'
|
||||
const state = reactive({})
|
||||
const emits = defineEmits(['clickSubBtn', 'clickBtn'])
|
||||
const emits = defineEmits(['clickBtn'])
|
||||
const props = defineProps({
|
||||
isBottom: false, //是否底部按钮
|
||||
btnText: '', //按钮文字
|
||||
subBtnText: '', //次要按钮文字
|
||||
disabled: false, //是否禁用
|
||||
plain: false, //是否镂空
|
||||
isLoading: false, //是否正在加载中
|
||||
})
|
||||
|
||||
//点击副按钮
|
||||
const clickSubBtn = () => {
|
||||
emits('clickSubBtn')
|
||||
}
|
||||
|
||||
//点击主按钮
|
||||
//点击
|
||||
const clickBtn = () => {
|
||||
emits('clickBtn')
|
||||
}
|
||||
|
@ -21,20 +21,9 @@
|
||||
<script setup>
|
||||
import { defineProps, defineEmits, reactive, watch } from 'vue'
|
||||
const props = defineProps({
|
||||
searchText: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
first_talk_record_infos: {
|
||||
type: Object,
|
||||
default(){
|
||||
return {}
|
||||
}
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
searchText: String,
|
||||
first_talk_record_infos: Object,
|
||||
disabled: Boolean,
|
||||
})
|
||||
const state = reactive({
|
||||
searchText: '', //搜索内容
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<tm-navbar
|
||||
:hideBack="props.hideBack"
|
||||
:hideHome="props.hideHome"
|
||||
hideHome
|
||||
:title="props.title"
|
||||
:shadow="props.shadowNum"
|
||||
:fontSize="34"
|
||||
@ -38,10 +38,6 @@ const props = defineProps({
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
hideHome: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
|
@ -25,18 +25,9 @@
|
||||
class="flex flex-col items-center justify-center"
|
||||
>
|
||||
<tm-image :width="40" :height="40" :src="copy07"></tm-image>
|
||||
<div class="mt-1">复制</div>
|
||||
<div>复制</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="props.isShowConvertText"
|
||||
@click="() => itemClick('convertText')"
|
||||
class="flex flex-col items-center justify-center"
|
||||
>
|
||||
<tm-image :width="40" :height="40" :src="copy07"></tm-image>
|
||||
<div class="mt-1">转文字</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="props.isShowMultipleChoose && !props.isChatRobot"
|
||||
@click="() => itemClick('multipleChoose')"
|
||||
class="flex flex-col items-center justify-center"
|
||||
>
|
||||
@ -45,15 +36,15 @@
|
||||
:height="40"
|
||||
:src="multipleChoices"
|
||||
></tm-image>
|
||||
<div class="mt-1">多选</div>
|
||||
<div>多选</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="props.isShowCite && !props.isChatRobot"
|
||||
v-if="props.isShowCite"
|
||||
@click="() => itemClick('actionCite')"
|
||||
class="flex flex-col items-center justify-center"
|
||||
>
|
||||
<tm-image :width="40" :height="40" :src="cite"></tm-image>
|
||||
<div class="mt-1">引用</div>
|
||||
<div>引用</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="props.isShowWithdraw"
|
||||
@ -61,15 +52,14 @@
|
||||
class="flex flex-col items-center justify-center"
|
||||
>
|
||||
<tm-image :width="40" :height="40" :src="withdraw"></tm-image>
|
||||
<div class="mt-1">撤回</div>
|
||||
<div>撤回</div>
|
||||
</div>
|
||||
<div
|
||||
:class="{ 'w-full': props.isChatRobot }"
|
||||
@click="() => itemClick('actionDelete')"
|
||||
class="flex flex-col items-center justify-center"
|
||||
>
|
||||
<tm-image :width="40" :height="40" :src="delete07"></tm-image>
|
||||
<div class="mt-1">删除</div>
|
||||
<div>删除</div>
|
||||
</div>
|
||||
</div>
|
||||
<div :style="data.iconStyle" class="icon"></div>
|
||||
@ -88,8 +78,8 @@
|
||||
// 组件
|
||||
|
||||
// uniapp & vue
|
||||
import { onLoad, onReady } from '@dcloudio/uni-app'
|
||||
import { defineEmits, defineProps } from 'vue'
|
||||
import { onLoad, onReady } from "@dcloudio/uni-app";
|
||||
import { defineEmits, defineProps } from "vue";
|
||||
import {
|
||||
reactive,
|
||||
ref,
|
||||
@ -98,152 +88,134 @@ import {
|
||||
nextTick,
|
||||
getCurrentInstance,
|
||||
onMounted,
|
||||
onBeforeUnmount,
|
||||
} from 'vue'
|
||||
import copy07 from '@/static/image/chatList/copy07@2x.png'
|
||||
import multipleChoices from '@/static/image/chatList/multipleChoices@2x.png'
|
||||
import cite from '@/static/image/chatList/cite@2x.png'
|
||||
import withdraw from '@/static/image/chatList/withdraw@2x.png'
|
||||
import delete07 from '@/static/image/chatList/delete@2x.png'
|
||||
onBeforeUnmount
|
||||
} from "vue";
|
||||
import copy07 from "@/static/image/chatList/copy07@2x.png";
|
||||
import multipleChoices from "@/static/image/chatList/multipleChoices@2x.png";
|
||||
import cite from "@/static/image/chatList/cite@2x.png";
|
||||
import withdraw from "@/static/image/chatList/withdraw@2x.png";
|
||||
import delete07 from "@/static/image/chatList/delete@2x.png";
|
||||
|
||||
// pinia
|
||||
const systemInfo = uni.getSystemInfoSync()
|
||||
const bubbleRef = ref(null)
|
||||
const systemInfo = uni.getSystemInfoSync();
|
||||
const bubbleRef = ref(null);
|
||||
|
||||
const props = defineProps({
|
||||
isShowCopy: {
|
||||
//是否显示复制
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
isShowCite: {
|
||||
//是否显示引用
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
isShowWithdraw: {
|
||||
//是否显示撤回
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
isShowConvertText: {
|
||||
//是否显示转文字
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
isShowMultipleChoose: {
|
||||
//是否显示多选
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
isChatRobot: {
|
||||
//是否是智能助手
|
||||
type: Boolean,
|
||||
default: false,
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const emits = defineEmits(['clickMenu'])
|
||||
const emits = defineEmits(["clickMenu"]);
|
||||
|
||||
/**
|
||||
* @name 生成UUID
|
||||
*/
|
||||
const uuid = () => {
|
||||
const reg = /[xy]/g
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
|
||||
const reg = /[xy]/g;
|
||||
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"
|
||||
.replace(reg, function (c) {
|
||||
var r = (Math.random() * 16) | 0,
|
||||
v = c == 'x' ? r : (r & 0x3) | 0x8
|
||||
return v.toString(16)
|
||||
v = c == "x" ? r : (r & 0x3) | 0x8;
|
||||
return v.toString(16);
|
||||
})
|
||||
.replace(/-/g, '')
|
||||
}
|
||||
.replace(/-/g, "");
|
||||
};
|
||||
|
||||
const popoverBoxId = `ID${uuid()}`
|
||||
const popoverContentId = `ID${uuid()}`
|
||||
const instance = getCurrentInstance()
|
||||
const popoverBoxId = `ID${uuid()}`;
|
||||
const popoverContentId = `ID${uuid()}`;
|
||||
const instance = getCurrentInstance();
|
||||
const data = reactive({
|
||||
popoverShow: false,
|
||||
defaultStyle: {},
|
||||
showStyle: {
|
||||
left: 0,
|
||||
right: '',
|
||||
transform: '',
|
||||
right: "",
|
||||
transform: "",
|
||||
},
|
||||
iconStyle: {
|
||||
left: '',
|
||||
right: '',
|
||||
transform: '',
|
||||
left: "",
|
||||
right: "",
|
||||
transform: "",
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
/**
|
||||
* @name 获取DOM
|
||||
*/
|
||||
const getDom = (dom) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const query = uni.createSelectorQuery().in(instance)
|
||||
let select = query.select(dom)
|
||||
const query = uni.createSelectorQuery().in(instance);
|
||||
let select = query.select(dom);
|
||||
const boundingClientRect = select.boundingClientRect((data) => {
|
||||
resolve(data)
|
||||
})
|
||||
boundingClientRect.exec()
|
||||
})
|
||||
}
|
||||
resolve(data);
|
||||
});
|
||||
boundingClientRect.exec();
|
||||
});
|
||||
};
|
||||
|
||||
const itemClick = (item) => {
|
||||
emits('clickMenu', item)
|
||||
}
|
||||
emits("clickMenu", item);
|
||||
};
|
||||
|
||||
// 弹起 长按5
|
||||
let pressDownTime = 0
|
||||
let time = null
|
||||
let pressDownTime = 0;
|
||||
let time = null;
|
||||
const onTouchstart = () => {
|
||||
time && clearTimeout(time)
|
||||
time = setTimeout(open, 500)
|
||||
}
|
||||
time && clearTimeout(time);
|
||||
time = setTimeout(open, 500);
|
||||
};
|
||||
|
||||
const onTouchend = () => {
|
||||
time && clearTimeout(time)
|
||||
}
|
||||
time && clearTimeout(time);
|
||||
};
|
||||
|
||||
const open = async () => {
|
||||
let popoverContent = await getDom(`#${popoverContentId}`)
|
||||
let popoverBox = await getDom(`#${popoverBoxId}`)
|
||||
let popoverContent = await getDom(`#${popoverContentId}`);
|
||||
let popoverBox = await getDom(`#${popoverBoxId}`);
|
||||
|
||||
// 缩放中心点
|
||||
let originX = popoverBox.width / 2
|
||||
let originX = popoverBox.width / 2;
|
||||
// 根据距离 初始化位置 判断是顶部还是底部
|
||||
let isTop = popoverBox.top - 50 > popoverContent.height
|
||||
let isTop = popoverBox.top - 50 > popoverContent.height;
|
||||
// 上面还是下面
|
||||
data.defaultStyle = {
|
||||
top: isTop ? '60rpx' : 'auto',
|
||||
bottom: !isTop ? '-20rpx' : 'auto',
|
||||
transform: `translateY(${isTop ? '-100%' : '100%'}) scale(.8)`,
|
||||
}
|
||||
top: isTop ? "60rpx" : "auto",
|
||||
bottom: !isTop ? "-20rpx" : "auto",
|
||||
transform: `translateY(${isTop ? "-100%" : "100%"}) scale(.8)`,
|
||||
};
|
||||
// 左边还是右边
|
||||
if (popoverBox.left > systemInfo.windowWidth - popoverBox.right) {
|
||||
data.defaultStyle.right = 0
|
||||
data.defaultStyle.right = 0;
|
||||
// 动画缩放中心点
|
||||
data.defaultStyle['transform-origin'] = `${
|
||||
data.defaultStyle["transform-origin"] = `${
|
||||
popoverContent.width - originX
|
||||
}px ${isTop ? '100%' : '0%'}`
|
||||
}px ${isTop ? "100%" : "0%"}`;
|
||||
} else {
|
||||
data.defaultStyle.left = 0
|
||||
data.defaultStyle.left = 0;
|
||||
// 动画缩放中心点
|
||||
data.defaultStyle['transform-origin'] = `${originX}px ${
|
||||
isTop ? '100%' : '0%'
|
||||
}`
|
||||
data.defaultStyle["transform-origin"] = `${originX}px ${
|
||||
isTop ? "100%" : "0%"
|
||||
}`;
|
||||
}
|
||||
data.showStyle = { ...data.defaultStyle }
|
||||
data.showStyle = { ...data.defaultStyle };
|
||||
// icon位置样式
|
||||
let iconDefsultStyle = {
|
||||
transform: `translate(0%, ${isTop ? '20%' : '-20%'})`,
|
||||
'border-top-color': isTop ? '#333333' : '',
|
||||
'border-bottom-color': !isTop ? '#333333' : '',
|
||||
top: !isTop ? '-20rpx' : 'auto',
|
||||
bottom: isTop ? '-20rpx' : 'auto',
|
||||
}
|
||||
transform: `translate(0%, ${isTop ? "20%" : "-20%"})`,
|
||||
"border-top-color": isTop ? "#333333" : "",
|
||||
"border-bottom-color": !isTop ? "#333333" : "",
|
||||
top: !isTop ? "-20rpx" : "auto",
|
||||
bottom: isTop ? "-20rpx" : "auto",
|
||||
};
|
||||
|
||||
setTimeout(() => {
|
||||
if (popoverBox.left > systemInfo.windowWidth - popoverBox.right) {
|
||||
@ -252,55 +224,55 @@ const open = async () => {
|
||||
...data.defaultStyle,
|
||||
// 显示
|
||||
opacity: 1,
|
||||
transform: `translateY(${isTop ? '-100%' : '100%'}) scale(1)`,
|
||||
'pointer-events': 'auto',
|
||||
}
|
||||
transform: `translateY(${isTop ? "-100%" : "100%"}) scale(1)`,
|
||||
"pointer-events": "auto",
|
||||
};
|
||||
data.iconStyle = {
|
||||
right: `${originX}px`,
|
||||
left: 'auto',
|
||||
left: "auto",
|
||||
...iconDefsultStyle,
|
||||
}
|
||||
};
|
||||
} else {
|
||||
data.showStyle = {
|
||||
// 位置
|
||||
...data.defaultStyle,
|
||||
// 显示
|
||||
opacity: 1,
|
||||
transform: `translateY(${isTop ? '-100%' : '100%'}) scale(1)`,
|
||||
'pointer-events': 'auto',
|
||||
}
|
||||
transform: `translateY(${isTop ? "-100%" : "100%"}) scale(1)`,
|
||||
"pointer-events": "auto",
|
||||
};
|
||||
data.iconStyle = {
|
||||
left: `${originX}px`,
|
||||
right: 'auto',
|
||||
right: "auto",
|
||||
...iconDefsultStyle,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (!data.popoverShow) data.popoverShow = true
|
||||
}, 200)
|
||||
}
|
||||
if (!data.popoverShow) data.popoverShow = true;
|
||||
}, 200);
|
||||
};
|
||||
|
||||
const close = (time) => {
|
||||
setTimeout(() => {
|
||||
data.popoverShow = false
|
||||
data.showStyle = data.defaultStyle
|
||||
}, time || 0)
|
||||
}
|
||||
data.popoverShow = false;
|
||||
data.showStyle = data.defaultStyle;
|
||||
}, time || 0);
|
||||
};
|
||||
const handleClickOutside = (event) => {
|
||||
if ((data.popoverShow = false)) {
|
||||
if (data.popoverShow = false) {
|
||||
return false
|
||||
}
|
||||
if (bubbleRef.value && !bubbleRef.value.contains(event.target)) {
|
||||
close()
|
||||
close();
|
||||
}
|
||||
}
|
||||
};
|
||||
onMounted(() => {
|
||||
document.addEventListener('click', handleClickOutside)
|
||||
})
|
||||
document.addEventListener('click', handleClickOutside);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener('click', handleClickOutside)
|
||||
})
|
||||
document.removeEventListener('click', handleClickOutside);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -1,155 +1,109 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, onMounted, onUnmounted, computed } from 'vue'
|
||||
import { PlayOne, PauseOne } from '@icon-park/vue-next'
|
||||
import { ITalkRecordExtraAudio, ITalkRecord } from '@/types/chat'
|
||||
import { onHide } from '@dcloudio/uni-app'
|
||||
import aTrumpet from '@/uni_modules/a-trumpet/components/a-trumpet/a-trumpet.vue'
|
||||
import { useUserStore } from '@/store'
|
||||
import { ref, reactive, onMounted } from "vue";
|
||||
import { PlayOne, PauseOne } from "@icon-park/vue-next";
|
||||
import { ITalkRecordExtraAudio, ITalkRecord } from "@/types/chat";
|
||||
|
||||
const props = defineProps<{
|
||||
extra: ITalkRecordExtraAudio
|
||||
data: ITalkRecord
|
||||
maxWidth?: Boolean
|
||||
}>()
|
||||
extra: ITalkRecordExtraAudio;
|
||||
data: ITalkRecord;
|
||||
maxWidth?: Boolean;
|
||||
}>();
|
||||
|
||||
const userStore = useUserStore()
|
||||
const talkParams = reactive({
|
||||
uid: computed(() => userStore.uid),
|
||||
})
|
||||
const audioRef = ref();
|
||||
const audioContext = ref<any>(null);
|
||||
|
||||
const durationDesc = ref("-");
|
||||
|
||||
const audioContext = ref<any>(null)
|
||||
const durationDesc = ref('-')
|
||||
const state = reactive({
|
||||
isAudioPlay: false,
|
||||
progress: 0,
|
||||
duration: 0,
|
||||
currentTime: 0,
|
||||
loading: true,
|
||||
})
|
||||
|
||||
// 监听其它音频播放事件
|
||||
function onOtherAudioPlay(e) {
|
||||
if (e.detail !== props.data.msg_id && state.isAudioPlay) {
|
||||
audioContext.value.pause()
|
||||
state.isAudioPlay = false
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
audioContext.value = uni.createInnerAudioContext()
|
||||
audioContext.value.src = props.extra.url
|
||||
// 使用uni-app的方式创建内部音频上下文
|
||||
audioContext.value = uni.createInnerAudioContext();
|
||||
audioContext.value.src = props.extra.url;
|
||||
|
||||
audioContext.value.onCanplay(() => {
|
||||
state.duration = audioContext.value.duration
|
||||
durationDesc.value = formatTime(parseInt(audioContext.value.duration))
|
||||
state.loading = false
|
||||
})
|
||||
state.duration = audioContext.value.duration;
|
||||
durationDesc.value = formatTime(parseInt(audioContext.value.duration));
|
||||
state.loading = false;
|
||||
});
|
||||
|
||||
audioContext.value.onTimeUpdate(() => {
|
||||
if (audioContext.value.duration == 0) {
|
||||
state.progress = 0
|
||||
state.progress = 0;
|
||||
} else {
|
||||
state.currentTime = audioContext.value.currentTime
|
||||
state.currentTime = audioContext.value.currentTime;
|
||||
state.progress =
|
||||
(audioContext.value.currentTime / audioContext.value.duration) * 100
|
||||
(audioContext.value.currentTime / audioContext.value.duration) * 100;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
audioContext.value.onEnded(() => {
|
||||
state.isAudioPlay = false
|
||||
state.progress = 0
|
||||
})
|
||||
state.isAudioPlay = false;
|
||||
state.progress = 0;
|
||||
});
|
||||
|
||||
audioContext.value.onError((e) => {
|
||||
console.log('音频播放异常===>', e)
|
||||
})
|
||||
|
||||
window.addEventListener('audio-play', onOtherAudioPlay)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('audio-play', onOtherAudioPlay)
|
||||
audioContext.value &&
|
||||
audioContext.value.destroy &&
|
||||
audioContext.value.destroy()
|
||||
})
|
||||
|
||||
onHide(() => {
|
||||
if (audioContext.value && audioContext.value.pause) {
|
||||
audioContext.value.pause()
|
||||
state.isAudioPlay = false
|
||||
}
|
||||
})
|
||||
console.log("音频播放异常===>", e);
|
||||
});
|
||||
});
|
||||
|
||||
const onPlay = () => {
|
||||
if (state.isAudioPlay) {
|
||||
audioContext.value.pause()
|
||||
state.isAudioPlay = false
|
||||
audioContext.value.pause();
|
||||
} else {
|
||||
// 通知其它音频暂停
|
||||
window.dispatchEvent(
|
||||
new CustomEvent('audio-play', { detail: props.data.msg_id }),
|
||||
)
|
||||
audioContext.value.play()
|
||||
state.isAudioPlay = true
|
||||
audioContext.value.play();
|
||||
}
|
||||
}
|
||||
|
||||
state.isAudioPlay = !state.isAudioPlay;
|
||||
};
|
||||
|
||||
const onPlayEnd = () => {
|
||||
state.isAudioPlay = false;
|
||||
state.progress = 0;
|
||||
};
|
||||
|
||||
const formatTime = (value: number = 0) => {
|
||||
if (value == 0) {
|
||||
return '-'
|
||||
return "-";
|
||||
}
|
||||
const minutes = Math.floor(value / 60)
|
||||
let seconds = value
|
||||
|
||||
const minutes = Math.floor(value / 60);
|
||||
let seconds = value;
|
||||
if (minutes > 0) {
|
||||
seconds = Math.floor(value - minutes * 60)
|
||||
seconds = Math.floor(value - minutes * 60);
|
||||
}
|
||||
return `${minutes}'${seconds}"`
|
||||
}
|
||||
|
||||
return `${minutes}'${seconds}"`;
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div
|
||||
class="audio-message"
|
||||
@click.stop="onPlay"
|
||||
:class="
|
||||
props?.data?.user_id == talkParams.uid
|
||||
? 'justify-end py-[22rpx] pl-[30rpx] pr-[16rpx]'
|
||||
: 'justify-start py-[22rpx] pr-[30rpx] pl-[16rpx]'
|
||||
"
|
||||
>
|
||||
<a-trumpet
|
||||
v-if="props?.data?.user_id != talkParams.uid"
|
||||
:isPlay="state.isAudioPlay"
|
||||
color="#C1C1C1"
|
||||
:size="30"
|
||||
></a-trumpet>
|
||||
<div
|
||||
:class="
|
||||
props?.data?.user_id == talkParams.uid ? 'mr-[8rpx]' : 'ml-[8rpx]'
|
||||
"
|
||||
>
|
||||
{{ Math.ceil(props?.extra?.duration / 1000) }}s
|
||||
<div class="im-message-audio">
|
||||
<div class="play">
|
||||
<div class="btn pointer" @click.stop="onPlay">
|
||||
<n-icon
|
||||
:size="18"
|
||||
:component="state.isAudioPlay ? PauseOne : PlayOne"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<a-trumpet
|
||||
v-if="props?.data?.user_id == talkParams.uid"
|
||||
:isPlay="state.isAudioPlay"
|
||||
color="#C1C1C1"
|
||||
:size="30"
|
||||
direction="left"
|
||||
></a-trumpet>
|
||||
<div class="desc">
|
||||
<span class="line" v-for="i in 23" :key="i"></span>
|
||||
<span
|
||||
class="indicator"
|
||||
:style="{ left: state.progress + '%' }"
|
||||
v-show="state.progress > 0"
|
||||
></span>
|
||||
</div>
|
||||
<div class="time">{{ durationDesc }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="less" scoped>
|
||||
.audio-message {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #fff;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.im-message-audio {
|
||||
--audio-bg-color: #f5f5f5;
|
||||
--audio-btn-bg-color: #ffffff;
|
||||
@ -291,7 +245,7 @@ const formatTime = (value: number = 0) => {
|
||||
}
|
||||
}
|
||||
|
||||
html[theme-mode='dark'] {
|
||||
html[theme-mode="dark"] {
|
||||
.im-message-audio {
|
||||
--audio-bg-color: #2c2c32;
|
||||
--audio-btn-bg-color: rgb(78, 75, 75);
|
||||
|
@ -25,130 +25,69 @@ const getFileTypeIMG = computed(() => {
|
||||
let objT = {
|
||||
finishedImg: '',
|
||||
blankImg: '',
|
||||
progressColor: '',
|
||||
}
|
||||
progressColor: ''
|
||||
};
|
||||
|
||||
switch (suffix) {
|
||||
case 'pdf':
|
||||
objT.finishedImg = filePaperPDF
|
||||
objT.blankImg = filePaperPDFBlank
|
||||
objT.progressColor = '#DE4E4E'
|
||||
break
|
||||
break;
|
||||
case 'doc':
|
||||
case 'docx':
|
||||
objT.finishedImg = filePaperWord
|
||||
objT.blankImg = filePaperWordBlank
|
||||
objT.progressColor = '#2750B2'
|
||||
break
|
||||
break;
|
||||
case 'xls':
|
||||
case 'xlsx':
|
||||
objT.finishedImg = filePaperExcel
|
||||
objT.blankImg = filePaperExcelBlank
|
||||
objT.progressColor = '#3C7F4B'
|
||||
break
|
||||
break;
|
||||
case 'ppt':
|
||||
case 'pptx':
|
||||
objT.finishedImg = filePaperPPT
|
||||
objT.blankImg = filePaperPPTBlank
|
||||
objT.progressColor = '#B74B2B'
|
||||
break
|
||||
break;
|
||||
default:
|
||||
objT.finishedImg = filePaperOther
|
||||
objT.blankImg = filePaperOtherBlank
|
||||
objT.progressColor = '#46299d'
|
||||
objT.progressColor = '#747474'
|
||||
}
|
||||
return objT
|
||||
})
|
||||
|
||||
const previewPDF = () => {
|
||||
if (typeof plus !== 'undefined') {
|
||||
downloadAndOpenFile()
|
||||
} else {
|
||||
document.addEventListener('plusready', () => {
|
||||
downloadAndOpenFile()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const downloadAndOpenFile = () => {
|
||||
uni.showLoading({ title: '加载中...', mask: true })
|
||||
const downloadUrl = props?.extra?.path
|
||||
if (!downloadUrl) {
|
||||
uni.hideLoading()
|
||||
uni.showToast({ title: '文件路径无效', icon: 'none' })
|
||||
return
|
||||
}
|
||||
const options = {
|
||||
filename: '_doc/downloads/', // 保存路径
|
||||
}
|
||||
const dtask = plus.downloader.createDownload(downloadUrl, options, function (
|
||||
d,
|
||||
status,
|
||||
) {
|
||||
if (status === 200) {
|
||||
uni.hideLoading()
|
||||
const filePath = d.filename
|
||||
if (filePath) {
|
||||
plus.runtime.openFile(filePath, {}, function () {})
|
||||
} else {
|
||||
uni.showToast({ title: '文件路径无效', icon: 'none' })
|
||||
}
|
||||
} else {
|
||||
uni.hideLoading()
|
||||
}
|
||||
})
|
||||
dtask.start()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section
|
||||
class="file-message"
|
||||
@click="previewPDF"
|
||||
:class="{ left: data.float === 'left', right: data.float === 'right' }"
|
||||
>
|
||||
<div class="flex justify-between">
|
||||
<div
|
||||
class="w-[228rpx] text-[32rpx] text-[#1A1A1A] h-[88rpx] leading-[44rpx] textEllipsis file_name"
|
||||
>
|
||||
<div class="w-[228rpx] text-[32rpx] text-[#1A1A1A] h-[88rpx] leading-[44rpx] textEllipsis">
|
||||
{{ extra.name }}
|
||||
</div>
|
||||
<div
|
||||
v-if="data.uploadStatus === 2 || !data.uploadStatus"
|
||||
class="w-[95rpx]"
|
||||
>
|
||||
<tm-image
|
||||
:width="95"
|
||||
:height="95"
|
||||
:src="getFileTypeIMG.finishedImg"
|
||||
></tm-image>
|
||||
<div v-if="data.uploadStatus === 2 || !data.uploadStatus" class="w-[95rpx]">
|
||||
<tm-image :width="95" :height="95" :src="getFileTypeIMG.finishedImg"></tm-image>
|
||||
</div>
|
||||
<div
|
||||
v-if="data.uploadStatus === 1 || data.uploadStatus === 3"
|
||||
class="w-[95rpx]"
|
||||
>
|
||||
<tm-image
|
||||
:width="95"
|
||||
:height="95"
|
||||
:src="getFileTypeIMG.blankImg"
|
||||
></tm-image>
|
||||
<wd-circle v-if="data.uploadStatus === 1"
|
||||
customClass="circleProgress"
|
||||
:modelValue="data.uploadCurrent"
|
||||
layerColor="#E3E3E3"
|
||||
:color="getFileTypeIMG.progressColor"
|
||||
:strokeWidth="3"
|
||||
:size="20"
|
||||
></wd-circle>
|
||||
<div class="upload-failed" v-if="data.uploadStatus === 3">
|
||||
<tm-icon :font-size="20" name="tmicon-times" color="#fff"></tm-icon>
|
||||
</div>
|
||||
<div v-if="data.uploadStatus === 1 || data.uploadStatus === 3" class="w-[95rpx]">
|
||||
<tm-image :width="95" :height="95" :src="getFileTypeIMG.blankImg"></tm-image>
|
||||
<wd-circle
|
||||
customClass="circleProgress"
|
||||
:modelValue="data.uploadCurrent"
|
||||
layerColor="#E3E3E3"
|
||||
:color="getFileTypeIMG.progressColor"
|
||||
:strokeWidth="3"
|
||||
:size="20"
|
||||
></wd-circle>
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider mt-[28rpx]"></div>
|
||||
<div class="text-[24rpx] text-[#747474] mt-[10rpx]">
|
||||
{{ fileFormatSize(extra.size) }}
|
||||
</div>
|
||||
<div class="text-[24rpx] text-[#747474] mt-[10rpx]">{{ fileFormatSize(extra.size) }}</div>
|
||||
<!-- <div class="main">
|
||||
<div class="ext">{{ getFileNameSuffix(extra.name) }}</div>
|
||||
<div class="file-box">
|
||||
@ -185,11 +124,6 @@ const downloadAndOpenFile = () => {
|
||||
border-radius: 16rpx 0 16rpx 16rpx;
|
||||
}
|
||||
|
||||
.file_name {
|
||||
word-break: break-all; /* 在任意字符间断行 */
|
||||
word-wrap: break-word; /* 允许长单词换行到下一行 */
|
||||
}
|
||||
|
||||
.main {
|
||||
height: 45px;
|
||||
display: flex;
|
||||
@ -278,7 +212,7 @@ const downloadAndOpenFile = () => {
|
||||
}
|
||||
|
||||
.divider {
|
||||
background-color: #e7e7e7;
|
||||
background-color: #E7E7E7;
|
||||
height: 1rpx;
|
||||
}
|
||||
|
||||
@ -291,20 +225,4 @@ const downloadAndOpenFile = () => {
|
||||
width: 40rpx !important;
|
||||
height: 40rpx !important;
|
||||
}
|
||||
|
||||
.upload-failed {
|
||||
position: absolute;
|
||||
top: 120rpx;
|
||||
right: 52rpx;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 1;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #ff4d4f;
|
||||
background: #ff4d4f;
|
||||
border-radius: 50%;
|
||||
}
|
||||
</style>
|
||||
|
@ -12,25 +12,19 @@ const props = defineProps<{
|
||||
const isShowRecord = ref(false)
|
||||
|
||||
const title = computed(() => {
|
||||
const uniqueNames = [...new Set(props.extra.records.map(v => v.nickname))];
|
||||
if (uniqueNames.length <= 2) {
|
||||
return uniqueNames.join('和');
|
||||
} else {
|
||||
return uniqueNames.slice(0, 2).join('和') + '等';
|
||||
}
|
||||
// return [...new Set(props.extra.records.map((v) => v.nickname))].join('和')
|
||||
return [...new Set(props.extra.records.map((v) => v.nickname))].join('、')
|
||||
})
|
||||
console.log(props.extra.records)
|
||||
|
||||
const onClick = () => {
|
||||
// isShowRecord.value = true
|
||||
uni.navigateTo({
|
||||
url: '/pages/forwardRecord/index?msgId=' + props.data.msg_id + '&created_at=' + props.data?.created_at
|
||||
url: '/pages/forwardRecord/index?msgId=' + props.data.msg_id
|
||||
})
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<section class="im-message-forward pointer" :class="{ left: data.float === 'left' }" @click="onClick">
|
||||
<div class="title">{{ extra.forward_name || title}}的会话记录</div>
|
||||
<div class="title">{{ title }} 的会话记录</div>
|
||||
<div class="list" v-for="(record, index) in extra.records" :key="index">
|
||||
<p>
|
||||
<span>{{ record.nickname }}: </span>
|
||||
@ -65,14 +59,13 @@ const onClick = () => {
|
||||
}
|
||||
|
||||
.title {
|
||||
max-height: 88rpx;
|
||||
height: 44rpx;
|
||||
line-height: 44rpx;
|
||||
font-size: 32rpx;
|
||||
color: #1A1A1A;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-weight: 400;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
@ -12,40 +12,40 @@ const img = computed(() => {
|
||||
// console.log(props.extra);
|
||||
let info = {
|
||||
width: 0,
|
||||
height: 0,
|
||||
height: 0
|
||||
}
|
||||
if (props.extra.url.includes('blob:http://')) {
|
||||
info = {
|
||||
width: props.extra.width,
|
||||
height: props.extra.height,
|
||||
height: props.extra.height
|
||||
}
|
||||
} else {
|
||||
}else {
|
||||
info = getImageInfo(props.extra.url)
|
||||
}
|
||||
|
||||
if (info.width == 0 || info.height == 0) {
|
||||
return {
|
||||
width: 450,
|
||||
height: 298,
|
||||
height: 298
|
||||
}
|
||||
}
|
||||
if (info.width < 300) {
|
||||
if(info.width<300){
|
||||
return {
|
||||
width: 300,
|
||||
height: info.height / (info.width / 300),
|
||||
height: info.height / (info.width / 300)
|
||||
}
|
||||
}
|
||||
|
||||
if (info.width < 350) {
|
||||
return {
|
||||
width: info.width,
|
||||
height: info.height,
|
||||
height: info.height
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
width: 350,
|
||||
height: info.height / (info.width / 350),
|
||||
height: info.height / (info.width / 350)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@ -54,30 +54,13 @@ const img = computed(() => {
|
||||
class="im-message-image"
|
||||
:class="{
|
||||
left: data.float === 'left',
|
||||
right: data.float === 'right',
|
||||
right: data.float === 'right'
|
||||
}"
|
||||
>
|
||||
<div class="image-container">
|
||||
<div class="relative">
|
||||
<tm-image
|
||||
preview
|
||||
:width="img.width"
|
||||
:height="img.height"
|
||||
:src="extra.url"
|
||||
model="aspectFill"
|
||||
/>
|
||||
<wd-circle
|
||||
custom-class="circleProgress"
|
||||
v-if="data.uploadStatus === 1"
|
||||
v-model="props.data.uploadCurrent"
|
||||
color="#46299d"
|
||||
layer-color="#E3E3E3"
|
||||
></wd-circle>
|
||||
<div class="upload-failed" v-if="data.uploadStatus === 3">
|
||||
<tm-icon :font-size="20" name="tmicon-times" color="#fff"></tm-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="image-container">
|
||||
<tm-image preview :width="img.width" :height="img.height" :src="extra.url" />
|
||||
<wd-circle custom-class="circleProgress" v-if="props.data.uploadCurrent && props.data.uploadCurrent<100" v-model="props.data.uploadCurrent" color="#ffffff" layer-color="#E3E3E3"></wd-circle>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
<style lang="less" scoped>
|
||||
@ -96,12 +79,12 @@ const img = computed(() => {
|
||||
}
|
||||
|
||||
&.right {
|
||||
background-color: #46299d;
|
||||
background-color: #46299D;
|
||||
border-radius: 16rpx 0 16rpx 16rpx;
|
||||
}
|
||||
}
|
||||
.image-container {
|
||||
position: relative;
|
||||
position: relative;
|
||||
|
||||
.circleProgress {
|
||||
position: absolute;
|
||||
@ -111,18 +94,4 @@ const img = computed(() => {
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
.upload-failed {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 1;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #ff4d4f;
|
||||
border-radius: 50%;
|
||||
}
|
||||
</style>
|
||||
|
@ -4,97 +4,34 @@ import { formatTime } from '@/utils/datetime'
|
||||
defineProps({
|
||||
login_uid: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
default: 0
|
||||
},
|
||||
user_id: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
default: 0
|
||||
},
|
||||
talk_type: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
default: 0
|
||||
},
|
||||
nickname: {
|
||||
type: String,
|
||||
default: '',
|
||||
default: ''
|
||||
},
|
||||
datetime: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
msg_id: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
revokeInfo: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
},
|
||||
},
|
||||
extra:{
|
||||
type: String,
|
||||
default: '',
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<div class="im-message-revoke">
|
||||
<div class="content" v-if="JSON.stringify(revokeInfo) !== '{}'">
|
||||
<span v-if="talk_type === 1 && login_uid === revokeInfo.withdraw_id">
|
||||
你撤回了一条消息 | {{ formatTime(datetime) }}
|
||||
<slot></slot>
|
||||
<!-- 添加插槽用于放置重新编辑按钮 -->
|
||||
</span>
|
||||
<span v-if="talk_type === 1 && login_uid !== revokeInfo.withdraw_id">
|
||||
{{revokeInfo.withdraw_name}}撤回了一条消息 | {{ formatTime(datetime) }}
|
||||
</span>
|
||||
<span v-if="talk_type === 2 && login_uid === revokeInfo.withdraw_id && login_uid === revokeInfo.retracted_id">
|
||||
你撤回了一条消息 |
|
||||
<div class="content">
|
||||
<span v-if="login_uid == user_id"> 你撤回了一条消息 | {{ formatTime(datetime) }} </span>
|
||||
<span v-else-if="talk_type == 1"> 对方撤回了一条消息 | {{ formatTime(datetime) }} </span>
|
||||
<span v-else>
|
||||
"{{ nickname }}" 撤回了一条消息 |
|
||||
{{ formatTime(datetime) }}
|
||||
<slot></slot>
|
||||
</span>
|
||||
<span v-if="talk_type === 2 && login_uid === revokeInfo.withdraw_id && login_uid !== revokeInfo.retracted_id">
|
||||
你撤回了{{revokeInfo.retracted_name}}一条消息 |
|
||||
{{ formatTime(datetime) }}
|
||||
</span>
|
||||
<span v-if="talk_type === 2 && login_uid !== revokeInfo.withdraw_id && revokeInfo.withdraw_id === revokeInfo.retracted_id">
|
||||
{{revokeInfo.withdraw_name}}撤回了一条消息 |
|
||||
{{ formatTime(datetime) }}
|
||||
</span>
|
||||
|
||||
<span v-if="talk_type === 2 && login_uid !== revokeInfo.withdraw_id && login_uid === revokeInfo.retracted_id && revokeInfo.withdraw_id !== revokeInfo.retracted_id">
|
||||
{{revokeInfo.withdraw_name}}撤回了你一条消息 |
|
||||
{{ formatTime(datetime) }}
|
||||
</span>
|
||||
<span v-if="talk_type === 2 && login_uid !== revokeInfo.withdraw_id && login_uid !== revokeInfo.retracted_id && revokeInfo.withdraw_id !== revokeInfo.retracted_id">
|
||||
{{revokeInfo.withdraw_name}}撤回了{{revokeInfo.retracted_name}}一条消息 |
|
||||
{{ formatTime(datetime) }}
|
||||
</span>
|
||||
|
||||
<!-- <span v-if="login_uid == user_idA"> 你撤回B了一条消息 | {{ formatTime(datetime) }} </span>
|
||||
<span v-else-if="login_uid == user_idB"> A撤回你了一条消息 | {{ formatTime(datetime) }} </span>
|
||||
<span v-else> A撤回B了一条消息 | {{ formatTime(datetime) }} </span> -->
|
||||
</div>
|
||||
<div class="content" v-if="JSON.stringify(revokeInfo) === '{}'">
|
||||
<span v-if="talk_type === 1 && login_uid === user_id">
|
||||
你撤回了一条消息 | {{ formatTime(datetime) }}
|
||||
<slot></slot>
|
||||
<!-- 添加插槽用于放置重新编辑按钮 -->
|
||||
</span>
|
||||
<span v-if="talk_type === 1 && login_uid !== user_id">
|
||||
{{nickname}}撤回了一条消息 | {{ formatTime(datetime) }}
|
||||
</span>
|
||||
<span v-if="talk_type === 2 && !extra && login_uid === user_id">
|
||||
你撤回了一条消息 | {{ formatTime(datetime) }}
|
||||
<slot></slot>
|
||||
<!-- 添加插槽用于放置重新编辑按钮 -->
|
||||
</span>
|
||||
<span v-if="talk_type === 2 && !extra && login_uid !== user_id">
|
||||
{{nickname}}撤回了一条消息 | {{ formatTime(datetime) }}
|
||||
</span>
|
||||
<span v-if="talk_type === 2 && extra">
|
||||
{{extra}} | {{ formatTime(datetime) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -2,7 +2,6 @@
|
||||
import { textReplaceEmoji } from '@/utils/emojis'
|
||||
import { textReplaceLink, textReplaceMention } from '@/utils/strings'
|
||||
import { ITalkRecordExtraText, ITalkRecord } from '@/types/chat'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = defineProps<{
|
||||
extra: ITalkRecordExtraText
|
||||
@ -13,14 +12,15 @@ const props = defineProps<{
|
||||
|
||||
const float = props.data.float
|
||||
|
||||
const textContent = computed(() => {
|
||||
let text = props.extra?.content || ''
|
||||
// text = textReplaceLink(text)
|
||||
if (props.data.talk_type == 2) {
|
||||
text = textReplaceMention(text, '#1890ff')
|
||||
}
|
||||
return textReplaceEmoji(text)
|
||||
})
|
||||
let textContent = props.extra?.content || ''
|
||||
|
||||
textContent = textReplaceLink(textContent)
|
||||
|
||||
if (props.data.talk_type == 2) {
|
||||
textContent = textReplaceMention(textContent, '#1890ff')
|
||||
}
|
||||
|
||||
textContent = textReplaceEmoji(textContent)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -1,8 +1,8 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, nextTick, getCurrentInstance, computed, onMounted } from 'vue'
|
||||
import { getImageInfo } from '@/utils/functions'
|
||||
import playCircle from '@/static/image/chatList/playCircle@2x.png'
|
||||
import { useStatus } from '@/store/status'
|
||||
import playCircle from "@/static/image/chatList/playCircle@2x.png";
|
||||
import { useStatus } from "@/store/status";
|
||||
|
||||
const { statusBarHeight } = useStatus()
|
||||
const instance = getCurrentInstance()
|
||||
@ -20,12 +20,12 @@ const open = ref(false)
|
||||
const img = computed(() => {
|
||||
let info = {
|
||||
width: 0,
|
||||
height: 0,
|
||||
height: 0
|
||||
}
|
||||
if (props.extra.url.includes('blob:http://')) {
|
||||
info = {
|
||||
width: props.extra.width,
|
||||
height: props.extra.height,
|
||||
height: props.extra.height
|
||||
}
|
||||
} else {
|
||||
info = getImageInfo(props.extra.url)
|
||||
@ -34,26 +34,26 @@ const img = computed(() => {
|
||||
if (info.width == 0 || info.height == 0) {
|
||||
return {
|
||||
width: 450,
|
||||
height: 298,
|
||||
height: 298
|
||||
}
|
||||
}
|
||||
if (info.width < 300) {
|
||||
return {
|
||||
width: 300,
|
||||
height: info.height / (info.width / 300),
|
||||
height: info.height / (info.width / 300)
|
||||
}
|
||||
}
|
||||
|
||||
if (info.width < 350) {
|
||||
return {
|
||||
width: info.width,
|
||||
height: info.height,
|
||||
height: info.height
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
width: 350,
|
||||
height: info.height / (info.width / 350),
|
||||
height: info.height / (info.width / 350)
|
||||
}
|
||||
})
|
||||
|
||||
@ -67,98 +67,57 @@ const fullscreenchange = (e) => {
|
||||
|
||||
/* 视频播放 获取第一帧 */
|
||||
const canplay = (e) => {
|
||||
console.log('Video can play:', e)
|
||||
console.log('Video can play:', e);
|
||||
|
||||
if (e.target) {
|
||||
setTimeout(() => {
|
||||
e.target.pause()
|
||||
}, 200)
|
||||
e.target.pause();
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
async function onPlay() {
|
||||
videoContext.value = uni.createVideoContext(props.extra.url, instance);
|
||||
videoContext.value.requestFullScreen({ direction: 2 });
|
||||
videoContext.value.play()
|
||||
open.value = true
|
||||
await nextTick()
|
||||
videoContext.value = uni.createVideoContext(props.extra.url, instance)
|
||||
|
||||
setTimeout(() => {
|
||||
// 先请求全屏
|
||||
videoContext.value.requestFullScreen({ direction: 2 })
|
||||
|
||||
// 延迟一下再播放,确保全屏已经完成
|
||||
setTimeout(() => {
|
||||
videoContext.value.play()
|
||||
}, 100)
|
||||
}, 200)
|
||||
}
|
||||
onMounted(() => {
|
||||
videoRef.value = uni.createVideoContext(props.data.msg_id)
|
||||
videoRef.value = uni.createVideoContext(props.data.msg_id);
|
||||
videoRef.value.play()
|
||||
setTimeout(() => {
|
||||
videoRef.value.pause()
|
||||
}, 200)
|
||||
}, 200);
|
||||
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<section
|
||||
class="im-message-video"
|
||||
:class="{ left: data.float === 'left' }"
|
||||
@click="onPlay"
|
||||
>
|
||||
<div
|
||||
class="coverVideo"
|
||||
:style="{
|
||||
width: img.width + 'rpx',
|
||||
height: img.height + 'rpx',
|
||||
}"
|
||||
v-if="props.extra.url.includes('blob:http://')"
|
||||
>
|
||||
<video
|
||||
:id="data.msg_id"
|
||||
:autoplay="false"
|
||||
disablepictureinpicture
|
||||
muted
|
||||
:src="props.extra.url"
|
||||
width="100%"
|
||||
height="100%"
|
||||
playsinline
|
||||
preload="auto"
|
||||
controls="false"
|
||||
x5-playsinline
|
||||
webkit-playsinline
|
||||
style="object-fit: cover; pointer-events: none;"
|
||||
></video>
|
||||
<section class="im-message-video" :class="{ left: data.float === 'left' }" @click="onPlay">
|
||||
<div class="coverVideo" :style="{
|
||||
width: img.width + 'rpx',
|
||||
height: img.height + 'rpx'
|
||||
}" v-if="props.extra.url.includes('blob:http://')">
|
||||
<video :id="data.msg_id" :autoplay="false" disablepictureinpicture muted :src="props.extra.url" width="100%"
|
||||
height="100%" playsinline preload="auto" controls="false" x5-playsinline
|
||||
webkit-playsinline style="object-fit: cover; pointer-events: none;">
|
||||
</video>
|
||||
</div>
|
||||
<wd-img
|
||||
v-else
|
||||
:width="`${img.width}rpx`"
|
||||
:height="`${img.height}rpx`"
|
||||
:src="data.extra.cover"
|
||||
/>
|
||||
<div
|
||||
v-if="data.uploadStatus === 2 || !data.uploadStatus"
|
||||
class="btn-video"
|
||||
:style="{
|
||||
width: img.width + 'rpx',
|
||||
height: img.height + 'rpx',
|
||||
}"
|
||||
>
|
||||
<wd-img v-else :width="`${img.width}rpx`" :height="`${img.height}rpx`" :src="data.extra.cover" />
|
||||
<div v-if="data.uploadStatus === 2 || !data.uploadStatus" class="btn-video" :style="{
|
||||
width: img.width + 'rpx',
|
||||
height: img.height + 'rpx'
|
||||
}">
|
||||
<tm-image :src="playCircle" :width="80" :height="80" />
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="btn-video"
|
||||
:style="{
|
||||
width: img.width + 'rpx',
|
||||
height: img.height + 'rpx',
|
||||
}"
|
||||
>
|
||||
<div v-else class="btn-video" :style="{
|
||||
width: img.width + 'rpx',
|
||||
height: img.height + 'rpx'
|
||||
}" >
|
||||
<wd-circle
|
||||
v-if="data.uploadStatus === 1"
|
||||
v-model="props.data.uploadCurrent"
|
||||
customClass="circleProgress"
|
||||
color="#46299d"
|
||||
layer-color="#E3E3E3"
|
||||
layerColor="#E3E3E3"
|
||||
color="#FFFFFF"
|
||||
:strokeWidth="6"
|
||||
:size="40"
|
||||
></wd-circle>
|
||||
@ -172,32 +131,18 @@ onMounted(() => {
|
||||
:width="70"
|
||||
:percent="props.data.uploadCurrent">
|
||||
</tm-progress> -->
|
||||
<div class="upload-failed" v-if="data.uploadStatus === 3">
|
||||
<tm-icon :font-size="20" name="tmicon-times" color="#fff"></tm-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="open">
|
||||
<video :src="props.extra.url" controls @fullscreenchange="fullscreenchange" :id="props.extra.url">
|
||||
</video>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<teleport to="body">
|
||||
<div v-show="open" class="video-container">
|
||||
<video
|
||||
:src="props.extra.url"
|
||||
controls
|
||||
@fullscreenchange="fullscreenchange"
|
||||
:id="props.extra.url"
|
||||
playsinline
|
||||
webkit-playsinline
|
||||
x5-playsinline
|
||||
class="fullscreen-video"
|
||||
></video>
|
||||
</div>
|
||||
</teleport>
|
||||
</template>
|
||||
<style lang="less" scoped>
|
||||
.im-message-video {
|
||||
overflow: hidden;
|
||||
padding: 20rpx 18rpx;
|
||||
background: #46299d;
|
||||
background: #46299D;
|
||||
min-width: 30rpx;
|
||||
min-height: 30rpx;
|
||||
display: inline-flex;
|
||||
@ -255,43 +200,10 @@ onMounted(() => {
|
||||
:deep(.uni-video-bar) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
}
|
||||
.circleProgress {
|
||||
width: 80rpx !important;
|
||||
height: 80rpx !important;
|
||||
}
|
||||
|
||||
.video-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: #000;
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.fullscreen-video {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.upload-failed {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 1;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #ff4d4f;
|
||||
border-radius: 50%;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,25 +0,0 @@
|
||||
<script setup>
|
||||
import './sys-message.less'
|
||||
import { useInject } from '@/hooks'
|
||||
|
||||
defineProps({
|
||||
extra: Object,
|
||||
data: Object
|
||||
})
|
||||
|
||||
const { showUserInfoModal } = useInject()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="im-message-sys-text">
|
||||
<div class="sys-text">
|
||||
|
||||
<template v-for="(user, index) in extra.members" :key="index">
|
||||
<a @click="showUserInfoModal(user.user_id)">{{ user.nickname }}</a>
|
||||
<em v-show="index < extra.members.length - 1">、</em>
|
||||
</template>
|
||||
|
||||
<span>已成为管理员</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
@ -1,19 +0,0 @@
|
||||
<script setup>
|
||||
import './sys-message.less'
|
||||
import { useInject } from '@/hooks'
|
||||
|
||||
const { showUserInfoModal } = useInject()
|
||||
|
||||
defineProps({
|
||||
extra: Object,
|
||||
data: Object
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="im-message-sys-text">
|
||||
<div class="sys-text">
|
||||
<span>{{ extra.content }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
@ -1,25 +0,0 @@
|
||||
<script setup>
|
||||
import './sys-message.less'
|
||||
import { useInject } from '@/hooks'
|
||||
|
||||
const { showUserInfoModal } = useInject()
|
||||
|
||||
defineProps({
|
||||
extra: Object,
|
||||
data: Object
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="im-message-sys-text">
|
||||
<div class="sys-text">
|
||||
<a @click="showUserInfoModal(data.user_id)">
|
||||
<!-- {{ data.nickname }} -->
|
||||
管理员
|
||||
</a>
|
||||
<!-- <span>修改群名为</span>
|
||||
<span>"{{ extra.group_name }}"</span> -->
|
||||
<span>修改了群信息</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
@ -24,7 +24,7 @@ const { showUserInfoModal } = useInject()
|
||||
<em v-show="index < extra.members.length - 1">、</em>
|
||||
</template>
|
||||
|
||||
<span>移出群聊</span>
|
||||
<span>踢出群聊</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -1,23 +0,0 @@
|
||||
<script setup>
|
||||
import './sys-message.less'
|
||||
import { useInject } from '@/hooks'
|
||||
|
||||
const { showUserInfoModal } = useInject()
|
||||
|
||||
defineProps({
|
||||
extra: Object,
|
||||
data: Object,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="im-message-sys-text">
|
||||
<div class="sys-text">
|
||||
<template v-for="(user, index) in extra?.members" :key="index">
|
||||
<a @click="showUserInfoModal(user.user_id)">{{ user.nickname }}</a>
|
||||
<em v-show="index < extra.members.length - 1">、</em>
|
||||
</template>
|
||||
<span>已离开此群</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
@ -1,153 +0,0 @@
|
||||
<script setup>
|
||||
import { handleFindWebview } from '@/utils/common'
|
||||
import noClockAfterWork from '@/static/image/chatBotMessageCard/noClockAfterWork.png'
|
||||
import noClockBeforeWork from '@/static/image/chatBotMessageCard/noClockBeforeWork.png'
|
||||
import copy from '@/static/image/chatBotMessageCard/copy.png'
|
||||
import errorClock from '@/static/image/chatBotMessageCard/errorClock.png'
|
||||
import prompt from '@/static/image/chatBotMessageCard/prompt.png'
|
||||
|
||||
const emits = defineEmits(['openPromptDrawer'])
|
||||
|
||||
const props = defineProps({
|
||||
extra: Object,
|
||||
data: Object,
|
||||
})
|
||||
|
||||
//聊天助手发送的系统消息卡片图片
|
||||
const getMessageCard = computed(() => {
|
||||
switch (props.data.msg_type) {
|
||||
case 1117: //催办提醒
|
||||
return prompt
|
||||
case 1118: //上班未打卡
|
||||
return noClockBeforeWork
|
||||
case 1119: //下班未打卡
|
||||
return noClockAfterWork
|
||||
case 1120: //待审批
|
||||
return prompt
|
||||
case 1121: //抄送
|
||||
return copy
|
||||
case 1122: //缺卡
|
||||
return errorClock
|
||||
case 1123: //迟到
|
||||
return errorClock
|
||||
case 1124: //早退
|
||||
return errorClock
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
})
|
||||
|
||||
//聊天助手发送的系统消息文字颜色
|
||||
const getTextColor = computed(() => {
|
||||
const msgType = props.data.msg_type
|
||||
//审批催办、审批
|
||||
if ([1117, 1120].includes(msgType)) return '#2668BF'
|
||||
//上班未打卡、下班未打卡
|
||||
if ([1118, 1119].includes(msgType)) return '#46299D'
|
||||
//抄送
|
||||
if (msgType === 1121) return '#2099BE'
|
||||
//缺卡、迟到、早退
|
||||
if ([1122, 1123, 1124].includes(msgType)) return '#933BA3'
|
||||
return ''
|
||||
})
|
||||
|
||||
//聊天助手发送的系统消息文字内容
|
||||
const getTextContent = computed(() => {
|
||||
const msgType = props.data.msg_type
|
||||
//审批催办、审批
|
||||
if ([1117, 1120].includes(msgType)) return '立即审批'
|
||||
//上班未打卡、下班未打卡
|
||||
if ([1118, 1119].includes(msgType)) return '立即打卡'
|
||||
//抄送、缺卡、迟到、早退
|
||||
if ([1121, 1122, 1123, 1124].includes(msgType)) return '立即查看'
|
||||
return ''
|
||||
})
|
||||
|
||||
//点击向webview通信,处理智能助手系统消息
|
||||
const handleSysMessagePush = () => {
|
||||
if (props?.extra?.url) {
|
||||
//直接返回地址的,直接在当前页面弹出抽屉打开
|
||||
emits('openPromptDrawer', encodeURIComponent(props?.extra?.url))
|
||||
} else {
|
||||
handleFindWebview(
|
||||
`handleChatRobotSysMessagePush('${encodeURIComponent(
|
||||
JSON.stringify({
|
||||
url: props?.extra?.url,
|
||||
msg_type: props?.data?.msg_type,
|
||||
}),
|
||||
)}')`,
|
||||
)
|
||||
uni.reLaunch({
|
||||
url: '/pages/index/index',
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="sys-message-push" @click="handleSysMessagePush">
|
||||
<div class="sys-message-push-card">
|
||||
<span class="sys-message-push-card-title">{{ props.extra.title }}</span>
|
||||
<span
|
||||
class="sys-message-push-card-message"
|
||||
v-html="props.extra.message"
|
||||
></span>
|
||||
<img :src="getMessageCard" alt="" />
|
||||
</div>
|
||||
<div class="sys-message-push-card-btn">
|
||||
<span :style="{ color: getTextColor }">{{ getTextContent }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.sys-message-push {
|
||||
border-radius: 0 16rpx 16rpx 16rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 6px 12px 2px rgba(188, 188, 188, 0.08);
|
||||
.sys-message-push-card {
|
||||
position: relative;
|
||||
width: 486rpx;
|
||||
height: 270rpx;
|
||||
.sys-message-push-card-title {
|
||||
position: absolute;
|
||||
top: 28rpx;
|
||||
left: 30rpx;
|
||||
font-size: 24rpx;
|
||||
line-height: 34rpx;
|
||||
color: rgba(255, 255, 255, 0.73);
|
||||
font-weight: 500;
|
||||
}
|
||||
.sys-message-push-card-message {
|
||||
position: absolute;
|
||||
bottom: 44rpx;
|
||||
left: 30rpx;
|
||||
font-size: 40rpx;
|
||||
line-height: 58rpx;
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
min-width: 326rpx;
|
||||
max-width: 100%;
|
||||
}
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.sys-message-push-card-btn {
|
||||
background-color: #fff;
|
||||
width: 486rpx;
|
||||
height: 78rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text {
|
||||
color: #000;
|
||||
font-size: 28rpx;
|
||||
line-height: 40rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -30,7 +30,6 @@ class Connect {
|
||||
},
|
||||
// Websocket 连接成功回调方法
|
||||
onOpen: () => {
|
||||
console.log("socket已连接")
|
||||
// 更新 WebSocket 连接状态
|
||||
useUserStore().updateSocketStatus(true)
|
||||
// online.value = true;
|
||||
@ -38,7 +37,6 @@ class Connect {
|
||||
},
|
||||
// Websocket 断开连接回调方法
|
||||
onClose: () => {
|
||||
console.log("socket已断开")
|
||||
// 更新 WebSocket 连接状态
|
||||
useUserStore().updateSocketStatus(false)
|
||||
// online.value = false
|
||||
|
@ -17,7 +17,7 @@ export const ChatMsgSysText = 1000 // 系统文本消息
|
||||
export const ChatMsgSysGroupCreate = 1101 // 创建群聊消息
|
||||
export const ChatMsgSysGroupMemberJoin = 1102 // 加入群聊消息
|
||||
export const ChatMsgSysGroupMemberQuit = 1103 // 群成员退出群消息
|
||||
export const ChatMsgSysGroupMemberKicked = 1104 // 移出群成员消息(普通群、项目群被踢)
|
||||
export const ChatMsgSysGroupMemberKicked = 1104 // 踢出群成员消息
|
||||
export const ChatMsgSysGroupMessageRevoke = 1105 // 管理员撤回成员消息
|
||||
export const ChatMsgSysGroupDismissed = 1106 // 群解散
|
||||
export const ChatMsgSysGroupMuted = 1107 // 群禁言
|
||||
@ -26,17 +26,6 @@ export const ChatMsgSysGroupMemberMuted = 1109 // 群成员禁言
|
||||
export const ChatMsgSysGroupMemberCancelMuted = 1110 // 群成员解除禁言
|
||||
export const ChatMsgSysGroupNotice = 1111 // 编辑群公告
|
||||
export const ChatMsgSysGroupTransfer = 1113 // 变更群主
|
||||
export const ChatMsgSysGroupAdmin = 1114 // 设置管理员
|
||||
export const ChatMsgSysGroupMemberRemoved = 1115 // 移出群成员消息(部门群、公司群自动移出)
|
||||
export const ChatMsgSysGroupInfoChange = 1116 // 管理员更新了群信息
|
||||
export const ChatMsgSysPush_PromptReminder = 1117 // 系统推送消息-催办提醒
|
||||
export const ChatMsgSysPush_NoClockReminderBeforeWork = 1118 // 系统推送消息-上班未打卡
|
||||
export const ChatMsgSysPush_NoClockReminderAfterWork = 1119 // 系统推送消息-下班未打卡
|
||||
export const ChatMsgSysPush_ApprovalReminder = 1120 // 系统推送消息-待审批提醒
|
||||
export const ChatMsgSysPush_CopyReminder = 1121 // 系统推送消息-抄送提醒
|
||||
export const ChatMsgSysPush_AbsentReminder = 1122 // 系统推送消息-缺卡提醒
|
||||
export const ChatMsgSysPush_LateReminder = 1123 // 系统推送消息-迟到提醒
|
||||
export const ChatMsgSysPush_EarlyReminder = 1124 // 系统推送消息-早退提醒
|
||||
|
||||
export const ChatMsgTypeMapping = {
|
||||
[ChatMsgTypeText]: '[文本消息]',
|
||||
@ -57,26 +46,14 @@ export const ChatMsgTypeMapping = {
|
||||
[ChatMsgSysGroupCreate]: '[创建群消息]',
|
||||
[ChatMsgSysGroupMemberJoin]: '[加入群消息]',
|
||||
[ChatMsgSysGroupMemberQuit]: '[退出群消息]',
|
||||
[ChatMsgSysGroupMemberKicked]: '[移出群消息]',
|
||||
[ChatMsgSysGroupMemberKicked]: '[踢出群消息]',
|
||||
[ChatMsgSysGroupMessageRevoke]: '[撤回消息]',
|
||||
[ChatMsgSysGroupDismissed]: '[群解散消息]',
|
||||
[ChatMsgSysGroupMuted]: '[群禁言消息]',
|
||||
[ChatMsgSysGroupCancelMuted]: '[群解除禁言消息]',
|
||||
[ChatMsgSysGroupMemberMuted]: '[群成员禁言消息]',
|
||||
[ChatMsgSysGroupMemberCancelMuted]: '[群成员解除禁言消息]',
|
||||
[ChatMsgSysGroupNotice]: '[群公告]',
|
||||
[ChatMsgSysGroupTransfer]: '[转让群主]',
|
||||
[ChatMsgSysGroupAdmin]: '[设置管理员]',
|
||||
[ChatMsgSysGroupMemberRemoved]: '[移出群成员消息]',
|
||||
[ChatMsgSysGroupInfoChange]: '[群信息更新]',
|
||||
[ChatMsgSysPush_PromptReminder]: '[催办提醒]',
|
||||
[ChatMsgSysPush_NoClockReminderBeforeWork]: '[未打卡提醒]',
|
||||
[ChatMsgSysPush_NoClockReminderAfterWork]: '[未打卡提醒]',
|
||||
[ChatMsgSysPush_ApprovalReminder]: '[审批提醒]',
|
||||
[ChatMsgSysPush_CopyReminder]: '[抄送提醒]',
|
||||
[ChatMsgSysPush_AbsentReminder]: '[异常卡提醒]',
|
||||
[ChatMsgSysPush_LateReminder]: '[异常卡提醒]',
|
||||
[ChatMsgSysPush_EarlyReminder]: '[异常卡提醒]'
|
||||
[ChatMsgSysGroupNotice]: '[群公告]'
|
||||
}
|
||||
|
||||
// 消息类型 - 消息组件 映射关系
|
||||
@ -101,24 +78,12 @@ export const MessageComponents = {
|
||||
[ChatMsgSysGroupMemberQuit]: 'sys-group-member-quit-message',
|
||||
[ChatMsgSysGroupMemberKicked]: 'sys-group-member-kicked-message',
|
||||
// [ChatMsgSysGroupMessageRevoke]: '[撤回消息]',
|
||||
[ChatMsgSysGroupDismissed]: 'sys-group-dismissed',
|
||||
// [ChatMsgSysGroupDismissed]: '[群解散消息]',
|
||||
[ChatMsgSysGroupMuted]: 'sys-group-muted-message',
|
||||
[ChatMsgSysGroupCancelMuted]: 'sys-group-cancel-muted-message',
|
||||
[ChatMsgSysGroupMemberMuted]: 'sys-group-member-muted-message',
|
||||
[ChatMsgSysGroupMemberCancelMuted]: 'sys-group-member-cancel-muted-message',
|
||||
[ChatMsgSysGroupTransfer]: 'sys-group-transfer-message',
|
||||
[ChatMsgSysGroupAdmin]:'sys-group-admin-message',
|
||||
[ChatMsgSysGroupMemberRemoved]:'sys-group-member-removed-message',
|
||||
[ChatMsgSysGroupInfoChange]:'sys-group-info-change-message',
|
||||
// 智能助手推送的系统消息采用相同的模版
|
||||
[ChatMsgSysPush_PromptReminder]:'sys-push-message',
|
||||
[ChatMsgSysPush_NoClockReminderBeforeWork]:'sys-push-message',
|
||||
[ChatMsgSysPush_NoClockReminderAfterWork]:'sys-push-message',
|
||||
[ChatMsgSysPush_ApprovalReminder]:'sys-push-message',
|
||||
[ChatMsgSysPush_CopyReminder]:'sys-push-message',
|
||||
[ChatMsgSysPush_AbsentReminder]:'sys-push-message',
|
||||
[ChatMsgSysPush_LateReminder]:'sys-push-message',
|
||||
[ChatMsgSysPush_EarlyReminder]:'sys-push-message'
|
||||
[ChatMsgSysGroupTransfer]: 'sys-group-transfer-message'
|
||||
}
|
||||
|
||||
// 可转发的消息类型
|
||||
@ -126,11 +91,10 @@ export const ForwardableMessageType = [
|
||||
ChatMsgTypeText,
|
||||
ChatMsgTypeCode,
|
||||
ChatMsgTypeImage,
|
||||
// ChatMsgTypeAudio,
|
||||
ChatMsgTypeAudio,
|
||||
ChatMsgTypeVideo,
|
||||
ChatMsgTypeFile,
|
||||
ChatMsgTypeLocation,
|
||||
ChatMsgTypeCard,
|
||||
ChatMsgTypeLink,
|
||||
ChatMsgTypeForward
|
||||
ChatMsgTypeLink
|
||||
]
|
||||
|
@ -68,38 +68,20 @@ class Revoke extends Base {
|
||||
}
|
||||
|
||||
handle() {
|
||||
const { updateDialogueRecord } = useDialogueListStore()
|
||||
const {updateDialogueRecord} = useDialogueListStore()
|
||||
useTalkStore().updateItem({
|
||||
index_name: this.getIndexName(),
|
||||
msg_text: this.resource.text,
|
||||
revokeInfo: {
|
||||
retracted_id: this.resource.retracted_id,
|
||||
retracted_name: this.resource.retracted_name,
|
||||
withdraw_id: this.resource.withdraw_id,
|
||||
withdraw_name: this.resource.withdraw_name,
|
||||
},
|
||||
updated_at: parseTime(new Date()),
|
||||
updated_at: parseTime(new Date())
|
||||
})
|
||||
|
||||
useDialogueStore().updateDialogueRecord({
|
||||
msg_id: this.msg_id,
|
||||
revokeInfo: {
|
||||
retracted_id: this.resource.retracted_id,
|
||||
retracted_name: this.resource.retracted_name,
|
||||
withdraw_id: this.resource.withdraw_id,
|
||||
withdraw_name: this.resource.withdraw_name,
|
||||
},
|
||||
is_revoke: 1,
|
||||
is_revoke: 1
|
||||
})
|
||||
updateDialogueRecord({
|
||||
msg_id: this.msg_id,
|
||||
revokeInfo: {
|
||||
retracted_id: this.resource.retracted_id,
|
||||
retracted_name: this.resource.retracted_name,
|
||||
withdraw_id: this.resource.withdraw_id,
|
||||
withdraw_name: this.resource.withdraw_name,
|
||||
},
|
||||
is_revoke: 1,
|
||||
is_revoke: 1
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -5,17 +5,8 @@ import { parseTime } from '@/utils/datetime'
|
||||
import * as message from '@/constant/message'
|
||||
import { formatTalkItem, palyMusic, formatTalkRecord } from '@/utils/talk'
|
||||
// import { isElectronMode } from '@/utils/common'
|
||||
import {
|
||||
ServeClearTalkUnreadNum,
|
||||
ServeCreateTalkList,
|
||||
} from '@/api/chat/index.js'
|
||||
import {
|
||||
useTalkStore,
|
||||
useDialogueStore,
|
||||
useDialogueListStore,
|
||||
useGroupStore,
|
||||
} from '@/store'
|
||||
import { handleFindWebview } from '@/utils/common'
|
||||
import { ServeClearTalkUnreadNum, ServeCreateTalkList } from '@/api/chat/index.js'
|
||||
import { useTalkStore, useDialogueStore,useDialogueListStore } from '@/store'
|
||||
|
||||
/**
|
||||
* 好友状态事件
|
||||
@ -41,11 +32,6 @@ class Talk extends Base {
|
||||
*/
|
||||
talk_type = 0
|
||||
|
||||
/**
|
||||
* 文件上传唯一随机值
|
||||
*/
|
||||
fileNum = ''
|
||||
|
||||
/**
|
||||
* 初始化构造方法
|
||||
*
|
||||
@ -57,10 +43,6 @@ class Talk extends Base {
|
||||
this.sender_id = resource.sender_id
|
||||
this.receiver_id = resource.receiver_id
|
||||
this.talk_type = resource.talk_type
|
||||
// this.fileNum = resource.file_num
|
||||
if (resource.file_num) {
|
||||
resource.data.file_num = resource.file_num
|
||||
}
|
||||
this.resource = resource.data
|
||||
|
||||
this.handle()
|
||||
@ -94,11 +76,7 @@ class Talk extends Base {
|
||||
*/
|
||||
getTalkText() {
|
||||
let text = ''
|
||||
if (
|
||||
this.resource.msg_type != message.ChatMsgTypeText &&
|
||||
//智能助手发送的系统消息,也直接显示内容
|
||||
this.resource.user_id !== 2
|
||||
) {
|
||||
if (this.resource.msg_type != message.ChatMsgTypeText) {
|
||||
text = message.ChatMsgTypeMapping[this.resource.msg_type]
|
||||
} else {
|
||||
text = this.resource.extra.content.replace(/<img .*?>/g, '')
|
||||
@ -111,10 +89,11 @@ class Talk extends Base {
|
||||
play() {
|
||||
// 客户端有消息提示
|
||||
// if (isElectronMode()) return
|
||||
|
||||
// useSettingsStore().isPromptTone && palyMusic()
|
||||
}
|
||||
|
||||
async handle() {
|
||||
handle() {
|
||||
// 不是自己发送的消息则需要播放提示音
|
||||
if (!this.isCurrSender()) {
|
||||
this.play()
|
||||
@ -122,21 +101,7 @@ class Talk extends Base {
|
||||
|
||||
// 判断会话列表是否存在,不存在则创建
|
||||
if (useTalkStore().findTalkIndex(this.getIndexName()) == -1) {
|
||||
if (this.resource.msg_type == 1102) {
|
||||
//被邀请进入群聊时,需要热更新会话列表
|
||||
await useTalkStore().loadTalkList()
|
||||
} else if (this.resource.msg_type == 1106) {
|
||||
//群解散时,需要热更新会话列表
|
||||
await useTalkStore().loadTalkList()
|
||||
} else if (
|
||||
this.resource.msg_type == 1104 ||
|
||||
this.resource.msg_type == 1115
|
||||
) {
|
||||
//群成员被移出时,需要热更新会话列表
|
||||
await useTalkStore().loadTalkList()
|
||||
} else {
|
||||
return this.addTalkItem()
|
||||
}
|
||||
return this.addTalkItem()
|
||||
}
|
||||
|
||||
// 判断当前是否正在和好友对话
|
||||
@ -144,24 +109,9 @@ class Talk extends Base {
|
||||
this.insertTalkRecord()
|
||||
} else {
|
||||
this.updateTalkItem()
|
||||
if (
|
||||
!useTalkStore().items[useTalkStore().findTalkIndex(this.getIndexName())]
|
||||
?.is_disturb &&
|
||||
!(
|
||||
useTalkStore().findTalkIndex(this.getIndexName()) == -1 &&
|
||||
(this.resource.msg_type == 1104 || this.resource.msg_type == 1115)
|
||||
)
|
||||
) {
|
||||
this.updateUnreadMsgNumAdd()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//更新未读数量+1
|
||||
updateUnreadMsgNumAdd() {
|
||||
handleFindWebview(`updateUnreadMsgNumAdd()`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示消息提示
|
||||
* @returns
|
||||
@ -173,6 +123,7 @@ class Talk extends Base {
|
||||
// lang: 'zh-CN',
|
||||
// body: '您有新的消息请注意查收'
|
||||
// })
|
||||
|
||||
// notification.onclick = () => {
|
||||
// notification.close()
|
||||
// }
|
||||
@ -200,16 +151,12 @@ class Talk extends Base {
|
||||
|
||||
ServeCreateTalkList({
|
||||
talk_type,
|
||||
receiver_id,
|
||||
}).then(async ({ code, data }) => {
|
||||
receiver_id
|
||||
}).then(({ code, data }) => {
|
||||
if (code == 200) {
|
||||
let item = formatTalkItem(data)
|
||||
if (!item?.is_disturb) {
|
||||
item.unread_num = 1
|
||||
this.updateUnreadMsgNumAdd()
|
||||
}
|
||||
item.unread_num = 1
|
||||
useTalkStore().addItem(item)
|
||||
await useTalkStore().loadTalkList()
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -219,76 +166,24 @@ class Talk extends Base {
|
||||
*/
|
||||
insertTalkRecord() {
|
||||
let record = this.resource
|
||||
let newRecord = formatTalkRecord(this.getAccountId(), this.resource)
|
||||
const { addDialogueRecord, addChatRecord } = useDialogueListStore()
|
||||
let newRecord = formatTalkRecord(this.getAccountId(), this.resource);
|
||||
const {addDialogueRecord,addChatRecord} = useDialogueListStore()
|
||||
// 群成员变化的消息,需要更新群成员列表
|
||||
if ([1102, 1103, 1104, 1115].includes(record.msg_type)) {
|
||||
if ([1102, 1103, 1104].includes(record.msg_type)) {
|
||||
useDialogueStore().updateGroupMembers()
|
||||
}
|
||||
|
||||
//群解散时,需要更新群成员权限
|
||||
if ([1106].includes(record.msg_type)) {
|
||||
useDialogueStore().updateDismiss()
|
||||
}
|
||||
|
||||
//群成员被移出时,需要更新群成员权限
|
||||
if ([1104, 1115].includes(record.msg_type)) {
|
||||
console.error(this.resource.extra.members, 'this.resource.extra.members')
|
||||
if (this.resource?.extra?.members?.length > 0) {
|
||||
const isMeQuit = this.resource.extra.members.find(
|
||||
(item) => item.user_id === this.getAccountId(),
|
||||
)
|
||||
if (isMeQuit) {
|
||||
useDialogueStore().updateQuit()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//群禁言变化时,需要更新群禁言状态——即更新群成员列表
|
||||
if ([1107, 1108, 1109, 1110].includes(record.msg_type)) {
|
||||
useDialogueStore().updateGroupMembers()
|
||||
}
|
||||
//群公告变化时,需要更新群公告(新增和修改有热更新,删除没有)
|
||||
if ([13].includes(record.msg_type)) {
|
||||
useGroupStore().ServeGetGroupNotices()
|
||||
}
|
||||
//群管理员变化时,需要更新群管理员列表——即更新群成员列表,同时更新群信息
|
||||
if ([1114].includes(record.msg_type)) {
|
||||
useDialogueStore().updateGroupMembers()
|
||||
useGroupStore().ServeGroupDetail()
|
||||
}
|
||||
if ([1116].includes(record.msg_type)) {
|
||||
// 更新会话信息
|
||||
useDialogueStore().setDialogue({
|
||||
name: record.extra.group_name,
|
||||
talk_type: record.talk_type,
|
||||
receiver_id: record.receiver_id,
|
||||
})
|
||||
// 更新群聊信息
|
||||
useGroupStore().updateGroupInfo({
|
||||
group_name: record.extra.group_name,
|
||||
avatar: record.extra.group_avatar,
|
||||
})
|
||||
// 更新会话列表中的会话信息
|
||||
const dialogue = useDialogueListStore().getDialogueList(
|
||||
`${record.talk_type}_${record.receiver_id}`,
|
||||
)
|
||||
if (dialogue) {
|
||||
dialogue.talk.username = record.extra.group_name
|
||||
}
|
||||
}
|
||||
addDialogueRecord([newRecord], 'add')
|
||||
addChatRecord(this.getIndexName(), newRecord)
|
||||
addDialogueRecord([newRecord],'add')
|
||||
addChatRecord(this.getIndexName(),newRecord)
|
||||
useDialogueStore().addDialogueRecord(newRecord)
|
||||
|
||||
if (!this.isCurrSender()) {
|
||||
// 推送已读消息
|
||||
// setTimeout(() => {
|
||||
// ws.emit('im.message.read', {
|
||||
// receiver_id: this.sender_id,
|
||||
// msg_ids: [this.resource.msg_id],
|
||||
// })
|
||||
// }, 1000)
|
||||
setTimeout(() => {
|
||||
ws.emit('im.message.read', {
|
||||
receiver_id: this.sender_id,
|
||||
msg_ids: [this.resource.msg_id]
|
||||
})
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
// 获取聊天面板元素节点
|
||||
@ -296,8 +191,7 @@ class Talk extends Base {
|
||||
if (!el) return
|
||||
|
||||
// 判断的滚动条是否在底部
|
||||
const isBottom =
|
||||
Math.ceil(el.scrollTop) + el.clientHeight >= el.scrollHeight
|
||||
const isBottom = Math.ceil(el.scrollTop) + el.clientHeight >= el.scrollHeight
|
||||
|
||||
if (isBottom || record.user_id == this.getAccountId()) {
|
||||
nextTick(() => {
|
||||
@ -310,15 +204,14 @@ class Talk extends Base {
|
||||
useTalkStore().updateItem({
|
||||
index_name: this.getIndexName(),
|
||||
msg_text: this.getTalkText(),
|
||||
updated_at: parseTime(new Date()),
|
||||
updated_at: parseTime(new Date())
|
||||
})
|
||||
|
||||
if (this.talk_type == 1 && this.getAccountId() !== this.sender_id) {
|
||||
//不在此处维护未读消息数量
|
||||
// ServeClearTalkUnreadNum({
|
||||
// talk_type: 1,
|
||||
// receiver_id: this.sender_id,
|
||||
// })
|
||||
ServeClearTalkUnreadNum({
|
||||
talk_type: 1,
|
||||
receiver_id: this.sender_id
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -329,31 +222,8 @@ class Talk extends Base {
|
||||
useTalkStore().updateMessage({
|
||||
index_name: this.getIndexName(),
|
||||
msg_text: this.getTalkText(),
|
||||
updated_at: parseTime(new Date()),
|
||||
updated_at: parseTime(new Date())
|
||||
})
|
||||
//收到新消息时,同时判断是否有人@我
|
||||
if (
|
||||
this.resource.msg_type === 1 &&
|
||||
this.resource?.extra?.mentions?.length > 0
|
||||
) {
|
||||
const findMention = this.resource?.extra?.mentions?.find(
|
||||
(mention) => mention === this.getAccountId(),
|
||||
)
|
||||
//有人@我或者@所有人,则更新会话列表
|
||||
if (findMention || this.resource?.extra?.mentions?.includes(0)) {
|
||||
useTalkStore().loadTalkList()
|
||||
}
|
||||
}
|
||||
if (this.resource.msg_type == 1116) {
|
||||
// 更新会话列表中的会话信息
|
||||
const dialogue = useDialogueListStore().getDialogueList(
|
||||
`${this.resource.talk_type}_${this.resource.receiver_id}`,
|
||||
)
|
||||
if (dialogue) {
|
||||
dialogue.talk.username = this.resource.extra.group_name
|
||||
}
|
||||
useTalkStore().loadTalkList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,12 +11,7 @@ import { reactive, nextTick, computed, h, inject } from 'vue'
|
||||
// EditTwo,
|
||||
// IdCard
|
||||
// } from '@icon-park/vue-next'
|
||||
import {
|
||||
ServeTopTalkList,
|
||||
ServeDeleteTalkList,
|
||||
ServeSetNotDisturb,
|
||||
ServeClearTalkUnreadNum,
|
||||
} from '@/api/chat'
|
||||
import { ServeTopTalkList, ServeDeleteTalkList, ServeSetNotDisturb } from '@/api/chat'
|
||||
import { useDialogueStore, useTalkStore, useDialogueListStore } from '@/store'
|
||||
import { ServeSecedeGroup } from '@/api/group'
|
||||
// import { ServeDeleteContact, ServeEditContactRemark } from '@/api/contact'
|
||||
@ -28,7 +23,7 @@ export function useSessionMenu() {
|
||||
show: false,
|
||||
x: 0,
|
||||
y: 0,
|
||||
item: {},
|
||||
item: {}
|
||||
})
|
||||
|
||||
const dialogueStore = useDialogueStore()
|
||||
@ -123,22 +118,10 @@ export function useSessionMenu() {
|
||||
// 移除会话
|
||||
const onRemoveTalk = (item) => {
|
||||
ServeDeleteTalkList({
|
||||
list_id: item.id,
|
||||
list_id: item.id
|
||||
}).then(({ code }) => {
|
||||
if (code == 200) {
|
||||
onDeleteTalk(item.index_name)
|
||||
console.error(item, 'item')
|
||||
if (item.unread_num > 0) {
|
||||
//同时已读
|
||||
ServeClearTalkUnreadNum(
|
||||
{
|
||||
talk_type: item.talk_type,
|
||||
receiver_id: item.receiver_id,
|
||||
},
|
||||
item.unread_num,
|
||||
).then(() => {
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -148,13 +131,13 @@ export function useSessionMenu() {
|
||||
ServeSetNotDisturb({
|
||||
talk_type: item.talk_type,
|
||||
receiver_id: item.receiver_id,
|
||||
is_disturb: item.is_disturb == 0 ? 1 : 0,
|
||||
is_disturb: item.is_disturb == 0 ? 1 : 0
|
||||
}).then(({ code, message }) => {
|
||||
if (code == 200) {
|
||||
message.success('设置成功!')
|
||||
talkStore.updateItem({
|
||||
index_name: item.index_name,
|
||||
is_disturb: item.is_disturb == 0 ? 1 : 0,
|
||||
is_disturb: item.is_disturb == 0 ? 1 : 0
|
||||
})
|
||||
} else {
|
||||
message.error(message)
|
||||
@ -170,12 +153,12 @@ export function useSessionMenu() {
|
||||
|
||||
ServeTopTalkList({
|
||||
list_id: item.id,
|
||||
type: item.is_top == 0 ? 1 : 2,
|
||||
type: item.is_top == 0 ? 1 : 2
|
||||
}).then(({ code, message }) => {
|
||||
if (code == 200) {
|
||||
talkStore.updateItem({
|
||||
index_name: item.index_name,
|
||||
is_top: item.is_top == 0 ? 1 : 0,
|
||||
is_top: item.is_top == 0 ? 1 : 0
|
||||
})
|
||||
} else {
|
||||
message.error(message)
|
||||
@ -218,7 +201,7 @@ export function useSessionMenu() {
|
||||
negativeText: '取消',
|
||||
onPositiveClick: () => {
|
||||
ServeSecedeGroup({
|
||||
group_id: item.receiver_id,
|
||||
group_id: item.receiver_id
|
||||
}).then(({ code, message }) => {
|
||||
if (code == 200) {
|
||||
message.success('已退出群聊')
|
||||
@ -227,7 +210,7 @@ export function useSessionMenu() {
|
||||
message.error(message)
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -276,18 +259,12 @@ export function useSessionMenu() {
|
||||
disturb: onSetDisturb,
|
||||
signout_group: onSignOutGroup,
|
||||
delete_contact: onDeleteContact,
|
||||
remark: onChangeRemark,
|
||||
remark: onChangeRemark
|
||||
}
|
||||
|
||||
dropdown.show = false
|
||||
evnets[key] && evnets[key](dropdown.item)
|
||||
}
|
||||
|
||||
return {
|
||||
dropdown,
|
||||
onCloseContextMenu,
|
||||
onContextMenuTalkHandle,
|
||||
onToTopTalk,
|
||||
onRemoveTalk,
|
||||
}
|
||||
return { dropdown, onCloseContextMenu, onContextMenuTalkHandle, onToTopTalk, onRemoveTalk }
|
||||
}
|
||||
|
103
src/main.js
@ -10,24 +10,13 @@ import tmui from '@/uni_modules/tmui'
|
||||
import { config } from '@/config/tmui/index.js'
|
||||
import 'dayjs/locale/zh-cn'
|
||||
import xLoaderror from '@/components/x-loaderror/index.vue'
|
||||
import asyncLoading from '@/components/async-loading/index.vue'
|
||||
import asyncError from '@/components/async-error/index.vue'
|
||||
import { vLoading } from '@/components/x-loading/index.js'
|
||||
import messagePopup from '@/components/x-message/useMessagePopup'
|
||||
import pageAnimation from '@/components/page-animation/index.vue'
|
||||
import * as plugins from './plugins'
|
||||
import {
|
||||
useDialogueStore,
|
||||
useTalkStore,
|
||||
useUserStore,
|
||||
useDialogueListStore,
|
||||
} from '@/store'
|
||||
import { uniStorage } from '@/utils/uniStorage.js'
|
||||
import { handleFindWebview } from '@/utils/common'
|
||||
|
||||
const { showMessage } = messagePopup()
|
||||
dayjs.locale('zh-cn')
|
||||
if (import.meta.env.VITE_SHOW_CONSOLE === 'true') {
|
||||
if (import.meta.env.VITE_SHOW_CONSOLE) {
|
||||
new VConsole()
|
||||
}
|
||||
export function createApp() {
|
||||
@ -39,8 +28,6 @@ export function createApp() {
|
||||
app.mixin(pageAnimation)
|
||||
app.component('customNavbar', customNavbar)
|
||||
app.component('x-loaderror', xLoaderror)
|
||||
app.component('AsyncLoading', asyncLoading)
|
||||
app.component('AsyncError', asyncError)
|
||||
app.directive('no-space', {
|
||||
mounted(el) {
|
||||
el.addEventListener('input', (e) => {
|
||||
@ -53,94 +40,6 @@ export function createApp() {
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
//获取当前聊天页面所在页面,并通过当前的receiver_id判断是否要创建本地通知栏消息
|
||||
window.getCurrentChatRoute = (msg) => {
|
||||
let pushMsg = JSON.parse(decodeURIComponent(msg))
|
||||
//当前所在聊天会话的receiver_id
|
||||
const receiver_id = pushMsg?.payload?.receiver_id
|
||||
// 获取当前页面路径
|
||||
const pages = getCurrentPages()
|
||||
const page = pages[pages.length - 1]
|
||||
console.log(page.route)
|
||||
|
||||
const dialogueStore = useDialogueStore()
|
||||
console.log(dialogueStore?.talk?.receiver_id)
|
||||
if (
|
||||
page?.route === 'pages/dialog/index' &&
|
||||
receiver_id === dialogueStore?.talk?.receiver_id
|
||||
) {
|
||||
return
|
||||
}
|
||||
console.log('===准备创建本地通知栏消息')
|
||||
handleFindWebview(`doCreatePushMessage('${msg}')`)
|
||||
}
|
||||
|
||||
//处理聊天推送弹窗点开
|
||||
window.openUniPushMsg = (msg) => {
|
||||
console.log('=====点击通知栏消息')
|
||||
let pushMsg = JSON.parse(decodeURIComponent(msg))
|
||||
console.log('=====pushMsg', pushMsg)
|
||||
//由于弹窗前处理了不该弹窗的场景,因此这里弹窗可以一并处理
|
||||
//也就是都跳转到聊天页面
|
||||
const talkStore = useTalkStore()
|
||||
talkStore.toTalk(pushMsg?.payload?.talk_type, pushMsg?.payload?.receiver_id)
|
||||
}
|
||||
|
||||
//处理当用户信息发生变化时,更新用户信息
|
||||
window.updateUserInfo = () => {
|
||||
useUserStore().loadSetting()
|
||||
}
|
||||
// 通讯录跳转
|
||||
window.handleContacts = () => {
|
||||
// 旧版本-按组织架构树的通讯录
|
||||
uni.navigateTo({
|
||||
url: '/pages/chooseByDeps/index?chooseMode=3&type=true'
|
||||
});
|
||||
|
||||
// 新版本-按公司别、好友、群组的通讯录
|
||||
// uni.navigateTo({
|
||||
// url: '/pages/addressBook/index?type=true',
|
||||
// })
|
||||
}
|
||||
|
||||
//处理OA、墨册强制刷新时,聊天同步强制刷新
|
||||
window.doLocationRefresh = () => {
|
||||
uniStorage.removeItem('dialogueList')
|
||||
uniStorage.removeItem('dialogue')
|
||||
useUserStore().loadSetting()
|
||||
useDialogueListStore().dialogueList.value = []
|
||||
// location.reload(true)
|
||||
}
|
||||
|
||||
//检查聊天页面是否可用
|
||||
window.checkChatWebviewAvailable = () => {
|
||||
handleFindWebview(`doneCheckChatWebviewAvailable()`)
|
||||
}
|
||||
|
||||
//获取从base传来的多选视频列表
|
||||
window.getBaseMulVideo = (videoList) => {
|
||||
const videos = JSON.parse(decodeURIComponent(videoList))
|
||||
console.error('=====videos', videos)
|
||||
if (videos.length > 0) {
|
||||
const videoUri = videos[0]
|
||||
console.error('=====videoUri', videoUri)
|
||||
}
|
||||
}
|
||||
|
||||
//检查是否是特殊测试用户,开启控制台
|
||||
const checkTestUser = () => {
|
||||
const userStore = useUserStore()
|
||||
if (
|
||||
import.meta.env.VITE_SHOW_CONSOLE === 'false' &&
|
||||
userStore.mobile == '18100591363'
|
||||
) {
|
||||
new VConsole()
|
||||
}
|
||||
}
|
||||
|
||||
checkTestUser()
|
||||
|
||||
window.message = ['success', 'error', 'warning'].reduce((acc, type) => {
|
||||
acc[type] = (message) => {
|
||||
if (typeof message === 'string') {
|
||||
|
@ -5,7 +5,8 @@
|
||||
"^tm-(.*)": "@/tmui/components/tm-$1/tm-$1.vue"
|
||||
}
|
||||
},
|
||||
"pages": [{
|
||||
"pages": [
|
||||
{
|
||||
"path": "pages/index/index",
|
||||
"type": "page",
|
||||
"style": {
|
||||
@ -177,38 +178,6 @@
|
||||
"navigationStyle": "custom",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/chatSettings/groupManage/manageGroupDeps",
|
||||
"type": "page",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/complaintReport/index",
|
||||
"type": "page",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/addressBook/index",
|
||||
"type": "page",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/addressBook/addFriend/index",
|
||||
"type": "page",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
|
@ -1,398 +0,0 @@
|
||||
<template>
|
||||
<div class="add-friend-page">
|
||||
<zPaging ref="zPaging" :show-scrollbar="false" @scrolltolower="doLoadMore">
|
||||
<template #top>
|
||||
<div :class="'top_bg'">
|
||||
<customNavbar
|
||||
:class="'index_top_navbar'"
|
||||
:title="$t('addFriend.pageTitle')"
|
||||
></customNavbar>
|
||||
<div class="pl-[32rpx] pr-[32rpx] pt-[32rpx] pb-[32rpx]">
|
||||
<customInput
|
||||
:searchText="searchVal"
|
||||
@inputSearchText="inputSearchText"
|
||||
></customInput>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div class="add-friend">
|
||||
<div class="add-friend-list" v-if="state.friendsList.length > 0">
|
||||
<div
|
||||
class="members-list-each"
|
||||
v-for="(item, index) in state.friendsList"
|
||||
:key="index"
|
||||
@click="toUserDetail(item)"
|
||||
>
|
||||
<div class="members-info">
|
||||
<avatarModule
|
||||
:mode="1"
|
||||
:avatar="item.avatar"
|
||||
:groupType="0"
|
||||
:userName="item.nickname"
|
||||
:customStyle="{ width: '72rpx', height: '72rpx' }"
|
||||
:customTextStyle="{
|
||||
fontSize: '32rpx',
|
||||
fontWeight: 'bold',
|
||||
color: '#fff',
|
||||
lineHeight: '44rpx',
|
||||
}"
|
||||
></avatarModule>
|
||||
<div class="members-info-area">
|
||||
<div
|
||||
class="members-info-basic"
|
||||
:style="{ padding: item.company_name ? 0 : '0 0 24rpx' }"
|
||||
>
|
||||
<span class="members-name">
|
||||
{{ item.nickname }}
|
||||
</span>
|
||||
<span class="members-jobNum">
|
||||
{{ item.job_num }}
|
||||
</span>
|
||||
<!-- <span class="members-company">{{ item.company_name }}</span> -->
|
||||
</div>
|
||||
<div class="members-positions-area">
|
||||
<tm-popover position="bc">
|
||||
<tm-scrolly :refresher="false" :height="84">
|
||||
<div class="members-positions">
|
||||
<div
|
||||
class="members-positions-each"
|
||||
v-for="(postionItem,
|
||||
positionIndex) in item.user_position"
|
||||
:key="positionIndex"
|
||||
>
|
||||
{{ postionItem.position_name }}
|
||||
</div>
|
||||
</div>
|
||||
</tm-scrolly>
|
||||
<template v-slot:label>
|
||||
<tm-scrolly
|
||||
:refresher="false"
|
||||
:height="
|
||||
item.user_position.length >= 4
|
||||
? 180
|
||||
: item.user_position.length === 3
|
||||
? 140
|
||||
: item.user_position.length === 2
|
||||
? 100
|
||||
: item.user_position.length === 1
|
||||
? 60
|
||||
: 0
|
||||
"
|
||||
>
|
||||
<div class="members-positions-popover-box">
|
||||
<div
|
||||
class="members-positions-popover"
|
||||
v-for="(postionItem,
|
||||
positionIndex) in item.user_position"
|
||||
:key="positionIndex"
|
||||
>
|
||||
{{ postionItem.position_name }}
|
||||
</div>
|
||||
</div>
|
||||
</tm-scrolly>
|
||||
</template>
|
||||
</tm-popover>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="company-infos" v-if="item.company_name">
|
||||
<div class="company-each">
|
||||
<span>{{ item.company_name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="addressBook-noData" v-if="state.friendsList.length === 0">
|
||||
<img src="@/static/image/search/search-no-data.png" />
|
||||
<span>
|
||||
{{
|
||||
searchVal
|
||||
? $t('addFriend.message.notFindData')
|
||||
: $t('search.hint')
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</zPaging>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import customInput from '@/components/custom-input/custom-input.vue'
|
||||
import zPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
|
||||
import avatarModule from '@/components/avatar-module/index.vue'
|
||||
import { ServeFriendSearch } from '@/api/addressBook/index'
|
||||
import { handleSetWebviewStyle } from '@/utils/common'
|
||||
|
||||
import { ref, onMounted, reactive } from 'vue'
|
||||
|
||||
import { useI18n } from 'vue-i18n'
|
||||
const { t } = useI18n()
|
||||
|
||||
const searchVal = ref('')
|
||||
|
||||
const state = reactive({
|
||||
friendsListPage: 1, //当前查询的可添加好友列表分页
|
||||
friendsListPageSize: 10, //可添加好友列表单页数量
|
||||
friendsList: [], //可添加好友列表
|
||||
hasMoreFriends: true, //是否还有更多可添加好友数据
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
getFriendsList()
|
||||
handleSetWebviewStyle()
|
||||
})
|
||||
|
||||
//输入搜索内容
|
||||
const inputSearchText = (e) => {
|
||||
searchVal.value = e
|
||||
}
|
||||
|
||||
//搜索可添加好友(精确搜索)
|
||||
const getFriendsList = () => {
|
||||
let params = {
|
||||
name: searchVal.value,
|
||||
}
|
||||
ServeFriendSearch(params)
|
||||
.then((res) => {
|
||||
console.error(res)
|
||||
if (res?.code === 200) {
|
||||
if (state.friendsListPage === 1) {
|
||||
state.friendsList = res.data?.user_list || []
|
||||
} else {
|
||||
state.friendsList = state.friendsList.concat(
|
||||
res.data?.user_list || [],
|
||||
)
|
||||
}
|
||||
if (state.friendsList.length < res.data?.count) {
|
||||
state.hasMoreFriends = true
|
||||
} else {
|
||||
state.hasMoreFriends = false
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
|
||||
//点击进入用户详情页面
|
||||
const toUserDetail = (userInfo) => {
|
||||
uni.navigateTo({
|
||||
url:
|
||||
'/pages/dialog/dialogDetail/userDetail??erpUserId=' +
|
||||
userInfo.erp_user_id,
|
||||
})
|
||||
}
|
||||
|
||||
//加载更多数据
|
||||
const doLoadMore = (e) => {
|
||||
state.friendsListPage += 1
|
||||
getFriendsList()
|
||||
}
|
||||
|
||||
watch(
|
||||
() => searchVal.value,
|
||||
(newVal) => {
|
||||
state.friendsListPage = 1
|
||||
getFriendsList()
|
||||
},
|
||||
)
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
::v-deep .zp-paging-container-content {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
::v-deep .index_top_navbar .tmicon-angle-left {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
::v-deep .index_top_navbar .text-weight-b {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
::v-deep .index_top_navbar .statusHeightTop > .noNvueBorder:first-child {
|
||||
background: transparent !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.top_bg {
|
||||
background: url('@/static/image/mine/page_top.png') no-repeat;
|
||||
background-size: cover;
|
||||
background-position: bottom center;
|
||||
}
|
||||
|
||||
:deep(.animateAll_tabs_tmui) {
|
||||
width: 120rpx !important;
|
||||
height: 10rpx !important;
|
||||
}
|
||||
|
||||
.add-friend-page {
|
||||
.add-friend {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-image: url('@/static/image/clockIn/z3280@3x.png');
|
||||
background-size: cover;
|
||||
background-position: bottom center;
|
||||
background-attachment: fixed;
|
||||
width: 100%;
|
||||
|
||||
.add-friend-list {
|
||||
margin: 30rpx 24rpx;
|
||||
overflow: hidden;
|
||||
flex: 1;
|
||||
gap: 30rpx 0;
|
||||
|
||||
.members-list-each {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
background-color: #fff;
|
||||
width: 100%;
|
||||
border-radius: 8rpx;
|
||||
|
||||
.swipe-action {
|
||||
width: 100%;
|
||||
overflow: visible !important;
|
||||
|
||||
:deep(.wd-swipe-action__right) {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #cf3050;
|
||||
padding: 0 48rpx;
|
||||
border-radius: 0 8rpx 8rpx 0;
|
||||
span {
|
||||
color: #fff;
|
||||
line-height: 1;
|
||||
font-size: 30rpx;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.members-info {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
gap: 16rpx;
|
||||
padding: 24rpx 24rpx 0;
|
||||
|
||||
.members-info-area {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
gap: 16rpx;
|
||||
|
||||
.members-info-basic {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
|
||||
.members-name {
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
width: 150rpx;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.members-jobNum {
|
||||
font-size: 26rpx;
|
||||
font-weight: 400;
|
||||
color: #999;
|
||||
word-break: break-all;
|
||||
margin: 10rpx 0 0;
|
||||
}
|
||||
|
||||
.members-company {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
.members-positions-area {
|
||||
.members-positions {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
flex-wrap: wrap;
|
||||
gap: 10rpx;
|
||||
padding: 4rpx 0 0;
|
||||
max-height: 84rpx;
|
||||
overflow: hidden;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
line-clamp: 2;
|
||||
|
||||
.members-positions-each {
|
||||
background-color: #eee9f8;
|
||||
line-height: 1;
|
||||
font-size: 24rpx;
|
||||
padding: 4rpx 16rpx;
|
||||
color: #46299d;
|
||||
}
|
||||
}
|
||||
.members-positions-popover-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10rpx 0;
|
||||
|
||||
.members-positions-popover {
|
||||
background-color: #eee9f8;
|
||||
line-height: 1;
|
||||
font-size: 24rpx;
|
||||
padding: 8rpx 16rpx;
|
||||
color: #46299d;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.company-infos {
|
||||
margin: 0 0 0 88rpx;
|
||||
padding: 0 0 24rpx 24rpx;
|
||||
.company-each {
|
||||
span {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.addressBook-noData {
|
||||
margin: 30rpx 24rpx;
|
||||
overflow: hidden;
|
||||
flex: 1;
|
||||
gap: 30rpx 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
img {
|
||||
width: 500rpx;
|
||||
}
|
||||
span {
|
||||
font-size: 30rpx;
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,944 +0,0 @@
|
||||
<template>
|
||||
<div class="address-book-page">
|
||||
<zPaging ref="zPaging" :show-scrollbar="false" @scrolltolower="doLoadMore">
|
||||
<template #top>
|
||||
<div :class="'top_bg'">
|
||||
<customNavbar
|
||||
:class="'index_top_navbar'"
|
||||
:title="$t('index.mine.addressBook')"
|
||||
:hideHome="navshow"
|
||||
:hideBack="navshow"
|
||||
>
|
||||
<template #left>
|
||||
<tm-icon
|
||||
@click="goWebHome"
|
||||
v-if="navshow"
|
||||
name="tmicon-angle-left"
|
||||
style="padding-left: 30rpx;"
|
||||
></tm-icon>
|
||||
</template>
|
||||
</customNavbar>
|
||||
<div class="pl-[32rpx] pr-[32rpx] pt-[32rpx] pb-[32rpx]">
|
||||
<customInput
|
||||
:searchText="searchVal"
|
||||
@inputSearchText="inputSearchText"
|
||||
></customInput>
|
||||
</div>
|
||||
</div>
|
||||
<tm-tabs
|
||||
:list="state.addressBookTabs"
|
||||
align="center"
|
||||
:width="750"
|
||||
:height="300"
|
||||
:itemWidth="250"
|
||||
default-name="company"
|
||||
activeColor="#46299d"
|
||||
activeFontColor="#46299d"
|
||||
tabs-line-ani-color="#46299d"
|
||||
:showTabsLineAni="true"
|
||||
:showTabsLine="false"
|
||||
:activeFontSize="32"
|
||||
:itemFontSize="30"
|
||||
@update:activeName="updateAddressBookTab"
|
||||
></tm-tabs>
|
||||
</template>
|
||||
<div class="address-book">
|
||||
<div class="address-book-tabs-panes-list">
|
||||
<div
|
||||
class="tabs-panes-each address-book-company"
|
||||
v-if="
|
||||
state.addressBookActiveTab === 'company' &&
|
||||
state.myContractList.length > 0
|
||||
"
|
||||
>
|
||||
<div class="address-book-company-name">
|
||||
<span>{{ state.myCompany }}</span>
|
||||
</div>
|
||||
<div
|
||||
class="members-list-each"
|
||||
v-for="(item, index) in state.myContractList"
|
||||
:key="index"
|
||||
>
|
||||
<div class="members-info" @click="toUserDetail(item)">
|
||||
<avatarModule
|
||||
:mode="1"
|
||||
:avatar="item.avatar"
|
||||
:groupType="0"
|
||||
:userName="item.nickname"
|
||||
:customStyle="{ width: '72rpx', height: '72rpx' }"
|
||||
:customTextStyle="{
|
||||
fontSize: '32rpx',
|
||||
fontWeight: 'bold',
|
||||
color: '#fff',
|
||||
lineHeight: '44rpx',
|
||||
}"
|
||||
></avatarModule>
|
||||
<div class="members-info-area">
|
||||
<div class="members-info-basic" style="padding: 0 0 24rpx;">
|
||||
<span class="members-name">
|
||||
{{ item.nickname }}
|
||||
</span>
|
||||
<span class="members-jobNum">
|
||||
{{ item.job_num }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="members-positions-area">
|
||||
<tm-popover position="bc">
|
||||
<tm-scrolly :refresher="false" :height="84">
|
||||
<div class="members-positions">
|
||||
<div
|
||||
class="members-positions-each"
|
||||
v-for="(postionItem,
|
||||
positionIndex) in item.user_position"
|
||||
:key="positionIndex"
|
||||
>
|
||||
{{ postionItem.position_name }}
|
||||
</div>
|
||||
</div>
|
||||
</tm-scrolly>
|
||||
<template v-slot:label>
|
||||
<tm-scrolly
|
||||
:refresher="false"
|
||||
:height="
|
||||
item.user_position.length >= 4
|
||||
? 180
|
||||
: item.user_position.length === 3
|
||||
? 140
|
||||
: item.user_position.length === 2
|
||||
? 100
|
||||
: item.user_position.length === 1
|
||||
? 60
|
||||
: 0
|
||||
"
|
||||
>
|
||||
<div class="members-positions-popover-box">
|
||||
<div
|
||||
class="members-positions-popover"
|
||||
v-for="(postionItem,
|
||||
positionIndex) in item.user_position"
|
||||
:key="positionIndex"
|
||||
>
|
||||
{{ postionItem.position_name }}
|
||||
</div>
|
||||
</div>
|
||||
</tm-scrolly>
|
||||
</template>
|
||||
</tm-popover>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="tabs-panes-each address-book-friends"
|
||||
v-if="
|
||||
state.addressBookActiveTab === 'friends' &&
|
||||
state.myFriendsList.length > 0
|
||||
"
|
||||
>
|
||||
<div
|
||||
class="members-list-each"
|
||||
v-for="(item, index) in state.myFriendsList"
|
||||
:key="index"
|
||||
>
|
||||
<wd-swipe-action
|
||||
class="swipe-action"
|
||||
@click="showDeleteModal(item, index)"
|
||||
v-if="
|
||||
!item.company_name ||
|
||||
(item.company_name && item.company_name !== state.myCompany)
|
||||
"
|
||||
>
|
||||
<div class="members-info" @click="toUserDetail(item)">
|
||||
<avatarModule
|
||||
:mode="1"
|
||||
:avatar="item.avatar"
|
||||
:groupType="0"
|
||||
:userName="item.nickname"
|
||||
:customStyle="{ width: '72rpx', height: '72rpx' }"
|
||||
:customTextStyle="{
|
||||
fontSize: '32rpx',
|
||||
fontWeight: 'bold',
|
||||
color: '#fff',
|
||||
lineHeight: '44rpx',
|
||||
}"
|
||||
></avatarModule>
|
||||
<div class="members-info-area">
|
||||
<div
|
||||
class="members-info-basic"
|
||||
:style="{ padding: item.company_name ? 0 : '0 0 24rpx' }"
|
||||
>
|
||||
<span class="members-name">
|
||||
{{ item.nickname }}
|
||||
</span>
|
||||
<span class="members-jobNum">
|
||||
{{ item.job_num }}
|
||||
</span>
|
||||
<!-- <span class="members-company">{{ item.company_name }}</span> -->
|
||||
</div>
|
||||
<div class="members-positions-area">
|
||||
<tm-popover position="bc">
|
||||
<tm-scrolly :refresher="false" :height="84">
|
||||
<div class="members-positions">
|
||||
<div
|
||||
class="members-positions-each"
|
||||
v-for="(postionItem,
|
||||
positionIndex) in item.user_position"
|
||||
:key="positionIndex"
|
||||
>
|
||||
{{ postionItem.position_name }}
|
||||
</div>
|
||||
</div>
|
||||
</tm-scrolly>
|
||||
<template v-slot:label>
|
||||
<tm-scrolly
|
||||
:refresher="false"
|
||||
:height="
|
||||
item.user_position.length >= 4
|
||||
? 180
|
||||
: item.user_position.length === 3
|
||||
? 140
|
||||
: item.user_position.length === 2
|
||||
? 100
|
||||
: item.user_position.length === 1
|
||||
? 60
|
||||
: 0
|
||||
"
|
||||
>
|
||||
<div class="members-positions-popover-box">
|
||||
<div
|
||||
class="members-positions-popover"
|
||||
v-for="(postionItem,
|
||||
positionIndex) in item.user_position"
|
||||
:key="positionIndex"
|
||||
>
|
||||
{{ postionItem.position_name }}
|
||||
</div>
|
||||
</div>
|
||||
</tm-scrolly>
|
||||
</template>
|
||||
</tm-popover>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="company-infos" v-if="item.company_name">
|
||||
<div class="company-each">
|
||||
<span>{{ item.company_name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<template #right>
|
||||
<div
|
||||
v-for="(swipeActionItem,
|
||||
swipeActionIndex) in state.swipeAction"
|
||||
:key="swipeActionIndex"
|
||||
>
|
||||
<span>{{ swipeActionItem.text }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</wd-swipe-action>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="tabs-panes-each address-book-groups"
|
||||
v-if="
|
||||
state.addressBookActiveTab === 'groups' &&
|
||||
state.myGroupsList.length > 0
|
||||
"
|
||||
>
|
||||
<div
|
||||
class="groups-list-each"
|
||||
v-for="(item, index) in state.myGroupsList"
|
||||
:key="index"
|
||||
>
|
||||
<div class="groups-info">
|
||||
<avatarModule
|
||||
:mode="2"
|
||||
:avatar="item?.avatar"
|
||||
:groupType="Number(item?.group_type)"
|
||||
:customStyle="{ width: '72rpx', height: '72rpx' }"
|
||||
></avatarModule>
|
||||
<span class="groups-name">
|
||||
{{ item?.group_name }}
|
||||
</span>
|
||||
<span
|
||||
class="groups-type"
|
||||
:style="{
|
||||
color:
|
||||
groupTypeMapping[item?.group_type]?.result_type_color,
|
||||
border:
|
||||
'1px solid' +
|
||||
groupTypeMapping[item?.group_type]?.result_type_color,
|
||||
}"
|
||||
>
|
||||
{{ groupTypeMapping[item?.group_type]?.result_type }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="groups-btns">
|
||||
<div class="groups-btns-each" @click="toGroupChat(item)">
|
||||
<span>{{ $t('addressBook.btns.enterGroup') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="addressBook-noData"
|
||||
v-if="
|
||||
!state.isLoadingData &&
|
||||
((state.addressBookActiveTab === 'company' &&
|
||||
state.myContractList.length === 0) ||
|
||||
(state.addressBookActiveTab === 'friends' &&
|
||||
state.myFriendsList.length === 0) ||
|
||||
(state.addressBookActiveTab === 'groups' &&
|
||||
state.myGroupsList.length === 0))
|
||||
"
|
||||
>
|
||||
<img src="@/static/image/search/search-no-data.png" />
|
||||
<span>
|
||||
{{
|
||||
searchVal
|
||||
? $t('addFriend.message.notFindData')
|
||||
: $t('search.hint')
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<tm-modal
|
||||
class="friendDeleteModal"
|
||||
ref="friendDeleteModalRef"
|
||||
:mask="true"
|
||||
:okText="$t('ok')"
|
||||
okColor="#46299d"
|
||||
cancelColor="#999"
|
||||
@ok="doDeleteFriend"
|
||||
@cancel="hideDeleteModal"
|
||||
:teleport="false"
|
||||
titleStyle="font-size: 40rpx;font-weight: 600;line-height: 1;"
|
||||
:height="300"
|
||||
:round="4"
|
||||
>
|
||||
<div class="friendDeleteModal-content">
|
||||
<span>{{ $t('addressBook.message.doOrNotDeleteFriend') }}</span>
|
||||
</div>
|
||||
</tm-modal>
|
||||
</zPaging>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import customInput from '@/components/custom-input/custom-input.vue'
|
||||
import zPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
|
||||
import avatarModule from '@/components/avatar-module/index.vue'
|
||||
|
||||
import { ref, onMounted, reactive, watch } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { handleSetWebviewStyle, handleFindWebview } from '@/utils/common'
|
||||
import { ServeUserGroupChatList, ServeCreateTalkList } from '@/api/chat/index'
|
||||
import { ServeGetSessionId } from '@/api/search/index'
|
||||
import { formatTalkItem } from '@/utils/talk'
|
||||
import { useDialogueStore, useTalkStore } from '@/store'
|
||||
import {
|
||||
ServeQueryFriendsList,
|
||||
ServeDeleteFriend,
|
||||
} from '@/api/addressBook/index'
|
||||
|
||||
import { useI18n } from 'vue-i18n'
|
||||
const { t } = useI18n()
|
||||
|
||||
const navshow = ref(false)
|
||||
const searchVal = ref('')
|
||||
const friendDeleteModalRef = ref(null)
|
||||
|
||||
const state = reactive({
|
||||
addressBookTabs: [], //tab页
|
||||
addressBookActiveTab: 'company', //当前选中的通讯录tab
|
||||
myContractListPage: 1, //当前查询的我的组织架构通讯录列表分页
|
||||
myContractListPageSize: 10, //我的组织架构通讯录列表单页数量
|
||||
myContractList: [], //我的组织架构通讯录列表
|
||||
hasMoreContracts: true, //是否还有更多我的组织架构通讯录数据
|
||||
myFriendsListPage: 1, //当前查询的我的好友列表分页
|
||||
myFriendsListPageSize: 10, //我的好友列表单页数量
|
||||
myFriendsList: [], //我的好友列表
|
||||
hasMoreFriends: true, //是否还有更多我的好友数据
|
||||
myGroupsListPage: 1, //当前查询的群组列表分页
|
||||
myGroupsListPageSize: 10, //群组列表单页数量
|
||||
myGroupsList: [], //我的群组列表
|
||||
hasMoreGroups: true, //是否还有更多我的群组数据
|
||||
myCompany: '', //当前登录人公司别
|
||||
swipeAction: [], //左滑操作栏按钮组
|
||||
isLoadingData: true, //是否正在加载数据
|
||||
})
|
||||
|
||||
onLoad((options) => {
|
||||
if (options.type) {
|
||||
navshow.value = true
|
||||
}
|
||||
})
|
||||
|
||||
const goWebHome = () => {
|
||||
uni.navigateBack()
|
||||
handleFindWebview(`handleBackHost()`)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
state.swipeAction = [
|
||||
{
|
||||
text: t('addressBook.btns.delete'), //操作按钮的文本,
|
||||
},
|
||||
]
|
||||
state.addressBookTabs = [
|
||||
{
|
||||
key: 'company',
|
||||
title: t('addressBook.tabs.company'),
|
||||
},
|
||||
{
|
||||
key: 'friends',
|
||||
title: t('addressBook.tabs.friends'),
|
||||
},
|
||||
{
|
||||
key: 'groups',
|
||||
title: t('addressBook.tabs.groups'),
|
||||
},
|
||||
]
|
||||
handleSetWebviewStyle()
|
||||
getMyContractList()
|
||||
})
|
||||
|
||||
//输入搜索内容
|
||||
const inputSearchText = (e) => {
|
||||
searchVal.value = e
|
||||
}
|
||||
|
||||
//切换通讯录tab
|
||||
const updateAddressBookTab = (e) => {
|
||||
state.addressBookActiveTab = e
|
||||
}
|
||||
|
||||
//获取“组织架构”通讯录列表
|
||||
const getMyContractList = () => {
|
||||
let params = {
|
||||
type: 'addressBook', //查我的通讯录的时候写死addressBook
|
||||
page: state.myContractListPage,
|
||||
page_size: state.myContractListPageSize,
|
||||
name: searchVal.value,
|
||||
}
|
||||
// console.log(params)
|
||||
state.isLoadingData = true
|
||||
ServeQueryFriendsList(params)
|
||||
.then((res) => {
|
||||
// console.log(res)
|
||||
state.isLoadingData = false
|
||||
if (res?.code === 200) {
|
||||
state.myCompany = res.data?.company_name
|
||||
if (state.myContractListPage === 1) {
|
||||
state.myContractList = res.data?.user_list || []
|
||||
} else {
|
||||
state.myContractList = state.myContractList.concat(
|
||||
res.data?.user_list || [],
|
||||
)
|
||||
}
|
||||
if (state.myContractList.length < res.data?.count) {
|
||||
state.hasMoreContracts = true
|
||||
} else {
|
||||
state.hasMoreContracts = false
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
state.isLoadingData = false
|
||||
})
|
||||
}
|
||||
|
||||
//获取“我的好友”列表
|
||||
const getMyFriendsList = () => {
|
||||
let params = {
|
||||
type: 'myFriends', //查我得好友的时候写死myFriends
|
||||
page: state.myFriendsListPage,
|
||||
page_size: state.myFriendsListPageSize,
|
||||
name: searchVal.value,
|
||||
}
|
||||
console.error(params)
|
||||
state.isLoadingData = true
|
||||
ServeQueryFriendsList(params)
|
||||
.then((res) => {
|
||||
console.log(res)
|
||||
state.isLoadingData = false
|
||||
if (res?.code === 200) {
|
||||
if (state.myFriendsListPage === 1) {
|
||||
state.myFriendsList = res.data?.user_list || []
|
||||
} else {
|
||||
state.myFriendsList = state.myFriendsList.concat(
|
||||
res.data?.user_list || [],
|
||||
)
|
||||
}
|
||||
if (state.myFriendsList.length < res.data?.count) {
|
||||
state.hasMoreFriends = true
|
||||
} else {
|
||||
state.hasMoreFriends = false
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
state.isLoadingData = false
|
||||
})
|
||||
}
|
||||
|
||||
//点击进入用户详情页面
|
||||
const toUserDetail = (userInfo) => {
|
||||
uni.navigateTo({
|
||||
url:
|
||||
'/pages/dialog/dialogDetail/userDetail??erpUserId=' +
|
||||
userInfo.erp_user_id,
|
||||
})
|
||||
}
|
||||
|
||||
//获取“我的群组”列表
|
||||
const getMyGroupsList = () => {
|
||||
let params = {
|
||||
page: state.myGroupsListPage,
|
||||
page_size: state.myGroupsListPageSize,
|
||||
group_name: searchVal.value,
|
||||
}
|
||||
state.isLoadingData = true
|
||||
ServeUserGroupChatList(params)
|
||||
.then((res) => {
|
||||
console.log(res)
|
||||
state.isLoadingData = false
|
||||
if (res?.code === 200) {
|
||||
if (state.myGroupsListPage === 1) {
|
||||
state.myGroupsList = res.data?.items || []
|
||||
} else {
|
||||
state.myGroupsList = state.myGroupsList.concat(res.data?.items || [])
|
||||
}
|
||||
if (state.myGroupsList.length < res.data?.total) {
|
||||
state.hasMoreGroups = true
|
||||
} else {
|
||||
state.hasMoreGroups = false
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
state.isLoadingData = false
|
||||
})
|
||||
}
|
||||
|
||||
//加载更多数据
|
||||
const doLoadMore = (e) => {
|
||||
if (state.addressBookActiveTab === 'company' && state.hasMoreContracts) {
|
||||
state.myContractListPage += 1
|
||||
getMyContractList()
|
||||
} else if (state.addressBookActiveTab === 'friends' && state.hasMoreFriends) {
|
||||
state.myFriendsListPage += 1
|
||||
getMyFriendsList()
|
||||
} else if (state.addressBookActiveTab === 'groups' && state.hasMoreGroups) {
|
||||
state.myGroupsListPage += 1
|
||||
getMyGroupsList()
|
||||
}
|
||||
}
|
||||
|
||||
//获取会话Id
|
||||
const getSessionId = (talk_type, receiver_id) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let params = {
|
||||
talkType: talk_type,
|
||||
receiverId: receiver_id,
|
||||
}
|
||||
const resp = ServeGetSessionId(params)
|
||||
console.log(resp)
|
||||
resp.then(({ code, data }) => {
|
||||
console.log(data)
|
||||
if (code == 200) {
|
||||
resolve(data?.sessionId)
|
||||
} else {
|
||||
}
|
||||
})
|
||||
resp.catch(() => {})
|
||||
})
|
||||
}
|
||||
|
||||
//点击跳转进入我的指定群聊
|
||||
const toGroupChat = async (groupInfo) => {
|
||||
let talk_type = 2
|
||||
let receiver_id = groupInfo.id
|
||||
const sessionId = await getSessionId(talk_type, receiver_id)
|
||||
if (useTalkStore().findTalkIndex(`${talk_type}_${receiver_id}`) === -1) {
|
||||
ServeCreateTalkList({
|
||||
talk_type,
|
||||
receiver_id,
|
||||
}).then(async ({ code, data }) => {
|
||||
if (code == 200) {
|
||||
let item = formatTalkItem(data)
|
||||
useTalkStore().addItem(item)
|
||||
}
|
||||
})
|
||||
}
|
||||
useDialogueStore().setDialogue({
|
||||
name: groupInfo.group_name,
|
||||
talk_type: 2,
|
||||
receiver_id: receiver_id,
|
||||
})
|
||||
uni.navigateTo({
|
||||
url: '/pages/dialog/index?sessionId=' + sessionId,
|
||||
})
|
||||
}
|
||||
|
||||
//显示删除好友确认框
|
||||
const showDeleteModal = (userInfo, friendIndex) => {
|
||||
friendDeleteModalRef.value.open({
|
||||
userInfo,
|
||||
friendIndex,
|
||||
})
|
||||
}
|
||||
|
||||
//关闭删除好友确认框
|
||||
const hideDeleteModal = () => {
|
||||
friendDeleteModalRef.value.close()
|
||||
}
|
||||
|
||||
//删除好友
|
||||
const doDeleteFriend = (args) => {
|
||||
console.log(args.userInfo, args.friendIndex)
|
||||
let params = {
|
||||
receiver_id: args.userInfo.id, //聊天的用户id
|
||||
talk_type: 1,
|
||||
}
|
||||
ServeDeleteFriend(params).then((res) => {
|
||||
console.log(res)
|
||||
if (res?.code === 200) {
|
||||
message.success(t('addressBook.message.deleteSuccess') + ' !')
|
||||
state.myFriendsList.splice(args.friendIndex, 1)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 映射表-根据groupType设置对应值
|
||||
const groupTypeMapping = {
|
||||
0: {},
|
||||
1: {},
|
||||
2: {
|
||||
result_type: t('index.mine.department'),
|
||||
result_type_color: '#377EC6',
|
||||
},
|
||||
3: {
|
||||
result_type: t('index.mine.project'),
|
||||
result_type_color: '#C1681C',
|
||||
},
|
||||
4: {
|
||||
result_type: t('index.type.company'),
|
||||
result_type_color: '#7A58DE',
|
||||
},
|
||||
}
|
||||
|
||||
watch(
|
||||
() => state.addressBookActiveTab,
|
||||
(newVal) => {
|
||||
if (newVal === 'company') {
|
||||
state.myContractListPage = 1
|
||||
getMyContractList()
|
||||
} else if (newVal === 'friends') {
|
||||
state.myFriendsListPage = 1
|
||||
getMyFriendsList()
|
||||
} else if (newVal === 'groups') {
|
||||
state.myGroupsListPage = 1
|
||||
getMyGroupsList()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
watch(
|
||||
() => searchVal.value,
|
||||
(newVal) => {
|
||||
if (state.addressBookActiveTab === 'company') {
|
||||
state.myContractListPage = 1
|
||||
getMyContractList()
|
||||
} else if (state.addressBookActiveTab === 'friends') {
|
||||
state.myFriendsListPage = 1
|
||||
getMyFriendsList()
|
||||
} else if (state.addressBookActiveTab === 'groups') {
|
||||
state.myGroupsListPage = 1
|
||||
getMyGroupsList()
|
||||
}
|
||||
},
|
||||
)
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
::v-deep .zp-paging-container-content {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
::v-deep .index_top_navbar .tmicon-angle-left {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
::v-deep .index_top_navbar .text-weight-b {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
::v-deep .index_top_navbar .statusHeightTop > .noNvueBorder:first-child {
|
||||
background: transparent !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.top_bg {
|
||||
background: url('@/static/image/mine/page_top.png') no-repeat;
|
||||
background-size: cover;
|
||||
background-position: bottom center;
|
||||
}
|
||||
|
||||
:deep(.animateAll_tabs_tmui) {
|
||||
width: 120rpx !important;
|
||||
height: 10rpx !important;
|
||||
}
|
||||
|
||||
.address-book-page {
|
||||
.address-book {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-image: url('@/static/image/clockIn/z3280@3x.png');
|
||||
background-size: cover;
|
||||
background-position: bottom center;
|
||||
background-attachment: fixed;
|
||||
width: 100%;
|
||||
|
||||
.address-book-tabs-panes-list {
|
||||
margin: 30rpx 24rpx;
|
||||
overflow: hidden;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.tabs-panes-each {
|
||||
gap: 30rpx 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
}
|
||||
.address-book-company,
|
||||
.address-book-friends {
|
||||
.address-book-company-name {
|
||||
margin: 0 0 -16rpx;
|
||||
span {
|
||||
font-size: 26rpx;
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
.members-list-each {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
background-color: #fff;
|
||||
width: 100%;
|
||||
border-radius: 8rpx;
|
||||
|
||||
.swipe-action {
|
||||
width: 100%;
|
||||
overflow: visible !important;
|
||||
|
||||
:deep(.wd-swipe-action__right) {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #cf3050;
|
||||
padding: 0 48rpx;
|
||||
border-radius: 0 8rpx 8rpx 0;
|
||||
span {
|
||||
color: #fff;
|
||||
line-height: 1;
|
||||
font-size: 30rpx;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.members-info {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
gap: 16rpx;
|
||||
padding: 24rpx 24rpx 0;
|
||||
width: 100%;
|
||||
|
||||
.members-info-area {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
gap: 16rpx;
|
||||
|
||||
.members-info-basic {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
|
||||
.members-name {
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
width: 150rpx;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.members-jobNum {
|
||||
font-size: 26rpx;
|
||||
font-weight: 400;
|
||||
color: #999;
|
||||
word-break: break-all;
|
||||
margin: 10rpx 0 0;
|
||||
}
|
||||
|
||||
.members-company {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
.members-positions-area {
|
||||
.members-positions {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
flex-wrap: wrap;
|
||||
gap: 10rpx;
|
||||
padding: 4rpx 0 0;
|
||||
max-height: 84rpx;
|
||||
overflow: hidden;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
line-clamp: 2;
|
||||
|
||||
.members-positions-each {
|
||||
background-color: #eee9f8;
|
||||
line-height: 1;
|
||||
font-size: 24rpx;
|
||||
padding: 4rpx 16rpx;
|
||||
color: #46299d;
|
||||
}
|
||||
}
|
||||
.members-positions-popover-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10rpx 0;
|
||||
|
||||
.members-positions-popover {
|
||||
background-color: #eee9f8;
|
||||
line-height: 1;
|
||||
font-size: 24rpx;
|
||||
padding: 8rpx 16rpx;
|
||||
color: #46299d;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.company-infos {
|
||||
margin: 0 0 0 88rpx;
|
||||
padding: 0 0 24rpx 24rpx;
|
||||
.company-each {
|
||||
span {
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.address-book-groups {
|
||||
.groups-list-each {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background-color: #fff;
|
||||
width: 100%;
|
||||
padding: 24rpx;
|
||||
border-radius: 8rpx;
|
||||
|
||||
.groups-info {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 16rpx;
|
||||
.groups-name {
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
.groups-type {
|
||||
font-size: 24rpx;
|
||||
border-radius: 6rpx;
|
||||
font-weight: bold;
|
||||
padding: 6rpx 12rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
.groups-btns {
|
||||
flex-shrink: 0;
|
||||
margin: 0 0 0 16rpx;
|
||||
.groups-btns-each {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #46299d;
|
||||
padding: 16rpx;
|
||||
border-radius: 8rpx;
|
||||
|
||||
span {
|
||||
color: #fff;
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.addressBook-noData {
|
||||
margin: 30rpx 24rpx;
|
||||
overflow: hidden;
|
||||
flex: 1;
|
||||
gap: 30rpx 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
img {
|
||||
width: 500rpx;
|
||||
}
|
||||
span {
|
||||
font-size: 30rpx;
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.friendDeleteModal {
|
||||
:deep(.overlay) {
|
||||
height: 100vh !important;
|
||||
}
|
||||
.friendDeleteModal-content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -9,7 +9,7 @@
|
||||
? ''
|
||||
: '0',
|
||||
}"
|
||||
v-for="(memberItem, memberIndex) in sortedMemberList"
|
||||
v-for="(memberItem, memberIndex) in props?.memberList"
|
||||
@click="toUserDetailPage(memberItem)"
|
||||
>
|
||||
<div
|
||||
@ -53,7 +53,7 @@
|
||||
>
|
||||
<div class="group-member-each">
|
||||
<div class="group-member-avatar" :style="{ background: 'unset' }">
|
||||
<img src="@/static/image/chatSettings/add-member.png" />
|
||||
<img src="/src/static/image/chatSettings/add-member.png" />
|
||||
</div>
|
||||
<div class="group-member-name">
|
||||
<span class="text-[24rpx] font-regular">添加</span>
|
||||
@ -71,7 +71,7 @@
|
||||
>
|
||||
<div class="group-member-each">
|
||||
<div class="group-member-avatar" :style="{ background: 'unset' }">
|
||||
<img src="@/static/image/chatSettings/remove-member.png" />
|
||||
<img src="/src/static/image/chatSettings/remove-member.png" />
|
||||
</div>
|
||||
<div class="group-member-name">
|
||||
<span class="text-[24rpx] font-regular">移除</span>
|
||||
@ -81,7 +81,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { defineProps, computed } from 'vue'
|
||||
import { defineProps } from 'vue'
|
||||
const props = defineProps({
|
||||
memberList: Array, //人员列表
|
||||
memberListsLimit: Number, //人员列表数量限制
|
||||
@ -90,36 +90,6 @@ const props = defineProps({
|
||||
hideAddRemoveBtns: Boolean, //是否隐藏添加移除按钮
|
||||
})
|
||||
|
||||
// 对成员列表进行排序
|
||||
const sortedMemberList = computed(() => {
|
||||
if (!props.memberList || props.memberList.length === 0) return [];
|
||||
|
||||
// 创建一个新数组,保留原始索引
|
||||
const indexedList = props.memberList.map((item, index) => ({ item, index }));
|
||||
|
||||
// 按照leader属性和原始顺序排序
|
||||
return indexedList.sort((a, b) => {
|
||||
const leaderA = a.item.leader || 0;
|
||||
const leaderB = b.item.leader || 0;
|
||||
|
||||
// 如果leader状态不同,优先按leader排序
|
||||
if ((leaderA === 1 || leaderA === 2) && (leaderB !== 1 && leaderB !== 2)) {
|
||||
return -1; // a排在前面
|
||||
}
|
||||
if ((leaderB === 1 || leaderB === 2) && (leaderA !== 1 && leaderA !== 2)) {
|
||||
return 1; // b排在前面
|
||||
}
|
||||
|
||||
// 如果都是管理员,按照leader值大小排序
|
||||
if ((leaderA === 1 || leaderA === 2) && (leaderB === 1 || leaderB === 2)) {
|
||||
return leaderB - leaderA; // leader值大的排前面
|
||||
}
|
||||
|
||||
// 其他情况保持原始顺序
|
||||
return a.index - b.index;
|
||||
}).map(item => item.item); // 返回排序后的原始对象
|
||||
})
|
||||
|
||||
//点击跳转到用户详情页面
|
||||
const toUserDetailPage = (userItem) => {
|
||||
console.log(userItem)
|
||||
|
@ -49,7 +49,7 @@
|
||||
props?.memberItem?.is_mine && props?.manageType === 'removeMembers'
|
||||
"
|
||||
>
|
||||
<img src="@/static/image/chatSettings/is-mine.png" />
|
||||
<img src="/src/static/image/chatSettings/is-mine.png" />
|
||||
</div>
|
||||
<div
|
||||
class="is-admin-tag"
|
||||
|
@ -1,803 +0,0 @@
|
||||
<template>
|
||||
<div class="select-member-by-alphabet">
|
||||
<ZPaging ref="zPaging" :show-scrollbar="false" :use-virtual-list="true" :virtual-list-col="5"
|
||||
:refresher-enabled="false" :loading-more-enabled="false" @scroll="onScroll" :fixed="false"
|
||||
:height="props?.selectAreaHeight">
|
||||
<div class="select-members">
|
||||
<div class="search-member" v-if="
|
||||
props?.manageType !== 'removeMembers' &&
|
||||
!(
|
||||
props?.manageType === 'admin' &&
|
||||
(groupParams.groupInfo.group_type === 2 ||
|
||||
groupParams.groupInfo.group_type === 4)
|
||||
)
|
||||
">
|
||||
<customInput :searchText="state.searchText" @inputSearchText="inputSearchText"></customInput>
|
||||
</div>
|
||||
<div v-show="props?.manageType == 'removeMembers'" class="my-self">
|
||||
<div class="my-self-left">
|
||||
<image style="width: 72rpx;border-radius: 50%;height: 72rpx;" :src="mySelfMember.avatar" mode=""></image>
|
||||
<div style="padding: 0 20rpx;">{{mySelfMember.nickname}}</div>
|
||||
<img style="width: 45rpx;" src="@/static/image/chatSettings/is-mine.png" />
|
||||
</div>
|
||||
|
||||
<div class="my-self-right">
|
||||
{{ $t('group.identify.admin') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="member-list" :style="{
|
||||
padding: props?.manageType === 'searchRecord' ? '20rpx 0 0' : '',
|
||||
}">
|
||||
<div class="member-list-alphabet-anchor-point" :style="{
|
||||
top: props?.manageType === 'mention' ? '90rpx' : '',
|
||||
}">
|
||||
<div class="member-list-alphabet-anchor-point-each" v-for="(alphabetItem, alphabetIndex) in state?.alphabet"
|
||||
:key="alphabetIndex" :style="{
|
||||
margin: state?.alphabet?.length > 17 ? '0' : '',
|
||||
}" @click.stop="scrollToView(alphabetItem)">
|
||||
<span class="text-[32rpx] font-regular" :style="{
|
||||
color:
|
||||
state.currentAlphabet === alphabetItem ? '#7A58DE' : '',
|
||||
}">
|
||||
{{ alphabetItem }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="member-list-alphabet" v-for="(alphabetItem, alphabetIndex) in state.resultMemberList"
|
||||
:key="alphabetIndex">
|
||||
<div class="member-list-alphabet-key" :style="{
|
||||
padding:
|
||||
props?.manageType === 'searchRecord' ||
|
||||
props?.manageType === 'removeMembers' ||
|
||||
props?.manageType === 'mention'
|
||||
? '10rpx 30rpx'
|
||||
: '',
|
||||
}" v-if="
|
||||
alphabetItem?.memberList?.length > 0 &&
|
||||
alphabetItem?.key !== '0'
|
||||
" :id="alphabetItem.key === '#' ? 'special-hash' : alphabetItem.key" :ref="
|
||||
(el) => {
|
||||
if (el) alphabetElementRefs[alphabetIndex] = el
|
||||
}
|
||||
">
|
||||
<span class="text-[32rpx] font-regular">
|
||||
{{ alphabetItem.key }}
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="alphabetItem?.memberList?.length > 0">
|
||||
<div class="member-list-each" v-for="(item, index) in alphabetItem?.memberList" :key="index">
|
||||
<tm-checkbox-group v-model="item.checkArr">
|
||||
<selectMemberItem :groupType="groupParams.groupInfo.group_type" :memberItem="item"
|
||||
@clickItem="handleClickItem(item)" :manageType="props?.manageType" :itemStyle="
|
||||
props?.manageType === 'searchRecord' ||
|
||||
props?.manageType === 'removeMembers' ||
|
||||
props?.manageType === 'mention'
|
||||
? 'list'
|
||||
: 'card'
|
||||
">
|
||||
<template #left v-if="props?.manageType !== 'searchRecord'">
|
||||
<div v-if="
|
||||
props?.manageType === 'removeMembers' && item?.is_mine
|
||||
">
|
||||
<tm-checkbox color="#fff" :transprent="true" :border="0" :disabled="true"></tm-checkbox>
|
||||
</div>
|
||||
<tm-checkbox v-if="
|
||||
!(
|
||||
props?.manageType === 'removeMembers' &&
|
||||
item?.is_mine
|
||||
) && props?.isMulSelect
|
||||
" :round="10" :color="
|
||||
item?.checkArr?.length > 0 ? '#46299d' : '#B4B4B4'
|
||||
" :outlined="
|
||||
item?.checkArr?.length > 0 ||
|
||||
(props?.manageType === 'silence' &&
|
||||
item.is_mute === 1) ||
|
||||
(props?.manageType === 'admin' &&
|
||||
(item.leader === 1 || item.leader === 2))
|
||||
? false
|
||||
: true
|
||||
" :value="item.id" :disabled="
|
||||
(props?.manageType === 'silence' &&
|
||||
item.is_mute === 1) ||
|
||||
(props?.manageType === 'admin' &&
|
||||
(item.leader === 1 || item.leader === 2))
|
||||
" @change="checkBoxChange"></tm-checkbox>
|
||||
</template>
|
||||
</selectMemberItem>
|
||||
</tm-checkbox-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ZPaging>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import customInput from '@/components/custom-input/custom-input.vue'
|
||||
import selectMemberItem from '../components/select-member-item.vue'
|
||||
import ZPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
|
||||
import useZPaging from '@/uni_modules/z-paging/components/z-paging/js/hooks/useZPaging.js'
|
||||
import groupAllMember from '@/static/image/chatList/groupAllMember.png'
|
||||
import {
|
||||
computed,
|
||||
onMounted,
|
||||
reactive,
|
||||
ref,
|
||||
watch,
|
||||
nextTick,
|
||||
defineProps,
|
||||
defineEmits,
|
||||
} from 'vue'
|
||||
import {
|
||||
ServeGroupNoSpeak,
|
||||
ServeEditGroupAdmin,
|
||||
ServeGroupAssignAdmin,
|
||||
ServeRemoveMembersGroup,
|
||||
} from '@/api/group/index.js'
|
||||
import {
|
||||
useDialogueStore,
|
||||
useGroupStore,
|
||||
useGroupTypeStore
|
||||
} from '@/store'
|
||||
|
||||
const emits = defineEmits([
|
||||
'updateSelectedMembersNum',
|
||||
'getSelectResult',
|
||||
'getMentionSelectLists',
|
||||
])
|
||||
|
||||
const zPaging = ref()
|
||||
useZPaging(zPaging)
|
||||
|
||||
const groupStore = useGroupStore()
|
||||
const groupParams = reactive({
|
||||
groupInfo: computed(() => groupStore.groupInfo),
|
||||
})
|
||||
|
||||
const groupTypeStore = useGroupTypeStore()
|
||||
const groupTypeParams = reactive({
|
||||
departmentAllPositions: computed(() => groupTypeStore.departmentAllPositions),
|
||||
})
|
||||
|
||||
const dialogueStore = useDialogueStore()
|
||||
const dialogueParams = reactive({
|
||||
memberList: computed(() => {
|
||||
const lowerCaseSearchText = state?.searchText.toLowerCase()
|
||||
return dialogueStore.members.filter((item) =>
|
||||
state?.searchText ?
|
||||
item.nickname.toLowerCase().includes(lowerCaseSearchText) :
|
||||
true,
|
||||
)
|
||||
}),
|
||||
receiverId: computed(() => dialogueStore.talk.receiver_id),
|
||||
})
|
||||
|
||||
const props = defineProps({
|
||||
manageType: {
|
||||
//管理类型
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
isCreateDepGroup: {
|
||||
//是否是创建部门群
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
selectAreaHeight: {
|
||||
//选择区域高度
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
isMulSelect: {
|
||||
//是否开启多选
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
})
|
||||
|
||||
const state = reactive({
|
||||
searchText: '', //搜索内容
|
||||
alphabet: [], //A-Z列表
|
||||
currentAlphabet: 'A', //当前A-Z位置
|
||||
resultMemberList: [], //按A-Z整理后的人员列表
|
||||
isAssign: false, //是否指定view
|
||||
scrollDirection: '', //当前列表滚动方向
|
||||
})
|
||||
|
||||
watch(
|
||||
() => dialogueParams?.memberList,
|
||||
(newMemberList) => {
|
||||
assembleAlphabetMemberList(newMemberList)
|
||||
}, {
|
||||
deep: true
|
||||
},
|
||||
)
|
||||
|
||||
watch(
|
||||
() => groupParams?.groupInfo,
|
||||
(newGroupInfo) => {
|
||||
assembleAlphabetMemberList(dialogueParams?.memberList)
|
||||
}, {
|
||||
deep: true
|
||||
},
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props?.isMulSelect,
|
||||
(newIsMulSelect) => {
|
||||
if (props?.manageType === 'mention') {
|
||||
if (!newIsMulSelect) {
|
||||
state.resultMemberList.unshift({
|
||||
key: '0',
|
||||
memberList: [{
|
||||
avatar: groupAllMember,
|
||||
erp_user_id: 0,
|
||||
gender: 0,
|
||||
is_mute: 0,
|
||||
key: '0',
|
||||
leader: 0,
|
||||
nickname: '所有人',
|
||||
remark: '',
|
||||
user_id: 0,
|
||||
}, ],
|
||||
})
|
||||
} else {
|
||||
if (state.resultMemberList[0].key === '0') {
|
||||
state.resultMemberList.splice(0, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
deep: true,
|
||||
immediate: true
|
||||
},
|
||||
)
|
||||
|
||||
//获取A-Z tag元素
|
||||
const alphabetElementRefs = ref([])
|
||||
//观察者
|
||||
let observer
|
||||
|
||||
onMounted(() => {
|
||||
if (props?.manageType) {
|
||||
assembleAlphabetMemberList(dialogueParams?.memberList)
|
||||
}
|
||||
if (props?.isCreateDepGroup) {
|
||||
assembleAlphabetMemberList()
|
||||
}
|
||||
|
||||
dialogueParams.memberList.forEach((ele) => {
|
||||
ele.checkArr = []
|
||||
})
|
||||
|
||||
const options = {
|
||||
root: null, // 使用浏览器窗口作为根容器
|
||||
threshold: 1.0, // 当元素100%离开视口时触发
|
||||
}
|
||||
observer = new IntersectionObserver(handleIntersection, options)
|
||||
nextTick(() => {
|
||||
watch(
|
||||
alphabetElementRefs,
|
||||
(newAlphabetElementRefs) => {
|
||||
if (Array.isArray(newAlphabetElementRefs)) {
|
||||
newAlphabetElementRefs.forEach((el, index) => {
|
||||
observeElement(el, index)
|
||||
})
|
||||
}
|
||||
}, {
|
||||
immediate: true,
|
||||
deep: true
|
||||
},
|
||||
)
|
||||
if (alphabetElementRefs.value.length > 0) {
|
||||
alphabetElementRefs.value.forEach((el, index) =>
|
||||
observeElement(el, index),
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
//观察元素
|
||||
const observeElement = (el, index) => {
|
||||
if (el && observer) {
|
||||
observer.observe(el)
|
||||
}
|
||||
}
|
||||
|
||||
// 观察者函数
|
||||
const handleIntersection = (entries) => {
|
||||
if (state.isAssign) {
|
||||
state.isAssign = false
|
||||
return
|
||||
}
|
||||
entries.forEach((entry) => {
|
||||
if (!entry.isIntersecting && state.scrollDirection === 'down') {
|
||||
state.currentAlphabet = entry.target.id
|
||||
} else if (entry.isIntersecting && state.scrollDirection === 'up') {
|
||||
if (state?.alphabet?.length > 1) {
|
||||
state?.alphabet.forEach((item, index) => {
|
||||
if (item === entry.target.id && index > 0) {
|
||||
state.currentAlphabet = state?.alphabet[index - 1]
|
||||
}
|
||||
})
|
||||
} else {
|
||||
state.currentAlphabet = entry.target.id
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//输入搜索文本
|
||||
const inputSearchText = (e) => {
|
||||
// console.log(e)
|
||||
state.searchText = e
|
||||
}
|
||||
|
||||
//点击item
|
||||
const handleClickItem = (item) => {
|
||||
if (
|
||||
(props?.manageType === 'silence' && item.is_mute === 1) ||
|
||||
(props?.manageType === 'admin' &&
|
||||
(item.leader === 1 || item.leader === 2)) ||
|
||||
(props?.manageType === 'removeMembers' && item.is_mine) ||
|
||||
(props?.manageType === 'mention' && !props?.isMulSelect)
|
||||
) {
|
||||
if (props?.manageType === 'mention' && !props?.isMulSelect) {
|
||||
emits('getMentionSelectLists', [item])
|
||||
}
|
||||
return
|
||||
}
|
||||
if (props?.manageType === 'searchRecord') {
|
||||
uni.navigateTo({
|
||||
url: '/pages/search/searchByCondition/index?condition=member&groupMemberId=' +
|
||||
item.id,
|
||||
})
|
||||
return
|
||||
}
|
||||
let itemList = dialogueParams.memberList
|
||||
if (
|
||||
props?.manageType === 'admin' &&
|
||||
(groupParams.groupInfo.group_type == 2 ||
|
||||
groupParams.groupInfo.group_type == 4 ||
|
||||
props?.isCreateDepGroup === 1)
|
||||
) {
|
||||
itemList = state.resultMemberList[0].memberList
|
||||
}
|
||||
itemList.forEach((ele) => {
|
||||
if (ele.id == item.id) {
|
||||
ele.checkArr = ele.checkArr?.length > 0 ? [] : [item.id]
|
||||
if (ele.checkArr?.length > 0) {
|
||||
emits('updateSelectedMembersNum', 1)
|
||||
} else {
|
||||
emits('updateSelectedMembersNum', -1)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
const mySelfMember = ref({})
|
||||
//组装A-Z排序的人员列表
|
||||
const assembleAlphabetMemberList = async (newMemberList) => {
|
||||
if (props?.manageType === 'removeMembers' && Array.isArray(newMemberList)) {
|
||||
// 只遍历一次,找到第一个 is_mine 为 true 的成员并移除
|
||||
for (let i = 0; i < newMemberList.length; i++) {
|
||||
const item = newMemberList[i];
|
||||
if (item?.is_mine === true) {
|
||||
mySelfMember.value = item;
|
||||
newMemberList.splice(i, 1); // 删除该成员
|
||||
break; // 找到后立即跳出
|
||||
}
|
||||
}
|
||||
}
|
||||
if (
|
||||
props?.manageType === 'searchRecord' ||
|
||||
props?.manageType === 'removeMembers' ||
|
||||
props?.manageType === 'mention'
|
||||
) {
|
||||
const resultMemberList = ref([])
|
||||
const alphabet = Array.from({
|
||||
length: 26
|
||||
}, (_, i) =>
|
||||
String.fromCharCode(i + 65),
|
||||
)
|
||||
let tempAlphabet = []
|
||||
|
||||
// 创建一个对象来存储所有分组的数据
|
||||
let groupedData = {
|
||||
'#': [],
|
||||
}
|
||||
alphabet.forEach((letter) => {
|
||||
groupedData[letter] = []
|
||||
})
|
||||
|
||||
// 对数据进行分组
|
||||
if (newMemberList) {
|
||||
newMemberList.forEach((item) => {
|
||||
const key = item.key?.toUpperCase()
|
||||
if (alphabet.includes(key)) {
|
||||
groupedData[key].push(item)
|
||||
} else {
|
||||
groupedData['#'].push(item)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 将分组数据转换为最终格式
|
||||
alphabet.forEach((letter) => {
|
||||
if (groupedData[letter].length > 0) {
|
||||
tempAlphabet.push(letter)
|
||||
}
|
||||
resultMemberList.value.push({
|
||||
key: letter,
|
||||
memberList: groupedData[letter],
|
||||
})
|
||||
})
|
||||
|
||||
// 添加#分组(如果有数据)
|
||||
if (groupedData['#'].length > 0) {
|
||||
tempAlphabet.push('#')
|
||||
resultMemberList.value.push({
|
||||
key: '#',
|
||||
memberList: groupedData['#'],
|
||||
})
|
||||
}
|
||||
|
||||
state.alphabet = tempAlphabet
|
||||
if (props?.manageType === 'mention' && !props?.isMulSelect) {
|
||||
resultMemberList.value.unshift({
|
||||
key: '0',
|
||||
memberList: [{
|
||||
avatar: groupAllMember,
|
||||
erp_user_id: 0,
|
||||
gender: 0,
|
||||
is_mute: 0,
|
||||
key: '0',
|
||||
leader: 0,
|
||||
nickname: '所有人',
|
||||
remark: '',
|
||||
user_id: 0,
|
||||
}, ],
|
||||
})
|
||||
}
|
||||
state.resultMemberList = resultMemberList
|
||||
} else {
|
||||
if (
|
||||
(groupParams.groupInfo.group_type == 2 ||
|
||||
groupParams.groupInfo.group_type == 4) &&
|
||||
props?.manageType === 'admin'
|
||||
) {
|
||||
let departmentIdsArr = []
|
||||
if (groupParams?.groupInfo?.deptInfos?.length > 0) {
|
||||
groupParams.groupInfo.deptInfos.forEach((item) => {
|
||||
departmentIdsArr.push(item.dept_id)
|
||||
})
|
||||
}
|
||||
getPosiByDep(departmentIdsArr)
|
||||
} else if (props?.isCreateDepGroup === 1) {
|
||||
let departmentIdsArr = []
|
||||
if (groupTypeStore?.depCheckedKeys?.value?.length > 0) {
|
||||
groupTypeStore.depCheckedKeys.value.forEach((item) => {
|
||||
departmentIdsArr.push(item.ID)
|
||||
})
|
||||
}
|
||||
getPosiByDep(departmentIdsArr)
|
||||
} else {
|
||||
state.resultMemberList = [{
|
||||
key: '',
|
||||
memberList: newMemberList,
|
||||
}, ]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//按部门获取岗位
|
||||
const getPosiByDep = async (departmentIdsArr) => {
|
||||
await groupTypeStore.getPositionByDepartment({
|
||||
IDs: departmentIdsArr,
|
||||
})
|
||||
let departmentAllPositions = []
|
||||
if (groupTypeParams?.departmentAllPositions?.value?.length > 0) {
|
||||
groupTypeParams?.departmentAllPositions?.value?.forEach((item) => {
|
||||
item?.AllPositions?.forEach((positionItem) => {
|
||||
departmentAllPositions.push({
|
||||
nickname: item.name + '-' + positionItem.name,
|
||||
id: item.ID + '-' + positionItem.ID,
|
||||
checkArr: [],
|
||||
positionInfo: {
|
||||
dept_id: item.ID,
|
||||
dept_name: item.name,
|
||||
position_id: positionItem.ID,
|
||||
position_name: positionItem.name,
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
if (groupParams?.groupInfo?.groupAdminList?.length > 0) {
|
||||
groupParams?.groupInfo?.groupAdminList.forEach((item) => {
|
||||
departmentAllPositions.forEach((idsItem) => {
|
||||
if (item.dept_id + '-' + item.position_id == idsItem.id) {
|
||||
idsItem.leader = 1
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
if (
|
||||
props?.isCreateDepGroup === 1 &&
|
||||
groupTypeStore?.groupAdmins?.value?.length > 0
|
||||
) {
|
||||
departmentAllPositions.forEach((allPos) => {
|
||||
groupTypeStore.groupAdmins.value.forEach((admin) => {
|
||||
if (allPos.id === admin.id) {
|
||||
allPos.checkArr = [allPos.id]
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
if (state?.searchText) {
|
||||
const lowerCaseSearchText = state?.searchText.toLowerCase()
|
||||
departmentAllPositions = departmentAllPositions.filter((item) =>
|
||||
state?.searchText ?
|
||||
item.nickname.toLowerCase().includes(lowerCaseSearchText) :
|
||||
true,
|
||||
)
|
||||
}
|
||||
state.resultMemberList = [{
|
||||
key: '',
|
||||
memberList: departmentAllPositions,
|
||||
}, ]
|
||||
}
|
||||
|
||||
//滚动到指定的view
|
||||
const scrollToView = (alphabet) => {
|
||||
state.currentAlphabet = alphabet
|
||||
state.isAssign = true
|
||||
|
||||
// 计算偏移高度
|
||||
const offsetHeight = document.getElementById('topArea')?.clientHeight ?
|
||||
document.getElementById('topArea').clientHeight - 1 :
|
||||
props?.manageType === 'mention' ?
|
||||
140 :
|
||||
80
|
||||
|
||||
// 使用scrollIntoViewById处理所有情况
|
||||
const targetId = alphabet === '#' ? 'special-hash' : alphabet
|
||||
zPaging.value?.scrollIntoViewById(targetId, offsetHeight)
|
||||
}
|
||||
|
||||
//监听列表滚动
|
||||
const onScroll = (e) => {
|
||||
if (e.detail.deltaY < 0) {
|
||||
state.scrollDirection = 'down'
|
||||
} else if (e.detail.deltaY > 0) {
|
||||
state.scrollDirection = 'up'
|
||||
} else {
|
||||
state.scrollDirection = ''
|
||||
}
|
||||
}
|
||||
|
||||
//选中的人员改变
|
||||
const checkBoxChange = (e) => {
|
||||
if (e) {
|
||||
emits('updateSelectedMembersNum', 1)
|
||||
} else {
|
||||
emits('updateSelectedMembersNum', -1)
|
||||
}
|
||||
}
|
||||
|
||||
//点击确认选择的成员
|
||||
const confirmSelectMembers = () => {
|
||||
let selectedUserIds = ''
|
||||
let itemList = dialogueStore.members
|
||||
let positionInfos = []
|
||||
let selectUserInfos = []
|
||||
if (
|
||||
props?.manageType === 'admin' &&
|
||||
(groupParams.groupInfo.group_type == 2 ||
|
||||
groupParams.groupInfo.group_type == 4 ||
|
||||
props?.isCreateDepGroup === 1)
|
||||
) {
|
||||
itemList = state.resultMemberList[0].memberList
|
||||
}
|
||||
itemList.forEach((ele) => {
|
||||
if (ele.checkArr?.length > 0) {
|
||||
if (!selectedUserIds) {
|
||||
selectedUserIds = String(ele.checkArr[0])
|
||||
} else {
|
||||
selectedUserIds += ',' + ele.checkArr[0]
|
||||
}
|
||||
selectUserInfos.push(ele)
|
||||
}
|
||||
if (
|
||||
ele.checkArr?.length > 0 ||
|
||||
(ele.leader && (ele.leader == 1 || ele.leader == 2))
|
||||
) {
|
||||
if (props?.isCreateDepGroup === 1) {
|
||||
let posInfo = Object.assign({}, ele.positionInfo, {
|
||||
name: ele.nickname,
|
||||
id: ele.id,
|
||||
})
|
||||
positionInfos.push(posInfo)
|
||||
} else {
|
||||
positionInfos.push(ele.positionInfo)
|
||||
}
|
||||
}
|
||||
})
|
||||
console.log(selectedUserIds)
|
||||
if (selectedUserIds) {
|
||||
if (props?.manageType === 'silence') {
|
||||
let params = {
|
||||
mode: 1, //1禁言2解禁
|
||||
group_id: dialogueParams.receiverId, //群id
|
||||
user_ids: selectedUserIds, //用户ids
|
||||
}
|
||||
console.log(params)
|
||||
const resp = ServeGroupNoSpeak(params)
|
||||
resp.then(({
|
||||
code,
|
||||
data
|
||||
}) => {
|
||||
console.log(data)
|
||||
if (code == 200) {
|
||||
useDialogueStore().updateGroupMembers()
|
||||
} else {}
|
||||
})
|
||||
resp.catch(() => {})
|
||||
} else if (props?.manageType === 'admin') {
|
||||
if (props?.isCreateDepGroup === 1) {
|
||||
// console.log(positionInfos)
|
||||
groupTypeStore.groupAdmins.value = positionInfos
|
||||
uni.navigateBack({
|
||||
delta: 1,
|
||||
})
|
||||
} else {
|
||||
if (
|
||||
groupParams.groupInfo.group_type == 1 ||
|
||||
groupParams.groupInfo.group_type == 3
|
||||
) {
|
||||
let params = {
|
||||
mode: 1, //1管理员,2不是管理员
|
||||
group_id: dialogueParams.receiverId, //群id
|
||||
user_ids: selectedUserIds,
|
||||
}
|
||||
console.log(params)
|
||||
const resp = ServeGroupAssignAdmin(params)
|
||||
resp.then(({
|
||||
code,
|
||||
data
|
||||
}) => {
|
||||
console.log(data)
|
||||
if (code == 200) {
|
||||
useDialogueStore().updateGroupMembers()
|
||||
} else {}
|
||||
})
|
||||
resp.catch(() => {})
|
||||
} else if (
|
||||
groupParams.groupInfo.group_type == 2 ||
|
||||
groupParams.groupInfo.group_type == 4
|
||||
) {
|
||||
let params = {
|
||||
source: 'app',
|
||||
id: dialogueParams.receiverId,
|
||||
deptInfos: groupParams.groupInfo.deptInfos,
|
||||
positionInfos: positionInfos,
|
||||
}
|
||||
console.log(params)
|
||||
const resp = ServeEditGroupAdmin(params)
|
||||
resp.then(({
|
||||
code,
|
||||
data
|
||||
}) => {
|
||||
console.log(data)
|
||||
if (code == 200) {
|
||||
groupStore.ServeGroupDetail()
|
||||
} else {}
|
||||
})
|
||||
resp.catch(() => {})
|
||||
}
|
||||
}
|
||||
} else if (props?.manageType === 'removeMembers') {
|
||||
let params = {
|
||||
group_id: dialogueParams.receiverId, //群id
|
||||
members_ids: selectedUserIds, //群成员id,批量的话逗号分割
|
||||
}
|
||||
console.log(params)
|
||||
const resp = ServeRemoveMembersGroup(params)
|
||||
resp.then(({
|
||||
code,
|
||||
data
|
||||
}) => {
|
||||
console.log(data)
|
||||
if (code == 200) {
|
||||
// console.error(-selectedUserIds.split(',').length)
|
||||
emits('updateSelectedMembersNum', -selectedUserIds.split(',').length)
|
||||
useDialogueStore().updateGroupMembers()
|
||||
groupStore.ServeGroupDetail()
|
||||
} else {}
|
||||
})
|
||||
resp.catch(() => {})
|
||||
} else if (props?.manageType === 'mention') {
|
||||
emits('getSelectResult', selectUserInfos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 暴露确认方法
|
||||
defineExpose({
|
||||
confirmSelectMembers,
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.my-self {
|
||||
padding: 10rpx 60rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background-color: #fff;
|
||||
|
||||
|
||||
.my-self-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 8%;
|
||||
}
|
||||
|
||||
.my-self-right {
|
||||
color: #b4b4b4;
|
||||
padding: 0.1875rem 0.375rem;
|
||||
border: #b4b4b4 1px solid;
|
||||
border-radius: 8rpx;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
}
|
||||
|
||||
.select-member-by-alphabet {
|
||||
.select-members {
|
||||
padding: 20rpx 32rpx;
|
||||
|
||||
.search-member {
|
||||
padding: 22rpx 16rpx;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.member-list {
|
||||
.member-list-alphabet-anchor-point {
|
||||
position: fixed;
|
||||
right: 32rpx;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.member-list-alphabet-anchor-point-each {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 0 14rpx;
|
||||
|
||||
span {
|
||||
width: 52rpx;
|
||||
text-align: center;
|
||||
line-height: 44rpx;
|
||||
color: $theme-text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.member-list-alphabet {
|
||||
.member-list-alphabet-key {
|
||||
background-color: #f3f3f3;
|
||||
|
||||
span {
|
||||
line-height: 44rpx;
|
||||
color: $theme-text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -16,10 +16,9 @@
|
||||
props?.item?.hasPointer &&
|
||||
(props?.isManager ||
|
||||
(!props?.isManager &&
|
||||
(props?.item?.label !== $t('chat.settings.groupName') ||
|
||||
props?.item?.label !== $t('chat.settings.groupType'))))
|
||||
props?.item?.label !== $t('chat.settings.groupName')))
|
||||
"
|
||||
src="@/static/image/chatSettings/pointer.png"
|
||||
src="/src/static/image/chatSettings/pointer.png"
|
||||
/>
|
||||
<tm-switch
|
||||
:width="88"
|
||||
@ -28,8 +27,8 @@
|
||||
barIcon=""
|
||||
color="#46299D"
|
||||
unCheckedColor="#EEEEEE"
|
||||
:modelValue="props?.item?.label === t('chat.settings.openReminder') ? openReminderStatus : modelValue"
|
||||
:defaultValue="props?.item?.label === t('chat.settings.openReminder') ? openReminderStatus : modelValue"
|
||||
:modelValue="modelValue"
|
||||
:defaultValue="modelValue"
|
||||
@change="changeSwitch($event, props?.item)"
|
||||
></tm-switch>
|
||||
</div>
|
||||
@ -46,8 +45,8 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { defineProps, defineEmits, computed, ref, onMounted, watch } from 'vue'
|
||||
import { ServeContactRobotQuery } from '@/api/chat'
|
||||
import { defineProps, defineEmits, computed } from 'vue'
|
||||
|
||||
import { useI18n } from 'vue-i18n'
|
||||
const { t } = useI18n()
|
||||
|
||||
@ -103,32 +102,6 @@ const modelValue = computed(() => {
|
||||
return switchStatus
|
||||
})
|
||||
|
||||
// 仅用于“拒收消息”场景的本地状态
|
||||
const openReminderStatus = ref(false)
|
||||
const loadRefuseStatus = async () => {
|
||||
try {
|
||||
const res = await ServeContactRobotQuery()
|
||||
openReminderStatus.value = !!(res?.data?.Result)
|
||||
} catch (err) {
|
||||
openReminderStatus.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (props?.item?.label === t('chat.settings.openReminder')) {
|
||||
loadRefuseStatus()
|
||||
}
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props?.item?.label,
|
||||
(newLabel) => {
|
||||
if (newLabel === t('chat.settings.openReminder')) {
|
||||
loadRefuseStatus()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
//切换开关选择
|
||||
const changeSwitch = (e, item) => {
|
||||
emits('changeSwitch', e, item.label)
|
||||
|
@ -22,7 +22,6 @@
|
||||
<customBtn
|
||||
:btnText="$t('button.text.edit')"
|
||||
@click="editAvatar"
|
||||
:isLoading="state.isLoading"
|
||||
></customBtn>
|
||||
</ZPaging>
|
||||
</div>
|
||||
@ -50,10 +49,6 @@ const dialogueParams = reactive({
|
||||
receiver_id: computed(() => dialogueStore.talk.receiver_id),
|
||||
})
|
||||
|
||||
const state = reactive({
|
||||
isLoading: false, //是否正在加载中
|
||||
})
|
||||
|
||||
const onProgressFn = (progress, id) => {
|
||||
console.log((progress.loaded / progress.total) * 100, 'progress')
|
||||
|
||||
@ -74,7 +69,6 @@ const editAvatar = () => {
|
||||
count: 1,
|
||||
success: async (res) => {
|
||||
console.log(res, 'res')
|
||||
state.isLoading = true
|
||||
res.tempFiles.forEach(async (file) => {
|
||||
console.log(file)
|
||||
let image = new Image()
|
||||
@ -99,7 +93,6 @@ const editAvatar = () => {
|
||||
const resp = ServeEditGroup(params)
|
||||
resp.then(({ code }) => {
|
||||
if (code == 200) {
|
||||
state.isLoading = false
|
||||
groupStore.updateGroupInfo({
|
||||
avatar: data.ori_url,
|
||||
})
|
||||
@ -107,18 +100,13 @@ const editAvatar = () => {
|
||||
// delta: 1,
|
||||
// })
|
||||
} else {
|
||||
state.isLoading = false
|
||||
}
|
||||
})
|
||||
resp.catch(() => {
|
||||
state.isLoading = false})
|
||||
resp.catch(() => {})
|
||||
} else {
|
||||
state.isLoading = false
|
||||
}
|
||||
},
|
||||
).catch(() => {
|
||||
state.isLoading = false
|
||||
})
|
||||
)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
@ -29,13 +29,10 @@
|
||||
:placeholder="$t('edit.groupName.placeholder')"
|
||||
placeholder-style="color:#B4B4B4;font-size:28rpx;font-weight:500;line-height:40rpx;"
|
||||
v-model="state.groupName"
|
||||
@input="handleGroupNameInput"
|
||||
maxlength="20"
|
||||
/>
|
||||
<img
|
||||
v-if="state.groupName"
|
||||
class="groupName-input-clearBtn"
|
||||
src="@/static/image/chatSettings/clear-btn.png"
|
||||
src="/src/static/image/chatSettings/clear-btn.png"
|
||||
@click="clearGroupNameInput"
|
||||
/>
|
||||
</div>
|
||||
@ -85,12 +82,7 @@ onLoad((options) => {
|
||||
const clearGroupNameInput = () => {
|
||||
state.groupName = ''
|
||||
}
|
||||
const handleGroupNameInput = (e) => {
|
||||
if (state.groupName.length > 20) {
|
||||
// 截取前20个字符
|
||||
state.groupName = state.groupName.slice(0, 20)
|
||||
}
|
||||
}
|
||||
|
||||
//点击确认修改
|
||||
const confirmEdit = () => {
|
||||
console.log(state.groupName)
|
||||
@ -154,7 +146,7 @@ const confirmEdit = () => {
|
||||
height: 110rpx;
|
||||
box-shadow: 0 6px 12px 2px rgba(188, 188, 188, 0.08);
|
||||
padding: 0 74rpx 0 32rpx;
|
||||
color: #747474;
|
||||
color: #B747474;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
line-height: 40rpx;
|
||||
|
@ -46,7 +46,7 @@
|
||||
<div class="group-admin-list-each-btns">
|
||||
<img
|
||||
v-if="item.is_mine"
|
||||
src="@/static/image/chatSettings/is-mine.png"
|
||||
src="/src/static/image/chatSettings/is-mine.png"
|
||||
/>
|
||||
<div
|
||||
class="group-admin-list-each-btns-each"
|
||||
@ -67,7 +67,7 @@
|
||||
margin: state?.groupAdminList.length == 0 ? '20rpx 0 0' : '',
|
||||
}"
|
||||
>
|
||||
<img src="@/static/image/chatSettings/add-btn.png" />
|
||||
<img src="/src/static/image/chatSettings/add-btn.png" />
|
||||
<span class="text-[28rpx] font-medium">
|
||||
{{ $t('chat.manage.addAdmin') }}
|
||||
</span>
|
||||
|
@ -1,76 +0,0 @@
|
||||
<template>
|
||||
<div class="outer-layer manage-group-deps-page">
|
||||
<div class="root">
|
||||
<ZPaging
|
||||
ref="zPaging"
|
||||
:show-scrollbar="false"
|
||||
:use-virtual-list="true"
|
||||
:virtual-list-col="5"
|
||||
:auto="false"
|
||||
:refresher-enabled="false"
|
||||
:loading-more-enabled="false"
|
||||
>
|
||||
<template #top>
|
||||
<customNavbar :title="$t('pageTitle.view.deps')"></customNavbar>
|
||||
</template>
|
||||
<div class="group-deps-list">
|
||||
<div
|
||||
class="group-deps-list-each"
|
||||
v-for="item in groupParams.groupInfo.deptInfos"
|
||||
:key="item.dept_id"
|
||||
>
|
||||
<span>{{ item.dept_name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</ZPaging>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import settingFormItem from '../components/settingFormItem.vue'
|
||||
import ZPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { computed, onMounted, reactive } from 'vue'
|
||||
|
||||
import { useGroupStore } from '@/store'
|
||||
|
||||
import { useI18n } from 'vue-i18n'
|
||||
const { t } = useI18n()
|
||||
|
||||
const groupStore = useGroupStore()
|
||||
const groupParams = reactive({
|
||||
groupInfo: computed(() => groupStore.groupInfo),
|
||||
})
|
||||
|
||||
const state = reactive({})
|
||||
|
||||
onLoad((options) => {
|
||||
console.log(options)
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
console.log(groupParams.groupInfo.deptInfos)
|
||||
})
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.outer-layer {
|
||||
flex: 1;
|
||||
background-image: url('@/static/image/clockIn/z3280@3x.png');
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.group-deps-list {
|
||||
margin: 20rpx 32rpx;
|
||||
background-color: #fff;
|
||||
.group-deps-list-each {
|
||||
padding: 34rpx 32rpx;
|
||||
border-bottom: 1px solid $theme-border-color;
|
||||
span {
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
line-height: 40rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -42,7 +42,7 @@
|
||||
class="add-silence-member-btn chat-settings-card"
|
||||
@click="toSelectMembersPage"
|
||||
>
|
||||
<img src="@/static/image/chatSettings/add-btn.png" />
|
||||
<img src="/src/static/image/chatSettings/add-btn.png" />
|
||||
<span class="text-[28rpx] font-medium">
|
||||
{{ $t('chat.manage.addSilenceMember') }}
|
||||
</span>
|
||||
|
@ -14,7 +14,7 @@
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<template #right v-if="state.isManager === 'true'">
|
||||
<template #right>
|
||||
<div
|
||||
v-if="state.editMode !== 3"
|
||||
class="nav-bar-done-btn"
|
||||
@ -39,7 +39,7 @@
|
||||
</template>
|
||||
<div class="notice-text-area">
|
||||
<div class="notice-view-area">
|
||||
<div class="notice-view-info" v-if="state.editMode !== 1 && state?.groupNoticeObj?.content">
|
||||
<div class="notice-view-info" v-if="state.editMode !== 1">
|
||||
<div class="notice-creater-avatar">
|
||||
<img :src="state?.groupNoticeObj?.avatar" />
|
||||
</div>
|
||||
@ -53,12 +53,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="notice-view-content" v-if="state.editMode === 3">
|
||||
<span class="text-[32rpx] font-regular" v-if="state?.groupNoticeObj?.content">
|
||||
<span class="text-[32rpx] font-regular">
|
||||
{{ state?.groupNoticeObj?.content }}
|
||||
</span>
|
||||
<span class="text-[32rpx] font-regular color-[#898989]" v-else>
|
||||
{{ $t('groupNotice.notice.empty') }}
|
||||
</span>
|
||||
</div>
|
||||
<wd-textarea
|
||||
size="large"
|
||||
@ -80,7 +77,6 @@ import useConfirm from '@/components/x-confirm/useConfirm.js'
|
||||
import ZPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
|
||||
import { reactive, computed, onMounted } from 'vue'
|
||||
import { useGroupStore, useDialogueStore } from '@/store'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import {
|
||||
ServeEditGroupNotice,
|
||||
ServeDeleteGroupNotice,
|
||||
@ -102,8 +98,7 @@ const state = reactive({
|
||||
groupNoticeObj: null, //群公告信息
|
||||
groupNotice: '', //群公告
|
||||
canDoComplete: false, //是否可以点击完成按钮
|
||||
editMode: 3, // 1:新增;2:修改;3:查看
|
||||
isManager: 'false', //是否是群管理员
|
||||
editMode: 1, // 1:新增;2:修改;3:查看
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
@ -112,14 +107,6 @@ onMounted(() => {
|
||||
state.editMode = 3
|
||||
state.groupNoticeObj = groupParams.groupNotice[0]
|
||||
inputGroupNotice(groupParams?.groupNotice[0]?.content)
|
||||
} else if( state.isManager === 'true'){
|
||||
state.editMode = 1
|
||||
}
|
||||
})
|
||||
onLoad((options) => {
|
||||
console.log(options)
|
||||
if (options?.is_manager) {
|
||||
state.isManager = options?.is_manager
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -11,45 +11,170 @@
|
||||
@scroll="onScroll"
|
||||
>
|
||||
<template #top>
|
||||
<div id="topArea">
|
||||
<customNavbar :title="pageTitle"></customNavbar>
|
||||
</div>
|
||||
<customNavbar :title="pageTitle" id="topArea"></customNavbar>
|
||||
</template>
|
||||
<selectMemberByAlphabet
|
||||
:manageType="state.manageType"
|
||||
:isCreateDepGroup="state.isCreateDepGroup"
|
||||
ref="selectMemberByAlphabetRef"
|
||||
:selectAreaHeight="state.selectAreaHeight"
|
||||
@updateSelectedMembersNum="updateSelectedMembersNum"
|
||||
></selectMemberByAlphabet>
|
||||
<template #bottom v-if="state.manageType !== 'searchRecord'">
|
||||
<div id="footArea">
|
||||
<customBtn
|
||||
v-if="state.manageType !== 'removeMembers'"
|
||||
:isBottom="true"
|
||||
:btnText="$t('ok')"
|
||||
@clickBtn="confirmSelectMembers"
|
||||
></customBtn>
|
||||
<div
|
||||
class="confirm-btn-area"
|
||||
v-if="state.manageType === 'removeMembers'"
|
||||
>
|
||||
<div class="confirm-btn-area-statistic-text">
|
||||
<span class="text-[28rpx] font-medium">
|
||||
{{
|
||||
$t('select.member.num') +
|
||||
':' +
|
||||
state.selectedMembersNum +
|
||||
$t('statistic.unit.person')
|
||||
}}
|
||||
<div class="select-members">
|
||||
<div
|
||||
class="search-member"
|
||||
v-if="state.manageType !== 'removeMembers'"
|
||||
>
|
||||
<customInput
|
||||
:searchText="state.searchText"
|
||||
@inputSearchText="inputSearchText"
|
||||
></customInput>
|
||||
</div>
|
||||
<div
|
||||
class="member-list"
|
||||
:style="{
|
||||
padding: state.manageType === 'searchRecord' ? '20rpx 0 0' : '',
|
||||
}"
|
||||
>
|
||||
<div class="member-list-alphabet-anchor-point">
|
||||
<div
|
||||
class="member-list-alphabet-anchor-point-each"
|
||||
v-for="(alphabetItem, alphabetIndex) in state?.alphabet"
|
||||
:key="alphabetIndex"
|
||||
:style="{
|
||||
margin: state?.alphabet?.length > 17 ? '0' : '',
|
||||
}"
|
||||
@click.stop="scrollToView(alphabetItem)"
|
||||
>
|
||||
<span
|
||||
class="text-[32rpx] font-regular"
|
||||
:style="{
|
||||
color:
|
||||
state.currentAlphabet === alphabetItem ? '#7A58DE' : '',
|
||||
}"
|
||||
>
|
||||
{{ alphabetItem }}
|
||||
</span>
|
||||
</div>
|
||||
<customBtn
|
||||
:btnText="$t('ok')"
|
||||
@clickBtn="confirmSelectMembers"
|
||||
:disabled="state.selectedMembersNum == 0 ? true : false"
|
||||
></customBtn>
|
||||
</div>
|
||||
<div
|
||||
class="member-list-alphabet"
|
||||
v-for="(alphabetItem, alphabetIndex) in state.resultMemberList"
|
||||
:key="alphabetIndex"
|
||||
>
|
||||
<div
|
||||
class="member-list-alphabet-key"
|
||||
:style="{
|
||||
padding:
|
||||
state.manageType === 'searchRecord' ||
|
||||
state.manageType === 'removeMembers'
|
||||
? '10rpx 30rpx'
|
||||
: '',
|
||||
}"
|
||||
v-if="alphabetItem?.memberList?.length > 0"
|
||||
:id="alphabetItem.key"
|
||||
:ref="
|
||||
(el) => {
|
||||
if (el) alphabetElementRefs[alphabetIndex] = el
|
||||
}
|
||||
"
|
||||
>
|
||||
<span class="text-[32rpx] font-regular">
|
||||
{{ alphabetItem.key }}
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="alphabetItem?.memberList?.length > 0">
|
||||
<div
|
||||
class="member-list-each"
|
||||
v-for="(item, index) in alphabetItem?.memberList"
|
||||
:key="index"
|
||||
>
|
||||
<tm-checkbox-group v-model="item.checkArr">
|
||||
<selectMemberItem
|
||||
:groupType="groupParams.groupInfo.group_type"
|
||||
:memberItem="item"
|
||||
@clickItem="handleClickItem(item)"
|
||||
:manageType="state.manageType"
|
||||
:itemStyle="
|
||||
state.manageType === 'searchRecord' ||
|
||||
state.manageType === 'removeMembers'
|
||||
? 'list'
|
||||
: 'card'
|
||||
"
|
||||
>
|
||||
<template
|
||||
#left
|
||||
v-if="state.manageType !== 'searchRecord'"
|
||||
>
|
||||
<div
|
||||
v-if="
|
||||
state.manageType === 'removeMembers' &&
|
||||
item?.is_mine
|
||||
"
|
||||
>
|
||||
<tm-checkbox
|
||||
color="#fff"
|
||||
:transprent="true"
|
||||
:border="0"
|
||||
:disabled="true"
|
||||
></tm-checkbox>
|
||||
</div>
|
||||
<tm-checkbox
|
||||
v-if="
|
||||
!(
|
||||
state.manageType === 'removeMembers' &&
|
||||
item?.is_mine
|
||||
)
|
||||
"
|
||||
:round="10"
|
||||
:color="
|
||||
item?.checkArr?.length > 0 ? '#46299d' : '#B4B4B4'
|
||||
"
|
||||
:outlined="
|
||||
item?.checkArr?.length > 0 ||
|
||||
(state.manageType === 'silence' &&
|
||||
item.is_mute === 1) ||
|
||||
(state.manageType === 'admin' &&
|
||||
(item.leader === 1 || item.leader === 2))
|
||||
? false
|
||||
: true
|
||||
"
|
||||
:value="item.id"
|
||||
:disabled="
|
||||
(state.manageType === 'silence' &&
|
||||
item.is_mute === 1) ||
|
||||
(state.manageType === 'admin' &&
|
||||
(item.leader === 1 || item.leader === 2))
|
||||
"
|
||||
@change="checkBoxChange"
|
||||
></tm-checkbox>
|
||||
</template>
|
||||
</selectMemberItem>
|
||||
</tm-checkbox-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template #bottom v-if="state.manageType !== 'searchRecord'">
|
||||
<customBtn
|
||||
v-if="state.manageType !== 'removeMembers'"
|
||||
:isBottom="true"
|
||||
:btnText="$t('ok')"
|
||||
@clickBtn="confirmSelectMembers"
|
||||
></customBtn>
|
||||
<div
|
||||
class="confirm-btn-area"
|
||||
v-if="state.manageType === 'removeMembers'"
|
||||
>
|
||||
<div class="confirm-btn-area-statistic-text">
|
||||
<span class="text-[28rpx] font-medium">
|
||||
{{
|
||||
$t('select.member.num') +
|
||||
':' +
|
||||
state.selectedMembersNum +
|
||||
$t('statistic.unit.person')
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
<customBtn
|
||||
:btnText="$t('ok')"
|
||||
@clickBtn="confirmSelectMembers"
|
||||
:disabled="state.selectedMembersNum == 0 ? true : false"
|
||||
></customBtn>
|
||||
</div>
|
||||
</template>
|
||||
</ZPaging>
|
||||
@ -57,63 +182,120 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import selectMemberByAlphabet from '../components/selectMemberByAlphabet.vue'
|
||||
import customInput from '@/components/custom-input/custom-input.vue'
|
||||
import selectMemberItem from '../components/select-member-item.vue'
|
||||
import customBtn from '@/components/custom-btn/custom-btn.vue'
|
||||
import ZPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
|
||||
import useZPaging from '@/uni_modules/z-paging/components/z-paging/js/hooks/useZPaging.js'
|
||||
import { computed, reactive, ref, onMounted, nextTick } from 'vue'
|
||||
import { computed, onMounted, reactive, ref, watch, nextTick } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import {
|
||||
ServeGroupNoSpeak,
|
||||
ServeEditGroupAdmin,
|
||||
ServeGroupAssignAdmin,
|
||||
ServeRemoveMembersGroup,
|
||||
} from '@/api/group/index.js'
|
||||
import { useDialogueStore, useGroupStore, useGroupTypeStore } from '@/store'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
const { t } = useI18n()
|
||||
|
||||
const selectMemberByAlphabetRef = ref(null)
|
||||
|
||||
const zPaging = ref()
|
||||
useZPaging(zPaging)
|
||||
|
||||
const state = reactive({
|
||||
manageType: '', //管理类型
|
||||
selectedMembersNum: 0, //当前选中数量
|
||||
isCreateDepGroup: 0, //是否是创建部门群
|
||||
selectAreaHeight: 0, //选择区域高度
|
||||
const groupStore = useGroupStore()
|
||||
const groupParams = reactive({
|
||||
groupInfo: computed(() => groupStore.groupInfo),
|
||||
})
|
||||
|
||||
const groupTypeStore = useGroupTypeStore()
|
||||
const groupTypeParams = reactive({
|
||||
departmentAllPositions: computed(() => groupTypeStore.departmentAllPositions),
|
||||
})
|
||||
|
||||
const dialogueStore = useDialogueStore()
|
||||
const dialogueParams = reactive({
|
||||
memberList: computed(() => {
|
||||
const lowerCaseSearchText = state?.searchText.toLowerCase()
|
||||
return dialogueStore.members.filter((item) =>
|
||||
state?.searchText
|
||||
? item.nickname.toLowerCase().includes(lowerCaseSearchText)
|
||||
: true,
|
||||
)
|
||||
}),
|
||||
receiverId: computed(() => dialogueStore.talk.receiver_id),
|
||||
})
|
||||
|
||||
const state = reactive({
|
||||
searchText: '', //搜索内容
|
||||
manageType: '', //管理类型
|
||||
alphabet: [], //A-Z列表
|
||||
resultMemberList: [], //按A-Z整理后的人员列表
|
||||
currentAlphabet: 'A', //当前A-Z位置
|
||||
scrollDirection: '', //当前列表滚动方向
|
||||
isAssign: false, //是否指定view
|
||||
selectedMembersNum: 0, //当前选中数量
|
||||
isCreateDepGroup: 0, //是否是创建部门群
|
||||
})
|
||||
|
||||
watch(
|
||||
() => dialogueParams?.memberList,
|
||||
(newMemberList) => {
|
||||
assembleAlphabetMemberList(newMemberList)
|
||||
},
|
||||
{ deep: true },
|
||||
)
|
||||
|
||||
watch(
|
||||
() => groupParams?.groupInfo,
|
||||
(newGroupInfo) => {
|
||||
assembleAlphabetMemberList(dialogueParams?.memberList)
|
||||
},
|
||||
{ deep: true },
|
||||
)
|
||||
|
||||
//获取A-Z tag元素
|
||||
const alphabetElementRefs = ref([])
|
||||
//观察者
|
||||
let observer
|
||||
|
||||
onLoad((options) => {
|
||||
// console.log(options)
|
||||
console.log(options)
|
||||
if (options.manageType) {
|
||||
state.manageType = options.manageType
|
||||
assembleAlphabetMemberList(dialogueParams?.memberList)
|
||||
}
|
||||
if (options.isCreateDepGroup) {
|
||||
state.isCreateDepGroup = Number(options.isCreateDepGroup)
|
||||
assembleAlphabetMemberList()
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
dialogueParams.memberList.forEach((ele) => {
|
||||
ele.checkArr = []
|
||||
})
|
||||
const options = {
|
||||
root: null, // 使用浏览器窗口作为根容器
|
||||
threshold: 1.0, // 当元素100%离开视口时触发
|
||||
}
|
||||
observer = new IntersectionObserver(handleIntersection, options)
|
||||
nextTick(() => {
|
||||
let selectAreaHeight = uni.getSystemInfoSync().windowHeight
|
||||
// console.log('页面高度:', uni.getSystemInfoSync().windowHeight)
|
||||
const topAreaQuery = uni.createSelectorQuery()
|
||||
topAreaQuery
|
||||
.select('#topArea')
|
||||
.boundingClientRect((res) => {
|
||||
if (res) {
|
||||
// console.log('元素高度:', res.height)
|
||||
selectAreaHeight = selectAreaHeight - res.height
|
||||
watch(
|
||||
alphabetElementRefs,
|
||||
(newAlphabetElementRefs) => {
|
||||
if (Array.isArray(newAlphabetElementRefs)) {
|
||||
newAlphabetElementRefs.forEach((el, index) => {
|
||||
observeElement(el, index)
|
||||
})
|
||||
}
|
||||
})
|
||||
.exec()
|
||||
const footAreaQuery = uni.createSelectorQuery()
|
||||
footAreaQuery
|
||||
.select('#footArea')
|
||||
.boundingClientRect((res) => {
|
||||
if (res) {
|
||||
// console.log('元素高度:', res.height)
|
||||
selectAreaHeight = selectAreaHeight - res.height
|
||||
}
|
||||
})
|
||||
.exec()
|
||||
// console.log(selectAreaHeight)
|
||||
state.selectAreaHeight = selectAreaHeight + 'px'
|
||||
},
|
||||
{ immediate: true, deep: true },
|
||||
)
|
||||
if (alphabetElementRefs.value.length > 0) {
|
||||
alphabetElementRefs.value.forEach((el, index) =>
|
||||
observeElement(el, index),
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@ -132,16 +314,341 @@ const pageTitle = computed(() => {
|
||||
return page_title
|
||||
})
|
||||
|
||||
//确认
|
||||
const confirmSelectMembers = () => {
|
||||
if (selectMemberByAlphabetRef.value) {
|
||||
selectMemberByAlphabetRef.value.confirmSelectMembers()
|
||||
// 观察者函数
|
||||
const handleIntersection = (entries) => {
|
||||
if (state.isAssign) {
|
||||
state.isAssign = false
|
||||
return
|
||||
}
|
||||
entries.forEach((entry) => {
|
||||
if (!entry.isIntersecting && state.scrollDirection === 'down') {
|
||||
state.currentAlphabet = entry.target.id
|
||||
} else if (entry.isIntersecting && state.scrollDirection === 'up') {
|
||||
if (state?.alphabet?.length > 1) {
|
||||
state?.alphabet.forEach((item, index) => {
|
||||
if (item === entry.target.id && index > 0) {
|
||||
state.currentAlphabet = state?.alphabet[index - 1]
|
||||
}
|
||||
})
|
||||
} else {
|
||||
state.currentAlphabet = entry.target.id
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//观察元素
|
||||
const observeElement = (el, index) => {
|
||||
if (el && observer) {
|
||||
observer.observe(el)
|
||||
}
|
||||
}
|
||||
|
||||
//更新选中的人数
|
||||
const updateSelectedMembersNum = (numChange) => {
|
||||
state.selectedMembersNum = state.selectedMembersNum + numChange
|
||||
//输入搜索文本
|
||||
const inputSearchText = (e) => {
|
||||
// console.log(e)
|
||||
state.searchText = e
|
||||
}
|
||||
|
||||
//点击item
|
||||
const handleClickItem = (item) => {
|
||||
if (
|
||||
(state.manageType === 'silence' && item.is_mute === 1) ||
|
||||
(state.manageType === 'admin' &&
|
||||
(item.leader === 1 || item.leader === 2)) ||
|
||||
(state.manageType === 'removeMembers' && item.is_mine)
|
||||
) {
|
||||
return
|
||||
}
|
||||
let itemList = dialogueParams.memberList
|
||||
if (
|
||||
state.manageType === 'admin' &&
|
||||
(groupParams.groupInfo.group_type == 2 ||
|
||||
groupParams.groupInfo.group_type == 4 ||
|
||||
state.isCreateDepGroup === 1)
|
||||
) {
|
||||
itemList = state.resultMemberList[0].memberList
|
||||
}
|
||||
itemList.forEach((ele) => {
|
||||
if (ele.id == item.id) {
|
||||
ele.checkArr = ele.checkArr?.length > 0 ? [] : [item.id]
|
||||
if (ele.checkArr?.length > 0) {
|
||||
state.selectedMembersNum += 1
|
||||
} else {
|
||||
state.selectedMembersNum -= 1
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//点击确认选择的成员
|
||||
const confirmSelectMembers = () => {
|
||||
let selectedUserIds = ''
|
||||
let itemList = dialogueParams.memberList
|
||||
let positionInfos = []
|
||||
if (
|
||||
state.manageType === 'admin' &&
|
||||
(groupParams.groupInfo.group_type == 2 ||
|
||||
groupParams.groupInfo.group_type == 4 ||
|
||||
state.isCreateDepGroup === 1)
|
||||
) {
|
||||
itemList = state.resultMemberList[0].memberList
|
||||
}
|
||||
itemList.forEach((ele) => {
|
||||
if (ele.checkArr?.length > 0) {
|
||||
if (!selectedUserIds) {
|
||||
selectedUserIds = String(ele.checkArr[0])
|
||||
} else {
|
||||
selectedUserIds += ',' + ele.checkArr[0]
|
||||
}
|
||||
}
|
||||
if (
|
||||
ele.checkArr?.length > 0 ||
|
||||
(ele.leader && (ele.leader == 1 || ele.leader == 2))
|
||||
) {
|
||||
if (state.isCreateDepGroup === 1) {
|
||||
let posInfo = Object.assign({}, ele.positionInfo, {
|
||||
name: ele.nickname,
|
||||
id: ele.id,
|
||||
})
|
||||
positionInfos.push(posInfo)
|
||||
} else {
|
||||
positionInfos.push(ele.positionInfo)
|
||||
}
|
||||
}
|
||||
})
|
||||
console.log(selectedUserIds)
|
||||
if (selectedUserIds) {
|
||||
if (state.manageType === 'silence') {
|
||||
let params = {
|
||||
mode: 1, //1禁言2解禁
|
||||
group_id: dialogueParams.receiverId, //群id
|
||||
user_ids: selectedUserIds, //用户ids
|
||||
}
|
||||
console.log(params)
|
||||
const resp = ServeGroupNoSpeak(params)
|
||||
resp.then(({ code, data }) => {
|
||||
console.log(data)
|
||||
if (code == 200) {
|
||||
useDialogueStore().updateGroupMembers()
|
||||
} else {
|
||||
}
|
||||
})
|
||||
resp.catch(() => {})
|
||||
} else if (state.manageType === 'admin') {
|
||||
if (state.isCreateDepGroup === 1) {
|
||||
// console.log(positionInfos)
|
||||
groupTypeStore.groupAdmins.value = positionInfos
|
||||
uni.navigateBack({
|
||||
delta: 1,
|
||||
})
|
||||
} else {
|
||||
if (
|
||||
groupParams.groupInfo.group_type == 1 ||
|
||||
groupParams.groupInfo.group_type == 3
|
||||
) {
|
||||
let params = {
|
||||
mode: 1, //1管理员,2不是管理员
|
||||
group_id: dialogueParams.receiverId, //群id
|
||||
user_ids: selectedUserIds,
|
||||
}
|
||||
console.log(params)
|
||||
const resp = ServeGroupAssignAdmin(params)
|
||||
resp.then(({ code, data }) => {
|
||||
console.log(data)
|
||||
if (code == 200) {
|
||||
useDialogueStore().updateGroupMembers()
|
||||
} else {
|
||||
}
|
||||
})
|
||||
resp.catch(() => {})
|
||||
} else if (
|
||||
groupParams.groupInfo.group_type == 2 ||
|
||||
groupParams.groupInfo.group_type == 4
|
||||
) {
|
||||
let params = {
|
||||
source: 'app',
|
||||
id: dialogueParams.receiverId,
|
||||
deptInfos: groupParams.groupInfo.deptInfos,
|
||||
positionInfos: positionInfos,
|
||||
}
|
||||
console.log(params)
|
||||
const resp = ServeEditGroupAdmin(params)
|
||||
resp.then(({ code, data }) => {
|
||||
console.log(data)
|
||||
if (code == 200) {
|
||||
groupStore.ServeGroupDetail()
|
||||
} else {
|
||||
}
|
||||
})
|
||||
resp.catch(() => {})
|
||||
}
|
||||
}
|
||||
} else if (state.manageType === 'removeMembers') {
|
||||
let params = {
|
||||
group_id: dialogueParams.receiverId, //群id
|
||||
members_ids: selectedUserIds, //群成员id,批量的话逗号分割
|
||||
}
|
||||
console.log(params)
|
||||
const resp = ServeRemoveMembersGroup(params)
|
||||
resp.then(({ code, data }) => {
|
||||
console.log(data)
|
||||
if (code == 200) {
|
||||
useDialogueStore().updateGroupMembers()
|
||||
groupStore.ServeGroupDetail()
|
||||
} else {
|
||||
}
|
||||
})
|
||||
resp.catch(() => {})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//组装A-Z排序的人员列表
|
||||
const assembleAlphabetMemberList = async (newMemberList) => {
|
||||
if (
|
||||
state.manageType === 'searchRecord' ||
|
||||
state.manageType === 'removeMembers'
|
||||
) {
|
||||
const resultMemberList = ref([])
|
||||
const alphabet = Array.from({ length: 26 }, (_, i) =>
|
||||
String.fromCharCode(i + 65),
|
||||
)
|
||||
let tempAlphabet = []
|
||||
alphabet.forEach((letter) => {
|
||||
const matchedItems = newMemberList.filter((item) => item.key === letter)
|
||||
if (matchedItems.length > 0) {
|
||||
tempAlphabet.push(letter)
|
||||
}
|
||||
resultMemberList.value.push({
|
||||
key: letter,
|
||||
memberList: matchedItems.length ? matchedItems : [],
|
||||
})
|
||||
})
|
||||
state.alphabet = tempAlphabet
|
||||
state.resultMemberList = resultMemberList
|
||||
} else {
|
||||
if (
|
||||
(groupParams.groupInfo.group_type == 2 ||
|
||||
groupParams.groupInfo.group_type == 4) &&
|
||||
state.manageType === 'admin'
|
||||
) {
|
||||
let departmentIdsArr = []
|
||||
if (groupParams?.groupInfo?.deptInfos?.length > 0) {
|
||||
groupParams.groupInfo.deptInfos.forEach((item) => {
|
||||
departmentIdsArr.push(item.dept_id)
|
||||
})
|
||||
}
|
||||
getPosiByDep(departmentIdsArr)
|
||||
} else if (state.isCreateDepGroup === 1) {
|
||||
let departmentIdsArr = []
|
||||
if (groupTypeStore?.depCheckedKeys?.value?.length > 0) {
|
||||
groupTypeStore.depCheckedKeys.value.forEach((item) => {
|
||||
departmentIdsArr.push(item.ID)
|
||||
})
|
||||
}
|
||||
getPosiByDep(departmentIdsArr)
|
||||
} else {
|
||||
state.resultMemberList = [
|
||||
{
|
||||
key: '',
|
||||
memberList: newMemberList,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const getPosiByDep = async (departmentIdsArr) => {
|
||||
await groupTypeStore.getPositionByDepartment({
|
||||
IDs: departmentIdsArr,
|
||||
})
|
||||
let departmentAllPositions = []
|
||||
if (groupTypeParams?.departmentAllPositions?.value?.length > 0) {
|
||||
groupTypeParams?.departmentAllPositions?.value?.forEach((item) => {
|
||||
item?.AllPositions?.forEach((positionItem) => {
|
||||
departmentAllPositions.push({
|
||||
nickname: item.name + '-' + positionItem.name,
|
||||
id: item.ID + '-' + positionItem.ID,
|
||||
checkArr: [],
|
||||
positionInfo: {
|
||||
dept_id: item.ID,
|
||||
dept_name: item.name,
|
||||
position_id: positionItem.ID,
|
||||
position_name: positionItem.name,
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
if (groupParams?.groupInfo?.groupAdminList?.length > 0) {
|
||||
groupParams?.groupInfo?.groupAdminList.forEach((item) => {
|
||||
departmentAllPositions.forEach((idsItem) => {
|
||||
if (item.dept_id + '-' + item.position_id == idsItem.id) {
|
||||
idsItem.leader = 1
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
if (
|
||||
state.isCreateDepGroup === 1 &&
|
||||
groupTypeStore?.groupAdmins?.value?.length > 0
|
||||
) {
|
||||
departmentAllPositions.forEach((allPos) => {
|
||||
groupTypeStore.groupAdmins.value.forEach((admin) => {
|
||||
if (allPos.id === admin.id) {
|
||||
allPos.checkArr = [allPos.id]
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
if(state?.searchText){
|
||||
const lowerCaseSearchText = state?.searchText.toLowerCase()
|
||||
departmentAllPositions = departmentAllPositions.filter((item) =>
|
||||
state?.searchText
|
||||
? item.nickname.toLowerCase().includes(lowerCaseSearchText)
|
||||
: true,
|
||||
)
|
||||
}
|
||||
state.resultMemberList = [
|
||||
{
|
||||
key: '',
|
||||
memberList: departmentAllPositions,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
//滚动到指定的view
|
||||
const scrollToView = (alphabet) => {
|
||||
state.currentAlphabet = alphabet
|
||||
state.isAssign = true
|
||||
console.log()
|
||||
zPaging.value?.scrollIntoViewById(
|
||||
alphabet,
|
||||
document.getElementById('topArea').clientHeight
|
||||
? document.getElementById('topArea').clientHeight - 1
|
||||
: 80,
|
||||
)
|
||||
}
|
||||
|
||||
//监听列表滚动
|
||||
const onScroll = (e) => {
|
||||
if (e.detail.deltaY < 0) {
|
||||
state.scrollDirection = 'down'
|
||||
} else if (e.detail.deltaY > 0) {
|
||||
state.scrollDirection = 'up'
|
||||
} else {
|
||||
state.scrollDirection = ''
|
||||
}
|
||||
}
|
||||
|
||||
//选中的人员改变
|
||||
const checkBoxChange = (e) => {
|
||||
if (e) {
|
||||
state.selectedMembersNum += 1
|
||||
} else {
|
||||
state.selectedMembersNum -= 1
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
@ -152,6 +659,48 @@ const updateSelectedMembersNum = (numChange) => {
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.select-members {
|
||||
padding: 20rpx 32rpx;
|
||||
.search-member {
|
||||
padding: 22rpx 16rpx;
|
||||
background-color: #fff;
|
||||
}
|
||||
.member-list {
|
||||
.member-list-alphabet-anchor-point {
|
||||
position: fixed;
|
||||
right: 32rpx;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.member-list-alphabet-anchor-point-each {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 0 14rpx;
|
||||
span {
|
||||
width: 52rpx;
|
||||
text-align: center;
|
||||
line-height: 44rpx;
|
||||
color: $theme-text;
|
||||
}
|
||||
}
|
||||
}
|
||||
.member-list-alphabet {
|
||||
.member-list-alphabet-key {
|
||||
background-color: #f3f3f3;
|
||||
span {
|
||||
line-height: 44rpx;
|
||||
color: $theme-text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.confirm-btn-area {
|
||||
background-color: #fff;
|
||||
padding: 14rpx 32rpx 72rpx;
|
||||
|
@ -17,17 +17,10 @@
|
||||
</div>
|
||||
<div class="avatarImg">
|
||||
<avatarModule
|
||||
:mode="props?.data?.group_type === 0 ? 1 : 2"
|
||||
:mode="2"
|
||||
:avatar="props?.data?.avatar"
|
||||
:groupType="props?.data?.group_type"
|
||||
:userName="props?.data?.name"
|
||||
:customStyle="{ width: '96rpx', height: '96rpx' }"
|
||||
:customTextStyle="{
|
||||
fontSize: '32rpx',
|
||||
fontWeight: 'bold',
|
||||
color: '#fff',
|
||||
lineHeight: '44rpx',
|
||||
}"
|
||||
></avatarModule>
|
||||
</div>
|
||||
<div class="chatInfo">
|
||||
@ -37,18 +30,7 @@
|
||||
class="text-[#171717] text-[32rpx] font-medium leading-[44rpx]"
|
||||
>
|
||||
<span>{{ props.data.name }}</span>
|
||||
<span v-if="props.data.talk_type === 2">
|
||||
({{ props.data.group_member_num }})
|
||||
</span>
|
||||
<span v-if="props.data.group_type === 2" class="depTag tag">
|
||||
部门
|
||||
</span>
|
||||
<span v-if="props.data.group_type === 3" class="projectTag tag">
|
||||
项目
|
||||
</span>
|
||||
<span v-if="props.data.group_type === 4" class="companyTag tag">
|
||||
公司
|
||||
</span>
|
||||
<span>({{ props.data.group_member_num }})</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -136,31 +118,16 @@ const cellClick = () => {
|
||||
opacity: 40%;
|
||||
}
|
||||
|
||||
.tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
// margin-left: 10rpx;
|
||||
margin-top: 4rpx;
|
||||
vertical-align: top;
|
||||
height: 38rpx;
|
||||
line-height: 38rpx;
|
||||
padding: 0 10rpx;
|
||||
font-size: 24rpx;
|
||||
border-radius: 6rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
.companyTag {
|
||||
width: 76rpx;
|
||||
height: 38rpx;
|
||||
border: 1px solid #7a58de;
|
||||
font-size: 24rpx;
|
||||
text-align: center;
|
||||
border-radius: 6rpx;
|
||||
color: #7a58de;
|
||||
}
|
||||
.depTag {
|
||||
border: 1px solid #377ec6;
|
||||
color: #377ec6;
|
||||
}
|
||||
.projectTag {
|
||||
border: 1px solid #c1681c;
|
||||
color: #c1681c;
|
||||
font-weight: bold;
|
||||
margin-left: 12rpx;
|
||||
}
|
||||
|
||||
.textEllipsis {
|
||||
|
@ -58,7 +58,6 @@
|
||||
okColor="#FFFFFF"
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
:okText="'发送'"
|
||||
>
|
||||
<template v-slot:title>
|
||||
<div
|
||||
@ -87,7 +86,7 @@
|
||||
<div
|
||||
class="mt-[8rpx] text-[#666666] text-[24rpx] w-[94rpx] truncate"
|
||||
>
|
||||
<span>{{ item.name }}</span>
|
||||
{{ item.name }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="w-full flex items-center justify-start">
|
||||
@ -99,16 +98,6 @@
|
||||
></avatarModule>
|
||||
<div class="ml-[16rpx] text-[#161616] text-[32rpx] font-bold">
|
||||
{{ selectItemsModal[0].name }}
|
||||
<span v-if="selectItemsModal[0].talk_type === 2">({{ selectItemsModal[0].group_member_num }})</span>
|
||||
<span v-if="selectItemsModal[0].group_type === 2" class="depTag tag">
|
||||
部门
|
||||
</span>
|
||||
<span v-if="selectItemsModal[0].group_type === 3" class="projectTag tag">
|
||||
项目
|
||||
</span>
|
||||
<span v-if="selectItemsModal[0].group_type === 4" class="companyTag tag">
|
||||
公司
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -219,7 +208,7 @@ watch(
|
||||
onMounted(() => {
|
||||
talkStore.loadTalkList()
|
||||
console.log(talkStore.talkItems)
|
||||
items.value = lodash.cloneDeep(talkStore.talkItems).filter(item=>item.is_dismiss === 0 && item.is_quit === 0 && ((item.talk_type === 1 && item.receiver_id !== 2) || item.talk_type !== 1))
|
||||
items.value = lodash.cloneDeep(talkStore.talkItems)
|
||||
})
|
||||
onUnmounted(() => {
|
||||
dialogueStore.setForwardType('')
|
||||
@ -252,32 +241,7 @@ onUnmounted(() => {
|
||||
margin-top: 20rpx;
|
||||
background-color: #fff;
|
||||
}
|
||||
.tag{
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
// margin-left: 10rpx;
|
||||
margin-top: 4rpx;
|
||||
vertical-align: top;
|
||||
height: 38rpx;
|
||||
line-height: 38rpx;
|
||||
padding: 0 10rpx;
|
||||
font-size: 24rpx;
|
||||
border-radius: 6rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
.companyTag {
|
||||
border: 1px solid #7a58de;
|
||||
color: #7a58de;
|
||||
}
|
||||
.depTag {
|
||||
border: 1px solid #377ec6;
|
||||
color: #377ec6;
|
||||
}
|
||||
.projectTag {
|
||||
border: 1px solid #c1681c;
|
||||
color: #c1681c;
|
||||
}
|
||||
|
||||
.btnBox {
|
||||
::v-deep .custom-btn-class {
|
||||
padding: 6rpx 24rpx !important;
|
||||
|
@ -15,7 +15,7 @@
|
||||
</div>
|
||||
<div class="mt-[54rpx] w-full h-[872rpx]">
|
||||
<div
|
||||
@click="groupActiveIndex = 0;depCheckedKeys = []"
|
||||
@click="groupActiveIndex = 0"
|
||||
class="groupCard firstPanel"
|
||||
:class="groupActiveIndex === 0 ? 'activePanel' : ''"
|
||||
>
|
||||
@ -33,7 +33,6 @@
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="isHasPermission"
|
||||
@click="groupActiveIndex = 1"
|
||||
class="groupCard secondPanel"
|
||||
:class="groupActiveIndex === 1 ? 'activePanel' : ''"
|
||||
@ -96,8 +95,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="isCreateProjecy"
|
||||
@click="groupActiveIndex = 2;depCheckedKeys = [];"
|
||||
@click="groupActiveIndex = 2"
|
||||
class="groupCard thirdPanel"
|
||||
:class="groupActiveIndex === 2 ? 'activePanel' : ''"
|
||||
>
|
||||
@ -133,36 +131,13 @@
|
||||
import ZPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
|
||||
import customBtn from '@/components/custom-btn/custom-btn.vue'
|
||||
import { ref, watch, computed } from 'vue'
|
||||
import { onShow, onLoad, onUnload } from '@dcloudio/uni-app'
|
||||
import { onShow, onLoad } from '@dcloudio/uni-app'
|
||||
import { useChatList } from '@/store/chatList/index.js'
|
||||
import { useAuth } from '@/store/auth'
|
||||
import { useTalkStore, useUserStore } from '@/store'
|
||||
import { useGroupTypeStore } from '@/store/groupType'
|
||||
import { userHasPermission } from '@/api/deps/index.js'
|
||||
const { groupActiveIndex, depCheckedKeys } = useGroupTypeStore()
|
||||
const { userInfo } = useAuth()
|
||||
onUnload(()=> {
|
||||
|
||||
})
|
||||
const isHasPermission = ref(false)
|
||||
const isCreateProjecy = ref(false)
|
||||
onShow( async() =>{
|
||||
const isHasRes = await userHasPermission({
|
||||
erpUserId: userInfo?.value?.ID,
|
||||
ruleUrl: [
|
||||
"auth_chat_app_create_all_dept",
|
||||
"auth_chat_app_create_limit_dept"
|
||||
]
|
||||
})
|
||||
if (isHasRes.code === 200) {
|
||||
if (isHasRes.data.auth_chat_app_create_all_dept || isHasRes.data.auth_chat_app_create_limit_dept) {
|
||||
isHasPermission.value = true
|
||||
} else{
|
||||
isHasPermission.value = false
|
||||
}
|
||||
isCreateProjecy.value = isHasRes.data.btn_rule_create_project_group
|
||||
}
|
||||
})
|
||||
const { groupActiveIndex, depCheckedKeys } = useGroupTypeStore()
|
||||
|
||||
const confirmBtnStatus = computed(() => {
|
||||
let disabledT = false
|
||||
@ -233,10 +208,10 @@ const handleConfirm = () => {
|
||||
border-radius: 12rpx;
|
||||
&.firstPanel {
|
||||
background-image: url('@/static/image/chatList/zu6033@2x.png');
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
&.secondPanel {
|
||||
background-image: url('@/static/image/chatList/zu6031@2x.png');
|
||||
margin-top: 28rpx;
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
&.thirdPanel {
|
||||
|
@ -404,7 +404,7 @@ const getCurrentMembers = async (depItem) => {
|
||||
departmentId: depItem.ID,
|
||||
status: 'notactive',
|
||||
})
|
||||
if (res.code === 200) {
|
||||
if (res.status === 0) {
|
||||
currentMembers.value = res.data.data.length
|
||||
? res.data.data.map((v) => {
|
||||
return {
|
||||
@ -628,7 +628,7 @@ const handleConfirm = async () => {
|
||||
departmentIds: allCheckedList.value.map((v) => v.ID),
|
||||
status: 'notactive',
|
||||
})
|
||||
if (res.code == 200 && res.data?.data?.length) {
|
||||
if (res.status == 0 && res.data?.data?.length) {
|
||||
res.data?.data.forEach((v) => {
|
||||
listT.push(v)
|
||||
})
|
||||
|
@ -1,366 +0,0 @@
|
||||
<template>
|
||||
<div class="outer-layer user-detail-page">
|
||||
<div class="root">
|
||||
<ZPaging ref="zPaging" :show-scrollbar="false">
|
||||
<template #top>
|
||||
<customNavbar :title="$t('complaint.title')"></customNavbar>
|
||||
</template>
|
||||
<!-- 投诉主体内容 -->
|
||||
<view class="complaint-container">
|
||||
<!-- 投诉类型选择 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">{{ $t('complaint.selectType') }}</text>
|
||||
<picker mode="selector" :range="complaintTypes" range-key="label" @change="handleTypeChange">
|
||||
<view class="picker">
|
||||
{{ selectedType.label || $t('complaint.selectPlaceholder') }}
|
||||
<uni-icons type="arrowright" size="16" color="#999"></uni-icons>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
|
||||
<!-- 图片证据上传 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">{{ $t('complaint.imageEvidence') }}</text>
|
||||
<view class="upload-area">
|
||||
<view v-for="(img, index) in imageList" :key="index" class="image-wrapper">
|
||||
<image :src="img" mode="aspectFill" class="uploaded-image" @click="previewImage(index)" />
|
||||
<uni-icons type="close" size="18" color="#fff" class="delete-icon"
|
||||
@click="removeImage(index)"></uni-icons>
|
||||
</view>
|
||||
<view v-if="imageList.length < 9" class="upload-btn" @click="chooseImage">
|
||||
<uni-icons type="plusempty" size="28" color="#999"></uni-icons>
|
||||
<text class="upload-text">{{ $t('complaint.addImage') }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 投诉内容 -->
|
||||
<view class="form-item">
|
||||
<text class="form-label">{{ $t('complaint.complaintContent') }}</text>
|
||||
<textarea v-model="complaintContent" :placeholder="$t('complaint.contentPlaceholder')"
|
||||
class="content-textarea"></textarea>
|
||||
</view>
|
||||
|
||||
<!-- 投诉须知 -->
|
||||
<view class="notice-box">
|
||||
<view class="notice-header" @click="toggleNotice">
|
||||
<text class="notice-title">{{ $t('complaint.noticeTitle') }}</text>
|
||||
<text class="toggle-btn">
|
||||
{{ isNoticeExpanded ? $t('complaint.collapse') : $t('complaint.expand') }}
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<!-- 折叠状态只显示前两项 -->
|
||||
<view class="notice-content" v-if="!isNoticeExpanded">
|
||||
<view class="notice-item">
|
||||
<text>{{ $t('complaint.noticeone') }}</text>
|
||||
</view>
|
||||
<view class="notice-item">
|
||||
<text>{{ $t('complaint.noticetwo') }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 展开状态显示全部 -->
|
||||
<view class="notice-content" v-else>
|
||||
<view class="notice-item">
|
||||
<text>{{ $t('complaint.noticeone') }}</text>
|
||||
</view>
|
||||
<view class="notice-item">
|
||||
<text>{{ $t('complaint.noticetwo') }}</text>
|
||||
</view>
|
||||
<view class="notice-item">
|
||||
<text>{{ $t('complaint.noticethree') }}</text>
|
||||
</view>
|
||||
<view class="notice-item">
|
||||
<text>{{ $t('complaint.noticefour') }}</text>
|
||||
</view>
|
||||
<view class="notice-item">
|
||||
<text>{{ $t('complaint.noticefive') }}</text>
|
||||
</view>
|
||||
<view class="notice-item">
|
||||
<text>{{ $t('complaint.noticenoticeContent') }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<button class="submit-btn" :disabled="!selectedType.value" @click="handleSubmit">
|
||||
{{ $t('complaint.submit') }}
|
||||
</button>
|
||||
</view>
|
||||
</ZPaging>
|
||||
|
||||
<!-- 固定在底部的提交按钮 -->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
<script setup>
|
||||
import ZPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
|
||||
import {
|
||||
ref
|
||||
} from 'vue';
|
||||
import {
|
||||
onLoad
|
||||
} from '@dcloudio/uni-app';
|
||||
import {
|
||||
useI18n
|
||||
} from 'vue-i18n';
|
||||
|
||||
const {
|
||||
t
|
||||
} = useI18n();
|
||||
|
||||
|
||||
// 投诉类型选项
|
||||
const complaintTypes = computed(() => [{
|
||||
label: t('complaint.typeOptions.porn'),
|
||||
value: 'porn'
|
||||
},
|
||||
{
|
||||
label: t('complaint.typeOptions.illegal'),
|
||||
value: 'illegal'
|
||||
},
|
||||
{
|
||||
label: t('complaint.typeOptions.gambling'),
|
||||
value: 'gambling'
|
||||
},
|
||||
{
|
||||
label: t('complaint.typeOptions.violence'),
|
||||
value: 'violence'
|
||||
},
|
||||
{
|
||||
label: t('complaint.typeOptions.selfHarm'),
|
||||
value: 'selfHarm'
|
||||
},
|
||||
{
|
||||
label: t('complaint.typeOptions.other'),
|
||||
value: 'other'
|
||||
}
|
||||
]);
|
||||
|
||||
// 表单数据
|
||||
const selectedType = ref({});
|
||||
const imageList = ref([]);
|
||||
const complaintContent = ref('');
|
||||
const isNoticeExpanded = ref(false);
|
||||
|
||||
// 切换投诉须知展开状态
|
||||
const toggleNotice = () => {
|
||||
isNoticeExpanded.value = !isNoticeExpanded.value;
|
||||
};
|
||||
|
||||
// 选择投诉类型
|
||||
const handleTypeChange = (e) => {
|
||||
selectedType.value = complaintTypes.value[e.detail.value];
|
||||
};
|
||||
|
||||
// 选择图片
|
||||
const chooseImage = () => {
|
||||
uni.chooseImage({
|
||||
count: 9 - imageList.value.length,
|
||||
sizeType: ['compressed'],
|
||||
sourceType: ['album', 'camera'],
|
||||
success: (res) => {
|
||||
imageList.value = [...imageList.value, ...res.tempFilePaths];
|
||||
if (imageList.value.length > 9) {
|
||||
uni.showToast({
|
||||
title: `最多只能选择9张图片`,
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 删除图片
|
||||
const removeImage = (index) => {
|
||||
imageList.value.splice(index, 1);
|
||||
};
|
||||
|
||||
// 预览图片
|
||||
const previewImage = (index) => {
|
||||
uni.previewImage({
|
||||
current: index,
|
||||
urls: imageList.value
|
||||
});
|
||||
};
|
||||
|
||||
// 提交投诉
|
||||
const handleSubmit = () => {
|
||||
const formData = {
|
||||
type: selectedType.value,
|
||||
images: imageList.value,
|
||||
content: complaintContent.value
|
||||
};
|
||||
uni.showLoading({
|
||||
title: t('complaint.toast.submitting') // 使用国际化文本
|
||||
});
|
||||
setTimeout(() => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: t('complaint.toast.success'),
|
||||
icon: 'success'
|
||||
});
|
||||
selectedType.value = {};
|
||||
imageList.value = [];
|
||||
complaintContent.value = '';
|
||||
// 返回上一页
|
||||
setTimeout(() => {
|
||||
uni.navigateBack();
|
||||
}, 1500);
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
onLoad(() => {
|
||||
// 页面加载时初始化
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
::v-deep .uni-picker-action-confirm {
|
||||
color: #452aa1 !important;
|
||||
}
|
||||
|
||||
.outer-layer {
|
||||
flex: 1;
|
||||
background-image: url('@/static/image/mine/1111.png');
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.complaint-container {
|
||||
padding: 20rpx 30rpx;
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
margin: 20rpx;
|
||||
border-radius: 16rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.form-item {
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.picker {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 24rpx;
|
||||
background-color: #f7f7f7;
|
||||
border-radius: 12rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.upload-area {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.upload-btn {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #f7f7f7;
|
||||
border-radius: 12rpx;
|
||||
border: 1rpx dashed #ddd;
|
||||
}
|
||||
|
||||
.upload-text {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
.image-wrapper {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
position: relative;
|
||||
border-radius: 12rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.uploaded-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.delete-icon {
|
||||
position: absolute;
|
||||
top: 8rpx;
|
||||
right: 8rpx;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
border-radius: 50%;
|
||||
padding: 4rpx;
|
||||
}
|
||||
|
||||
.content-textarea {
|
||||
width: 100%;
|
||||
height: 200rpx;
|
||||
padding: 20rpx;
|
||||
background-color: #f7f7f7;
|
||||
border-radius: 12rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.notice-box {
|
||||
padding: 20rpx;
|
||||
background-color: #f0f7ff;
|
||||
border-radius: 12rpx;
|
||||
margin: 40rpx 0;
|
||||
}
|
||||
|
||||
.notice-title {
|
||||
font-size: 28rpx;
|
||||
color: #452aa1;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.notice-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
|
||||
.toggle-btn {
|
||||
color: #452aa1;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.notice-content {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
padding-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
margin-top: 40rpx;
|
||||
background-color: #452aa1;
|
||||
color: white;
|
||||
border-radius: 8rpx;
|
||||
height: 90rpx;
|
||||
line-height: 90rpx;
|
||||
font-size: 32rpx;
|
||||
|
||||
&[disabled] {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -8,8 +8,12 @@
|
||||
<div class="group-avatar flex items-center justify-center">
|
||||
<div class="avatar-placeholder" v-if="groupActiveIndex === -1"></div>
|
||||
<div v-else>
|
||||
<avatarModule :mode="2" :avatar="avatarImg" :groupType="groupType"
|
||||
:customStyle="{ width: '192rpx', height: '192rpx' }"></avatarModule>
|
||||
<avatarModule
|
||||
:mode="2"
|
||||
:avatar="avatarImg"
|
||||
:groupType="groupType"
|
||||
:customStyle="{ width: '192rpx', height: '192rpx' }"
|
||||
></avatarModule>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group flex items-center justify-between">
|
||||
@ -17,12 +21,25 @@
|
||||
群名称
|
||||
</div>
|
||||
<div class="input-box">
|
||||
<tm-input v-model="groupName" :followTheme="false" fontColor="#747474" placeholderStyle="color: #B4B4B4"
|
||||
focusColor="#FFF" :fontSize="28" :maxlength="20" :height="40" :transprent="true"
|
||||
placeholder="请输入群名称(1~20个字)" :padding="[0, 0]" align="right"></tm-input>
|
||||
<tm-input
|
||||
v-model="groupName"
|
||||
:followTheme="false"
|
||||
fontColor="#747474"
|
||||
placeholderStyle="color: #B4B4B4"
|
||||
focusColor="#FFF"
|
||||
:fontSize="28"
|
||||
:maxlength="20"
|
||||
:height="40"
|
||||
:transprent="true"
|
||||
placeholder="请输入群名称(1~20个字)"
|
||||
:padding="[0, 0]"
|
||||
align="right"
|
||||
></tm-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group w-full flex flex-col mt-[20rpx] leading-[40rpx]">
|
||||
<div
|
||||
class="input-group w-full flex flex-col mt-[20rpx] leading-[40rpx]"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="input-item">
|
||||
群类型
|
||||
@ -35,310 +52,344 @@
|
||||
<span v-else-if="groupActiveIndex === 2">项目群</span>
|
||||
</div>
|
||||
<div class="ml-[32rpx]">
|
||||
<tm-icon :font-size="22" color="#747474" name="tmicon-angle-right"></tm-icon>
|
||||
<tm-icon
|
||||
:font-size="22"
|
||||
color="#747474"
|
||||
name="tmicon-angle-right"
|
||||
></tm-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="depCheckedKeys.length && groupActiveIndex === 1" class="mt-[32rpx]">
|
||||
<div v-for="(item, index) in depCheckedKeys" class="text-[#747474] text-[28rpx] leading-[40rpx] font-bold"
|
||||
<div
|
||||
v-if="depCheckedKeys.length && groupActiveIndex === 1"
|
||||
class="mt-[32rpx]"
|
||||
>
|
||||
<div
|
||||
v-for="(item, index) in depCheckedKeys"
|
||||
class="text-[#747474] text-[28rpx] leading-[40rpx] font-bold"
|
||||
:class="[
|
||||
index !== 0 ? 'mt-[10rpx]' : '',
|
||||
depsNoExpanded_1 && index > 4 ? 'hidden' : '',
|
||||
]">
|
||||
depsNoExpanded && index > 4 ? 'hidden' : '',
|
||||
]"
|
||||
>
|
||||
{{ item.name }}
|
||||
</div>
|
||||
<div class="text-[#46299D] text-[28rpx] mt-[20rpx] font-bold flex justify-center">
|
||||
<div v-if="depCheckedKeys.length > 5" @click="depsNoExpanded_1 = !depsNoExpanded_1" class="w-[100rpx]">
|
||||
{{ depsNoExpanded_1 ? '展开' : '收起' }}
|
||||
<div
|
||||
class="text-[#46299D] text-[28rpx] mt-[20rpx] font-bold flex justify-center"
|
||||
>
|
||||
<div
|
||||
v-if="depCheckedKeys.length > 5"
|
||||
@click="depsNoExpanded = !depsNoExpanded"
|
||||
class="w-[100rpx]"
|
||||
>
|
||||
{{ depsNoExpanded ? '展开' : '收起' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="groupActiveIndex === 0 || groupActiveIndex === 2"
|
||||
class="input-group w-full flex flex-col mt-[20rpx] leading-[40rpx]">
|
||||
<div
|
||||
v-if="groupActiveIndex === 0 || groupActiveIndex === 2"
|
||||
class="input-group w-full flex flex-col mt-[20rpx] leading-[40rpx]"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="input-item">
|
||||
群成员
|
||||
</div>
|
||||
<div @click="chooseMembers" class="left-box">
|
||||
<div class="ml-[32rpx] flex items-center">
|
||||
<div class="text-[#B4B4B4] text-[28rpx] font-bold mr-[32rpx]">
|
||||
<div
|
||||
class="text-[#B4B4B4] text-[28rpx] font-bold mr-[32rpx]"
|
||||
>
|
||||
全部({{ allChooseMembers?.length || 0 }})
|
||||
</div>
|
||||
<tm-icon :font-size="22" color="#747474" name="tmicon-angle-right"></tm-icon>
|
||||
<tm-icon
|
||||
:font-size="22"
|
||||
color="#747474"
|
||||
name="tmicon-angle-right"
|
||||
></tm-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<groupMemberList :groupType="3" :is_manager="true" :memberList="allChooseMembers" :memberListsLimit="15"
|
||||
:hideAddRemoveBtns="true"></groupMemberList>
|
||||
<groupMemberList
|
||||
:groupType="3"
|
||||
:is_manager="true"
|
||||
:memberList="allChooseMembers"
|
||||
:memberListsLimit="15"
|
||||
:hideAddRemoveBtns="true"
|
||||
></groupMemberList>
|
||||
</div>
|
||||
|
||||
<div v-if="groupActiveIndex === 1" class="input-group w-full flex flex-col mt-[20rpx] leading-[40rpx]">
|
||||
<div
|
||||
v-if="groupActiveIndex === 1"
|
||||
class="input-group w-full flex flex-col mt-[20rpx] leading-[40rpx]"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="input-item">
|
||||
群管理员
|
||||
</div>
|
||||
<div @click="chooseGroupAdmin" class="left-box">
|
||||
<div class="ml-[32rpx] flex items-center">
|
||||
<div v-if="!groupAdmins.length" class="text-[#B4B4B4] text-[28rpx] font-bold mr-[32rpx]">
|
||||
<div
|
||||
v-if="!groupAdmins.length"
|
||||
class="text-[#B4B4B4] text-[28rpx] font-bold mr-[32rpx]"
|
||||
>
|
||||
请选择群管理员
|
||||
</div>
|
||||
<tm-icon :font-size="22" color="#747474" name="tmicon-angle-right"></tm-icon>
|
||||
<tm-icon
|
||||
:font-size="22"
|
||||
color="#747474"
|
||||
name="tmicon-angle-right"
|
||||
></tm-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="groupAdmins.length" class="mt-[32rpx]">
|
||||
<div v-for="(item, index) in groupAdmins" class="text-[#747474] text-[28rpx] leading-[40rpx] font-bold"
|
||||
<div
|
||||
v-for="(item, index) in groupAdmins"
|
||||
class="text-[#747474] text-[28rpx] leading-[40rpx] font-bold"
|
||||
:class="[
|
||||
index !== 0 ? 'mt-[10rpx]' : '',
|
||||
depsNoExpanded_2 && index > 4 ? 'hidden' : '',
|
||||
]">
|
||||
depsNoExpanded && index > 4 ? 'hidden' : '',
|
||||
]"
|
||||
>
|
||||
{{ item.name }}
|
||||
</div>
|
||||
<div class="text-[#46299D] text-[28rpx] mt-[20rpx] font-bold flex justify-center">
|
||||
<div v-if="groupAdmins.length > 5" @click="depsNoExpanded_2 = !depsNoExpanded_2" class="w-[100rpx]">
|
||||
{{ depsNoExpanded_2 ? '展开' : '收起' }}
|
||||
<div
|
||||
class="text-[#46299D] text-[28rpx] mt-[20rpx] font-bold flex justify-center"
|
||||
>
|
||||
<div
|
||||
v-if="groupAdmins.length > 5"
|
||||
@click="depsNoExpanded = !depsNoExpanded"
|
||||
class="w-[100rpx]"
|
||||
>
|
||||
{{ depsNoExpanded ? '展开' : '收起' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template #bottom>
|
||||
<customBtn :isBottom="true" :btnText="$t('pageTitle.create.group')" @click="handleConfirm"
|
||||
:isLoading="isLoading" :disabled="confirmBtnStatus || isLoading"></customBtn>
|
||||
<customBtn
|
||||
:isBottom="true"
|
||||
:btnText="$t('pageTitle.create.group')"
|
||||
@click="handleConfirm"
|
||||
:disabled="confirmBtnStatus"
|
||||
></customBtn>
|
||||
</template>
|
||||
</zPaging>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import zPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
|
||||
import customBtn from '@/components/custom-btn/custom-btn.vue'
|
||||
import groupMemberList from '../chatSettings/components/groupMembersList.vue'
|
||||
import avatarModule from '@/components/avatar-module/index.vue'
|
||||
import zPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
|
||||
import customBtn from '@/components/custom-btn/custom-btn.vue'
|
||||
import groupMemberList from '../chatSettings/components/groupMembersList.vue'
|
||||
import avatarModule from '@/components/avatar-module/index.vue'
|
||||
|
||||
import {
|
||||
ref,
|
||||
watch,
|
||||
computed,
|
||||
onMounted
|
||||
} from 'vue'
|
||||
import {
|
||||
onShow,
|
||||
onLoad,
|
||||
onUnload
|
||||
} from '@dcloudio/uni-app'
|
||||
import {
|
||||
useChatList
|
||||
} from '@/store/chatList/index.js'
|
||||
import {
|
||||
useAuth
|
||||
} from '@/store/auth'
|
||||
import {
|
||||
useTalkStore,
|
||||
useUserStore,
|
||||
useGroupStore
|
||||
} from '@/store'
|
||||
import addCircle from '@/static/image/chatList/addCircle.png'
|
||||
import cahtPopover from '@/static/image/chatList/cahtPopover.png'
|
||||
import {
|
||||
ServeCreateGroup
|
||||
} from '@/api/group/index'
|
||||
import {
|
||||
useGroupTypeStore
|
||||
} from '@/store/groupType'
|
||||
import {
|
||||
handleSetWebviewStyle
|
||||
} from '@/utils/common'
|
||||
import { ref, watch, computed } from 'vue'
|
||||
import { onShow, onLoad } from '@dcloudio/uni-app'
|
||||
import { useChatList } from '@/store/chatList/index.js'
|
||||
import { useAuth } from '@/store/auth'
|
||||
import { useTalkStore, useUserStore, useGroupStore } from '@/store'
|
||||
import addCircle from '@/static/image/chatList/addCircle.png'
|
||||
import cahtPopover from '@/static/image/chatList/cahtPopover.png'
|
||||
import { ServeCreateGroup } from '@/api/group/index'
|
||||
import { useGroupTypeStore } from '@/store/groupType'
|
||||
|
||||
const {
|
||||
groupName,
|
||||
groupActiveIndex,
|
||||
depCheckedKeys,
|
||||
groupAdmins,
|
||||
createDepGroup,
|
||||
resetGroupInfo,
|
||||
allChooseMembers,
|
||||
} = useGroupTypeStore()
|
||||
const talkStore = useTalkStore()
|
||||
const userStore = useUserStore()
|
||||
const groupStore = useGroupStore()
|
||||
const {
|
||||
userInfo
|
||||
} = useAuth()
|
||||
const {
|
||||
groupName,
|
||||
groupActiveIndex,
|
||||
depCheckedKeys,
|
||||
groupAdmins,
|
||||
createDepGroup,
|
||||
resetGroupInfo,
|
||||
allChooseMembers,
|
||||
} = useGroupTypeStore()
|
||||
const talkStore = useTalkStore()
|
||||
const userStore = useUserStore()
|
||||
const groupStore = useGroupStore()
|
||||
const { userInfo } = useAuth()
|
||||
|
||||
const groupChatType = ref('')
|
||||
const depsNoExpanded_1 = ref(true)
|
||||
const depsNoExpanded_2 = ref(true)
|
||||
const groupChatType = ref('')
|
||||
const depsNoExpanded = ref(true)
|
||||
|
||||
onLoad(()=> {
|
||||
groupStore.$reset()
|
||||
})
|
||||
|
||||
onLoad(() => {
|
||||
groupStore.$reset()
|
||||
})
|
||||
onUnload(() => {
|
||||
resetGroupInfo();
|
||||
})
|
||||
onMounted(() => {
|
||||
handleSetWebviewStyle()
|
||||
//群类型
|
||||
const groupType = computed(() => {
|
||||
let group_type = ''
|
||||
switch (groupActiveIndex.value) {
|
||||
case 0:
|
||||
group_type = 1
|
||||
break
|
||||
case 1:
|
||||
group_type = 2
|
||||
break
|
||||
case 2:
|
||||
group_type = 3
|
||||
break
|
||||
default:
|
||||
group_type = ''
|
||||
}
|
||||
return group_type
|
||||
})
|
||||
|
||||
//点击跳转到选择群类型页面
|
||||
const chooseGroupType = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/chooseGroupType/index',
|
||||
})
|
||||
}
|
||||
|
||||
//群类型
|
||||
const groupType = computed(() => {
|
||||
let group_type = ''
|
||||
switch (groupActiveIndex.value) {
|
||||
case 0:
|
||||
group_type = 1
|
||||
break
|
||||
case 1:
|
||||
group_type = 2
|
||||
break
|
||||
case 2:
|
||||
group_type = 3
|
||||
break
|
||||
default:
|
||||
group_type = ''
|
||||
const chooseGroupAdmin = () => {
|
||||
uni.navigateTo({
|
||||
url:
|
||||
'/pages/chatSettings/groupManage/selectMembers?manageType=admin&isCreateDepGroup=1',
|
||||
})
|
||||
}
|
||||
|
||||
const chooseMembers = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/chooseByDeps/index?chooseMode=2',
|
||||
})
|
||||
}
|
||||
|
||||
//点击发起群聊
|
||||
const handleConfirm = async () => {
|
||||
// console.log(allChooseMembers.value)
|
||||
let erp_ids = ''
|
||||
if (allChooseMembers?.value?.length > 0) {
|
||||
allChooseMembers?.value?.forEach((ele) => {
|
||||
if (!erp_ids) {
|
||||
erp_ids = String(ele.ID)
|
||||
} else {
|
||||
erp_ids += ',' + ele.ID
|
||||
}
|
||||
})
|
||||
}
|
||||
if (groupActiveIndex.value === 0) {
|
||||
//普通群
|
||||
let params = {
|
||||
avatar: '',
|
||||
name: groupName.value,
|
||||
erp_ids: erp_ids,
|
||||
type: 1,
|
||||
profile: '',
|
||||
}
|
||||
return group_type
|
||||
})
|
||||
|
||||
//点击跳转到选择群类型页面
|
||||
const chooseGroupType = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/chooseGroupType/index',
|
||||
})
|
||||
}
|
||||
|
||||
const chooseGroupAdmin = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/chatSettings/groupManage/selectMembers?manageType=admin&isCreateDepGroup=1',
|
||||
})
|
||||
}
|
||||
|
||||
const chooseMembers = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/chooseByDeps/index?chooseMode=2',
|
||||
})
|
||||
}
|
||||
const isLoading = ref(false)
|
||||
|
||||
// 点击发起群聊
|
||||
const handleConfirm = async () => {
|
||||
if (isLoading.value) return
|
||||
isLoading.value = true
|
||||
|
||||
try {
|
||||
let erp_ids = ''
|
||||
if (allChooseMembers?.value?.length > 0) {
|
||||
allChooseMembers.value.forEach((ele) => {
|
||||
if (!erp_ids) {
|
||||
erp_ids = String(ele.ID)
|
||||
} else {
|
||||
erp_ids += ',' + ele.ID
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let res = null
|
||||
|
||||
if (groupActiveIndex.value === 0) {
|
||||
// 普通群
|
||||
const params = {
|
||||
avatar: '',
|
||||
name: groupName.value,
|
||||
erp_ids: erp_ids,
|
||||
type: 1,
|
||||
profile: '',
|
||||
}
|
||||
console.log('普通群参数:', params)
|
||||
res = await ServeCreateGroup(params)
|
||||
} else if (groupActiveIndex.value === 1) {
|
||||
// 部门群
|
||||
res = await createDepGroup()
|
||||
} else if (groupActiveIndex.value === 2) {
|
||||
// 项目群
|
||||
const params = {
|
||||
avatar: '',
|
||||
name: groupName.value,
|
||||
erp_ids: erp_ids,
|
||||
type: 3,
|
||||
profile: '',
|
||||
}
|
||||
console.log('项目群参数:', params)
|
||||
res = await ServeCreateGroup(params)
|
||||
}
|
||||
|
||||
if (res?.code === 200) {
|
||||
resetGroupInfo()
|
||||
uni.navigateBack()
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
console.log(params)
|
||||
const res = await ServeCreateGroup(params)
|
||||
if (res.code === 200) {
|
||||
resetGroupInfo()
|
||||
uni.navigateBack()
|
||||
}
|
||||
} else if (groupActiveIndex.value === 1) {
|
||||
//部门群
|
||||
const res = await createDepGroup()
|
||||
if (res.code === 200) {
|
||||
resetGroupInfo()
|
||||
uni.navigateBack()
|
||||
}
|
||||
} else if (groupActiveIndex.value === 2) {
|
||||
//项目群
|
||||
let params = {
|
||||
avatar: '',
|
||||
name: groupName.value,
|
||||
erp_ids: erp_ids,
|
||||
type: 3,
|
||||
profile: '',
|
||||
}
|
||||
console.log(params)
|
||||
const res = await ServeCreateGroup(params)
|
||||
if (res.code === 200) {
|
||||
resetGroupInfo()
|
||||
uni.navigateBack()
|
||||
}
|
||||
} else {
|
||||
}
|
||||
//发起群聊按钮可点击状态
|
||||
const confirmBtnStatus = computed(() => {
|
||||
return groupActiveIndex.value === -1;
|
||||
});
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
depsNoExpanded_1.value = true;
|
||||
depsNoExpanded_2.value = true;
|
||||
})
|
||||
//发起群聊按钮可点击状态
|
||||
const confirmBtnStatus = computed(() => {
|
||||
let disabledT = false
|
||||
console.log(groupActiveIndex.value !== -1)
|
||||
if (
|
||||
groupName.value === '' ||
|
||||
(groupActiveIndex.value && groupActiveIndex.value === -1) ||
|
||||
(!groupActiveIndex.value && groupActiveIndex.value !== 0)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
switch (groupActiveIndex.value) {
|
||||
case 0:
|
||||
break
|
||||
case 1:
|
||||
if (!depCheckedKeys.value.length) {
|
||||
disabledT = true
|
||||
}
|
||||
if (!groupAdmins.value.length) {
|
||||
disabledT = true
|
||||
}
|
||||
break
|
||||
case 2:
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
return disabledT
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
depsNoExpanded.value = true
|
||||
})
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
::v-deep .zp-paging-container-content {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
}
|
||||
::v-deep .zp-paging-container-content {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.create-group-chat {
|
||||
background-image: url('@/static/image/clockIn/z3280@3x.png');
|
||||
background-size: cover;
|
||||
background-position: center bottom;
|
||||
width: 100%;
|
||||
padding: 0 32rpx 20rpx;
|
||||
|
||||
.group-avatar {
|
||||
padding: 60rpx 0;
|
||||
|
||||
.avatar-placeholder {
|
||||
width: 192rpx;
|
||||
height: 192rpx;
|
||||
background-color: #e0e0e0;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.create-group-chat {
|
||||
background-image: url('@/static/image/clockIn/z3280@3x.png');
|
||||
background-size: cover;
|
||||
background-position: center bottom;
|
||||
width: 100%;
|
||||
padding: 0 32rpx 20rpx;
|
||||
.group-avatar {
|
||||
padding: 60rpx 0;
|
||||
.avatar-placeholder {
|
||||
width: 192rpx;
|
||||
height: 192rpx;
|
||||
background-color: #e0e0e0;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
.divider {
|
||||
height: 1rpx;
|
||||
background-color: #7c7c7c;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.divider {
|
||||
height: 1rpx;
|
||||
background-color: #7c7c7c;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
background-color: #fff;
|
||||
padding: 38rpx 40rpx 32rpx 32rpx;
|
||||
}
|
||||
|
||||
.input-item {
|
||||
line-height: 40rpx;
|
||||
font-size: 28rpx;
|
||||
color: #000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.input-box {
|
||||
margin-left: 84rpx;
|
||||
line-height: 40rpx;
|
||||
width: 404rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.left-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.input-group {
|
||||
background-color: #fff;
|
||||
padding: 38rpx 40rpx 32rpx 32rpx;
|
||||
}
|
||||
.input-item {
|
||||
line-height: 40rpx;
|
||||
font-size: 28rpx;
|
||||
color: #000;
|
||||
font-weight: bold;
|
||||
}
|
||||
.input-box {
|
||||
margin-left: 84rpx;
|
||||
line-height: 40rpx;
|
||||
width: 404rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
.left-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,34 +1,25 @@
|
||||
<template>
|
||||
<div>
|
||||
<zPaging :fixed="false" :height="'210px'" :show-scrollbar="false">
|
||||
<div class="emojiRoot">
|
||||
<div
|
||||
v-for="(img, key) in emojis"
|
||||
v-html="img"
|
||||
:key="key"
|
||||
@click="onSendEmoticon(1, key, img)"
|
||||
class="option pointer flex-center"
|
||||
/>
|
||||
</div>
|
||||
</zPaging>
|
||||
<div class="emojiRoot">
|
||||
<div v-for="(img, key) in emojis" v-html="img" :key="key" @click="onSendEmoticon(1, key, img)"
|
||||
class="option pointer flex-center" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import zPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
|
||||
import { ref, reactive, defineProps, defineEmits } from 'vue'
|
||||
import dayjs from 'dayjs'
|
||||
import { ref, reactive, defineProps,defineEmits } from "vue"
|
||||
import dayjs from "dayjs";
|
||||
import { beautifyTime } from '@/utils/datetime'
|
||||
import { useTalkStore } from '@/store'
|
||||
import { useSessionMenu } from '@/hooks'
|
||||
import { emojis } from '@/utils/emojis'
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: {},
|
||||
required: false,
|
||||
},
|
||||
})
|
||||
});
|
||||
|
||||
const emit = defineEmits(['on-select'])
|
||||
const onSendEmoticon = (type, value, img = '') => {
|
||||
@ -42,15 +33,21 @@ const onSendEmoticon = (type, value, img = '') => {
|
||||
emit('on-select', { type, value, img })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.emojiRoot {
|
||||
width: 100%;
|
||||
height: 420rpx;
|
||||
padding: 5rpx 32rpx;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.option {
|
||||
|
@ -1,39 +1,25 @@
|
||||
<template>
|
||||
<div class="emojiRoot">
|
||||
<div
|
||||
@click="() => photoActionsSelect(0)"
|
||||
class="flex flex-col items-center"
|
||||
>
|
||||
<div
|
||||
class="w-[106rpx] h-[106rpx] bg-[#F9F9F9] rounded-[60rpx] flex-center"
|
||||
>
|
||||
<div @click="()=>photoActionsSelect(0)" class="flex flex-col items-center">
|
||||
<div class="w-[106rpx] h-[106rpx] bg-[#F9F9F9] rounded-[60rpx] flex-center">
|
||||
<tm-image :width="53" :height="44" :src="photoAlbum"></tm-image>
|
||||
</div>
|
||||
<div class="mt-[6rpx] text-[#666666] text-[24rpx]">照片</div>
|
||||
</div>
|
||||
<div
|
||||
@click="() => photoActionsSelect(1)"
|
||||
class="flex flex-col items-center"
|
||||
>
|
||||
<div
|
||||
class="w-[106rpx] h-[106rpx] bg-[#F9F9F9] rounded-[60rpx] flex-center"
|
||||
>
|
||||
<div @click="()=>photoActionsSelect(1)" class="flex flex-col items-center">
|
||||
<div class="w-[106rpx] h-[106rpx] bg-[#F9F9F9] rounded-[60rpx] flex-center">
|
||||
<tm-image :width="53" :height="44" :src="videoImg"></tm-image>
|
||||
</div>
|
||||
<div class="mt-[6rpx] text-[#666666] text-[24rpx]">视频</div>
|
||||
</div>
|
||||
<div @click="takePhoto" class="flex flex-col items-center">
|
||||
<div
|
||||
class="w-[106rpx] h-[106rpx] bg-[#F9F9F9] rounded-[60rpx] flex-center"
|
||||
>
|
||||
<div class="w-[106rpx] h-[106rpx] bg-[#F9F9F9] rounded-[60rpx] flex-center">
|
||||
<tm-image :width="53" :height="44" :src="photoGraph"></tm-image>
|
||||
</div>
|
||||
<div class="mt-[6rpx] text-[#666666] text-[24rpx]">拍摄</div>
|
||||
</div>
|
||||
<div @click="chooseFile" class="flex flex-col items-center">
|
||||
<div
|
||||
class="w-[106rpx] h-[106rpx] bg-[#F9F9F9] rounded-[60rpx] flex-center"
|
||||
>
|
||||
<div class="w-[106rpx] h-[106rpx] bg-[#F9F9F9] rounded-[60rpx] flex-center">
|
||||
<tm-image :width="53" :height="44" :src="folder"></tm-image>
|
||||
</div>
|
||||
<div class="mt-[6rpx] text-[#666666] text-[24rpx]">文件</div>
|
||||
@ -41,15 +27,10 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, reactive, defineProps, defineEmits } from 'vue'
|
||||
import dayjs from 'dayjs'
|
||||
import { ref, reactive, defineProps, defineEmits } from "vue"
|
||||
import dayjs from "dayjs";
|
||||
import { beautifyTime } from '@/utils/datetime'
|
||||
import {
|
||||
useDialogueListStore,
|
||||
useDialogueStore,
|
||||
useUserStore,
|
||||
useUploadsStore,
|
||||
} from '@/store'
|
||||
import { useDialogueListStore, useDialogueStore, useUserStore,useUploadsStore } from '@/store'
|
||||
import { useSessionMenu } from '@/hooks'
|
||||
import photoAlbum from '@/static/image/chatList/photoAlbum.png'
|
||||
import photoGraph from '@/static/image/chatList/photoGraph.png'
|
||||
@ -57,168 +38,87 @@ import videoImg from '@/static/image/chatList/video@2x.png'
|
||||
import folder from '@/static/image/chatList/folder.png'
|
||||
import { uploadImg } from '@/api/chat'
|
||||
import { uniqueId } from '@/utils'
|
||||
import { handleFindWebview } from '@/utils/common'
|
||||
|
||||
const props = defineProps({
|
||||
sendUserInfo: {
|
||||
type: Object,
|
||||
default: {},
|
||||
required: true,
|
||||
required: true
|
||||
},
|
||||
talkParams: {
|
||||
type: Object,
|
||||
default: {},
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const state = reactive({
|
||||
base64Url: '',
|
||||
})
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const uploadsStore = useUploadsStore()
|
||||
const {
|
||||
addDialogueRecord,
|
||||
virtualList,
|
||||
updateUploadProgress,
|
||||
} = useDialogueListStore()
|
||||
const { addDialogueRecord, virtualList, updateUploadProgress } = useDialogueListStore()
|
||||
const dialogueStore = useDialogueStore()
|
||||
const userStore = useUserStore()
|
||||
|
||||
const emit = defineEmits(['selectImg'])
|
||||
|
||||
const onProgressFn = (progress, id) => {
|
||||
console.log((progress.loaded / progress.total) * 100, 'progress')
|
||||
console.log(progress.loaded / progress.total * 100, 'progress');
|
||||
|
||||
updateUploadProgress(id, (progress.loaded / progress.total) * 100)
|
||||
updateUploadProgress(id, progress.loaded / progress.total * 100)
|
||||
}
|
||||
|
||||
const photoActionsSelect = (index) => {
|
||||
if (index === 0) {
|
||||
if (typeof plus === 'undefined') {
|
||||
uni.chooseImage({
|
||||
sourceType: ['album'],
|
||||
count: 9,
|
||||
success: async (res) => {
|
||||
console.log(res, 'res')
|
||||
res.tempFiles.forEach(async (file) => {
|
||||
const fileSizeInMB = (file.size / (1024 * 1024)).toFixed(2)
|
||||
if (fileSizeInMB > 100) {
|
||||
plus.nativeUI.toast('图片大小不能超过100MB')
|
||||
return
|
||||
}
|
||||
const result = await onUploadImageVideo(file, 'image')
|
||||
if (result) {
|
||||
emit('selectImg', result, result.file_num)
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
} else {
|
||||
plus?.gallery.pick(
|
||||
(res) => {
|
||||
console.log(res, 'res')
|
||||
res.files.reverse()
|
||||
res.files.forEach(async (filePath) => {
|
||||
plus?.io?.resolveLocalFileSystemURL(
|
||||
filePath,
|
||||
async (entry) => {
|
||||
entry.file((file) => {
|
||||
const fileReader = new plus.io.FileReader()
|
||||
fileReader.readAsDataURL(file)
|
||||
fileReader.onloadend = async (e) => {
|
||||
const base64Url = e.target.result
|
||||
const fileObj = base64ToFile(base64Url)
|
||||
const fileSizeInMB = (fileObj.size / (1024 * 1024)).toFixed(
|
||||
2,
|
||||
)
|
||||
if (fileSizeInMB > 100) {
|
||||
plus.nativeUI.toast('图片大小不能超过100MB')
|
||||
return
|
||||
}
|
||||
let data = await onUploadImageVideo(fileObj, 'image')
|
||||
if (data) {
|
||||
emit('selectImg', data, data.file_num)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
(err) => {
|
||||
console.log(err)
|
||||
},
|
||||
)
|
||||
})
|
||||
},
|
||||
(err) => {
|
||||
console.log(err)
|
||||
},
|
||||
{
|
||||
filter: 'image',
|
||||
maximum: 9,
|
||||
multiple: true,
|
||||
onmaxed: () => {
|
||||
plus.nativeUI.toast('最多只能选择9张图片')
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// handleFindWebview(`getPlusVideoPicker()`)
|
||||
// return
|
||||
uni.chooseImage({
|
||||
sourceType: ['album'],
|
||||
count: 9,
|
||||
success: async (res) => {
|
||||
console.log(res,'res');
|
||||
res.tempFiles.forEach(async (file) => {
|
||||
let data = await onUploadImageVideo(file, 'image')
|
||||
emit('selectImg', data)
|
||||
})
|
||||
}
|
||||
})
|
||||
}else{
|
||||
uni.chooseVideo({
|
||||
sourceType: ['album'],
|
||||
compressed: true,
|
||||
maxDuration: 60,
|
||||
success: async (res) => {
|
||||
console.log(res, 'res')
|
||||
const fileSizeInMB = (res.tempFile.size / (1024 * 1024)).toFixed(2)
|
||||
if (fileSizeInMB > 100) {
|
||||
plus.nativeUI.toast('视频大小不能超过100MB')
|
||||
return
|
||||
}
|
||||
let data = await onUploadImageVideo(
|
||||
res.tempFile,
|
||||
'video',
|
||||
res.tempFilePath,
|
||||
)
|
||||
if (data) {
|
||||
emit('selectImg', data, data.file_num)
|
||||
}
|
||||
},
|
||||
console.log(res,'res');
|
||||
let data = await onUploadImageVideo(res.tempFile, 'video',res.tempFilePath)
|
||||
emit('selectImg', data)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const onUploadImageVideo = async (file, type = 'image', fileUrl) => {
|
||||
console.log('开始上传文件:', file.name)
|
||||
uploadsStore.updateUploadStatus(true)
|
||||
const onUploadImageVideo = async (file, type = 'image',fileUrl) => {
|
||||
console.log(file, 'file');
|
||||
return new Promise(async (resolve) => {
|
||||
if (type === 'image') {
|
||||
let image = new Image()
|
||||
image.src = URL.createObjectURL(file)
|
||||
image.onload = async () => {
|
||||
image.onload = () => {
|
||||
const form = new FormData()
|
||||
form.append('file', file)
|
||||
form.append('source', 'fonchain-chat')
|
||||
form.append('urlParam', `width=${image.width}&height=${image.height}`)
|
||||
form.append("source", "fonchain-chat");
|
||||
form.append("urlParam", `width=${image.width}&height=${image.height}`);
|
||||
let randomId = uniqueId()
|
||||
let newItem = {
|
||||
avatar: userStore.avatar,
|
||||
created_at: dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
||||
extra: {
|
||||
height: image.height,
|
||||
name: '',
|
||||
name: "",
|
||||
size: 0,
|
||||
url: image.src,
|
||||
width: image.width,
|
||||
width: image.width
|
||||
},
|
||||
float: 'right',
|
||||
float: "right",
|
||||
isCheck: false,
|
||||
is_mark: 0,
|
||||
is_read: 0,
|
||||
is_revoke: 0,
|
||||
msg_id: randomId,
|
||||
file_num: randomId,
|
||||
msg_type: 3,
|
||||
nickname: userStore.nickname,
|
||||
receiver_id: dialogueStore.talk.receiver_id,
|
||||
@ -226,372 +126,210 @@ const onUploadImageVideo = async (file, type = 'image', fileUrl) => {
|
||||
talk_type: dialogueStore.talk.talk_type,
|
||||
user_id: userStore.uid,
|
||||
uploadCurrent: 0,
|
||||
uploadStatus: 1, // 1 上传中 2 上传成功 3 上传失败
|
||||
uploadStatus: 1, // 1 上传中 2 上传成功 3 上传失败
|
||||
}
|
||||
|
||||
virtualList.value.unshift(newItem)
|
||||
|
||||
try {
|
||||
const result = await uploadImg(form, (e) => onProgressFn(e, randomId))
|
||||
console.log('上传完成,结果:', result)
|
||||
|
||||
if (result.status === 0) {
|
||||
// 更新上传状态为成功
|
||||
const index = virtualList.value.findIndex(
|
||||
(item) => item.file_num === randomId,
|
||||
)
|
||||
if (index !== -1) {
|
||||
virtualList.value[index].uploadStatus = 2
|
||||
virtualList.value[index].uploadCurrent = 100
|
||||
}
|
||||
|
||||
// 确保返回数据
|
||||
uploadImg(form, (e) => onProgressFn(e, randomId)).then(({ status, data, msg }) => {
|
||||
if (status == 0) {
|
||||
resolve({
|
||||
type: 'image',
|
||||
url: result.data.ori_url,
|
||||
url: data.ori_url,
|
||||
size: file.size,
|
||||
width: image.width,
|
||||
height: image.height,
|
||||
file_num: randomId,
|
||||
height: image.height
|
||||
})
|
||||
} else {
|
||||
uploadsStore.updateUploadStatus(false)
|
||||
// 更新上传状态为失败
|
||||
const index = virtualList.value.findIndex(
|
||||
(item) => item.file_num === randomId,
|
||||
)
|
||||
if (index !== -1) {
|
||||
virtualList.value[index].uploadStatus = 3
|
||||
}
|
||||
message.error(result.msg)
|
||||
resolve('')
|
||||
message.error(msg)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('上传出错:', error)
|
||||
uploadsStore.updateUploadStatus(false)
|
||||
// 更新上传状态为失败
|
||||
const index = virtualList.value.findIndex(
|
||||
(item) => item.file_num === randomId,
|
||||
)
|
||||
if (index !== -1) {
|
||||
virtualList.value[index].uploadStatus = 3
|
||||
}
|
||||
message.error('上传失败')
|
||||
resolve('')
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
uni.getVideoInfo({
|
||||
src: fileUrl,
|
||||
success: async (resp) => {
|
||||
console.log('视频信息:', resp)
|
||||
const form = new FormData()
|
||||
form.append('file', file)
|
||||
form.append('source', 'fonchain-chat')
|
||||
form.append('type', 'video')
|
||||
form.append('urlParam', `width=${resp.width}&height=${resp.height}`)
|
||||
let randomId = uniqueId()
|
||||
let newItem = {
|
||||
avatar: userStore.avatar,
|
||||
created_at: dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
||||
extra: {
|
||||
src:fileUrl,
|
||||
success:(resp)=>{
|
||||
console.log(resp);
|
||||
form.append('file', file)
|
||||
form.append("source", "fonchain-chat");
|
||||
form.append("type", "video");
|
||||
form.append("urlParam", `width=${resp.width}&height=${resp.height}`);
|
||||
let randomId = uniqueId()
|
||||
let newItem = {
|
||||
avatar: userStore.avatar,
|
||||
created_at: dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
||||
extra: {
|
||||
duration: parseInt(resp.duration),
|
||||
height: resp.height,
|
||||
name: "",
|
||||
url: fileUrl,
|
||||
width: resp.width
|
||||
},
|
||||
float: "right",
|
||||
isCheck: false,
|
||||
is_mark: 0,
|
||||
is_read: 0,
|
||||
is_revoke: 0,
|
||||
msg_id: randomId,
|
||||
msg_type: 5,
|
||||
nickname: userStore.nickname,
|
||||
receiver_id: dialogueStore.talk.receiver_id,
|
||||
sequence: -1,
|
||||
talk_type: dialogueStore.talk.talk_type,
|
||||
user_id: userStore.uid,
|
||||
uploadCurrent: 0,
|
||||
uploadStatus: 1, // 1 上传中 2 上传成功 3 上传失败
|
||||
}
|
||||
virtualList.value.unshift(newItem)
|
||||
uploadImg(form, (e) => onProgressFn(e, randomId)).then(({ status, data, msg }) => {
|
||||
if (status == 0) {
|
||||
console.log(data);
|
||||
resolve({
|
||||
type: 'video',
|
||||
url: data.ori_url,
|
||||
cover: data.cover_url,
|
||||
duration: parseInt(resp.duration),
|
||||
height: resp.height,
|
||||
name: '',
|
||||
url: fileUrl,
|
||||
width: resp.width,
|
||||
},
|
||||
float: 'right',
|
||||
isCheck: false,
|
||||
is_mark: 0,
|
||||
is_read: 0,
|
||||
is_revoke: 0,
|
||||
msg_id: randomId,
|
||||
file_num: randomId,
|
||||
msg_type: 5,
|
||||
nickname: userStore.nickname,
|
||||
receiver_id: dialogueStore.talk.receiver_id,
|
||||
sequence: -1,
|
||||
talk_type: dialogueStore.talk.talk_type,
|
||||
user_id: userStore.uid,
|
||||
uploadCurrent: 0,
|
||||
uploadStatus: 1,
|
||||
size: file.size
|
||||
})
|
||||
} else {
|
||||
// resolve('')
|
||||
// message.error(msg)
|
||||
}
|
||||
virtualList.value.unshift(newItem)
|
||||
|
||||
try {
|
||||
const result = await uploadImg(form, (e) =>
|
||||
onProgressFn(e, randomId),
|
||||
)
|
||||
console.log('视频上传完成,结果:', result)
|
||||
|
||||
if (result.status === 0) {
|
||||
// 更新上传状态为成功
|
||||
const index = virtualList.value.findIndex(
|
||||
(item) => item.file_num === randomId,
|
||||
)
|
||||
if (index !== -1) {
|
||||
virtualList.value[index].uploadStatus = 2
|
||||
virtualList.value[index].uploadCurrent = 100
|
||||
}
|
||||
|
||||
resolve({
|
||||
type: 'video',
|
||||
url: result.data.ori_url,
|
||||
cover: result.data.cover_url,
|
||||
duration: parseInt(resp.duration),
|
||||
size: file.size,
|
||||
file_num: randomId,
|
||||
})
|
||||
} else {
|
||||
uploadsStore.updateUploadStatus(false)
|
||||
// 更新上传状态为失败
|
||||
const index = virtualList.value.findIndex(
|
||||
(item) => item.file_num === randomId,
|
||||
)
|
||||
if (index !== -1) {
|
||||
virtualList.value[index].uploadStatus = 3
|
||||
}
|
||||
message.error(result.msg)
|
||||
resolve('')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('视频上传出错:', error)
|
||||
uploadsStore.updateUploadStatus(false)
|
||||
// 更新上传状态为失败
|
||||
const index = virtualList.value.findIndex(
|
||||
(item) => item.file_num === randomId,
|
||||
)
|
||||
if (index !== -1) {
|
||||
virtualList.value[index].uploadStatus = 3
|
||||
}
|
||||
message.error('上传失败')
|
||||
resolve('')
|
||||
}
|
||||
},
|
||||
fail: (error) => {
|
||||
console.error('获取视频信息失败:', error)
|
||||
uploadsStore.updateUploadStatus(false)
|
||||
message.error('获取视频信息失败')
|
||||
resolve('')
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
const form = new FormData()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const base64ToFile = (base64) => {
|
||||
if (!base64) {
|
||||
message.warning('您的系统暂不支持发送原图哦')
|
||||
}
|
||||
// base64转file
|
||||
const [header, base64String] = base64.split(';base64,')
|
||||
const imageType = header.split(':')[1]
|
||||
const byteCharacters = atob(base64String)
|
||||
const [header, base64String] = base64.split(";base64,");
|
||||
const imageType = header.split(":")[1];
|
||||
const byteCharacters = atob(base64String);
|
||||
const byteArray = new Uint8Array(
|
||||
Array.from(byteCharacters, (char) => char.charCodeAt(0)),
|
||||
)
|
||||
return new File([new Blob([byteArray], { type: imageType })], 'example.png', {
|
||||
type: imageType,
|
||||
})
|
||||
Array.from(byteCharacters, (char) => char.charCodeAt(0))
|
||||
);
|
||||
return new File(
|
||||
[new Blob([byteArray], { type: imageType })],
|
||||
"example.png",
|
||||
{ type: imageType }
|
||||
);
|
||||
}
|
||||
|
||||
const choosePhoto = (filter = 'none', maximum = 9, multiple = true) => {
|
||||
window.plus?.gallery.pick(
|
||||
(res) => {
|
||||
console.log(res)
|
||||
res.files.reverse()
|
||||
res.files.forEach(async (filePath) => {
|
||||
const suffix = filePath.split('.').pop()?.toLowerCase() || ''
|
||||
if (['jpg', 'png'].includes(suffix)) {
|
||||
console.log('进入图片')
|
||||
window.plus?.io?.resolveLocalFileSystemURL(
|
||||
filePath,
|
||||
async (entry) => {
|
||||
entry.file((file) => {
|
||||
const fileReader = new plus.io.FileReader()
|
||||
fileReader.readAsDataURL(file)
|
||||
fileReader.onloadend = async (e) => {
|
||||
const base64Url = e.target.result
|
||||
const fileObj = base64ToFile(base64Url)
|
||||
let data = await onUploadImageVideo(fileObj, 'image')
|
||||
emit('selectImg', data)
|
||||
}
|
||||
})
|
||||
},
|
||||
(err) => {
|
||||
console.log(err)
|
||||
},
|
||||
)
|
||||
}
|
||||
if (['mp4', 'flv'].includes(suffix)) {
|
||||
console.log(filePath, '进入视频')
|
||||
// const localUrl = plus.io.convertLocalFileSystemURL(filePath)
|
||||
// console.log(localUrl);
|
||||
|
||||
plus.io.getVideoInfo({
|
||||
filePath: filePath,
|
||||
success: (event) => {
|
||||
console.log(event)
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log(err)
|
||||
},
|
||||
window.plus?.gallery.pick((res) => {
|
||||
console.log(res);
|
||||
res.files.reverse()
|
||||
res.files.forEach(async (filePath) => {
|
||||
const suffix = filePath.split('.').pop()?.toLowerCase() || ''
|
||||
if (['jpg', 'png'].includes(suffix)) {
|
||||
console.log("进入图片")
|
||||
window.plus?.io?.resolveLocalFileSystemURL(filePath, async (entry) => {
|
||||
entry.file((file) => {
|
||||
const fileReader = new plus.io.FileReader();
|
||||
fileReader.readAsDataURL(file);
|
||||
fileReader.onloadend = async (e) => {
|
||||
const base64Url = e.target.result;
|
||||
const fileObj = base64ToFile(base64Url);
|
||||
let data = await onUploadImageVideo(fileObj, 'image')
|
||||
emit('selectImg', data)
|
||||
};
|
||||
})
|
||||
// window.plus?.io?.resolveLocalFileSystemURL(localUrl, async (entry) => {
|
||||
// entry.file((file) => {
|
||||
// console.log(file,'file');
|
||||
// const fileReader = new plus.io.FileReader();
|
||||
// fileReader.readAsDataURL(file);
|
||||
// fileReader.onloadend = async (e) => {
|
||||
// const base64Url = e.target.result;
|
||||
// const fileObj = base64ToFile(base64Url);
|
||||
// let data = await onUploadImageVideo(fileObj, 'video')
|
||||
// emit('selectImg', data)
|
||||
// };
|
||||
// })
|
||||
// },
|
||||
// (err) => {
|
||||
// console.log(err);
|
||||
// }
|
||||
// )
|
||||
},
|
||||
(err) => {
|
||||
console.log(err);
|
||||
}
|
||||
)
|
||||
}
|
||||
if (['mp4', 'flv'].includes(suffix)) {
|
||||
console.log(filePath,"进入视频")
|
||||
// const localUrl = plus.io.convertLocalFileSystemURL(filePath)
|
||||
// console.log(localUrl);
|
||||
|
||||
plus.io.getVideoInfo({
|
||||
filePath:filePath,
|
||||
success:(event)=>{
|
||||
console.log(event);
|
||||
},
|
||||
fail:(err)=>{
|
||||
console.log(err);
|
||||
}
|
||||
})
|
||||
},
|
||||
(err) => {
|
||||
console.log(err)
|
||||
},
|
||||
});
|
||||
// window.plus?.io?.resolveLocalFileSystemURL(localUrl, async (entry) => {
|
||||
// entry.file((file) => {
|
||||
// console.log(file,'file');
|
||||
// const fileReader = new plus.io.FileReader();
|
||||
// fileReader.readAsDataURL(file);
|
||||
// fileReader.onloadend = async (e) => {
|
||||
// const base64Url = e.target.result;
|
||||
// const fileObj = base64ToFile(base64Url);
|
||||
// let data = await onUploadImageVideo(fileObj, 'video')
|
||||
// emit('selectImg', data)
|
||||
// };
|
||||
// })
|
||||
// },
|
||||
// (err) => {
|
||||
// console.log(err);
|
||||
// }
|
||||
// )
|
||||
}
|
||||
})
|
||||
}, (err) => {
|
||||
console.log(err);
|
||||
},
|
||||
{
|
||||
filter: filter,
|
||||
maximum: maximum,
|
||||
multiple: multiple,
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
const takePhoto = () => {
|
||||
if (typeof plus !== 'undefined') {
|
||||
getCamera()
|
||||
} else {
|
||||
document.addEventListener('plusready', () => {
|
||||
getCamera()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const getCamera = () => {
|
||||
const cmr = plus.camera.getCamera()
|
||||
cmr.captureImage(
|
||||
(p) => {
|
||||
plus.io.resolveLocalFileSystemURL(
|
||||
p,
|
||||
(entry) => {
|
||||
compressAndShowImage(entry.toLocalURL(), entry.name)
|
||||
},
|
||||
(err) => {
|
||||
console.log(err)
|
||||
},
|
||||
)
|
||||
},
|
||||
() => {},
|
||||
{ index: '2' },
|
||||
)
|
||||
}
|
||||
|
||||
const compressAndShowImage = (url, filename) => {
|
||||
const dst = `_doc/upload/${filename}`
|
||||
plus.zip.compressImage(
|
||||
{ src: url, dst, quality: 10, overwrite: true },
|
||||
(zip) => displayImage(zip.target),
|
||||
(err) => {
|
||||
console.log(err)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
const displayImage = (url) => {
|
||||
plus.io.resolveLocalFileSystemURL(url, (entry) => {
|
||||
entry.file((file) => {
|
||||
const fileReader = new plus.io.FileReader()
|
||||
fileReader.readAsDataURL(file)
|
||||
fileReader.onloadend = async (e) => {
|
||||
state.base64Url = e.target.result
|
||||
const imageFile = base64ToFile(state.base64Url)
|
||||
let data = await onUploadImageVideo(imageFile, 'image')
|
||||
emit('selectImg', data, data.file_num)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const chooseFile = () => {
|
||||
uni.chooseFile({
|
||||
count: 1,
|
||||
extension: [''],
|
||||
extension:[''],
|
||||
success: (res) => {
|
||||
const fileSizeInMB = (res.tempFiles[0].size / (1024 * 1024)).toFixed(2)
|
||||
if (fileSizeInMB > 100) {
|
||||
plus.nativeUI.toast('文件大小不能超过100MB')
|
||||
return
|
||||
}
|
||||
let randomId = uniqueId()
|
||||
let newItem = {
|
||||
avatar: userStore.avatar,
|
||||
created_at: dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
||||
extra: {
|
||||
drive: 3,
|
||||
name: res.tempFiles[0].name,
|
||||
size: res.tempFiles[0].size,
|
||||
path: res.tempFilePaths[0],
|
||||
},
|
||||
float: 'right',
|
||||
isCheck: false,
|
||||
is_mark: 0,
|
||||
is_read: 0,
|
||||
is_revoke: 0,
|
||||
msg_id: randomId,
|
||||
file_num: randomId,
|
||||
msg_type: 6,
|
||||
nickname: userStore.nickname,
|
||||
receiver_id: dialogueStore.talk.receiver_id,
|
||||
sequence: -1,
|
||||
talk_type: dialogueStore.talk.talk_type,
|
||||
user_id: userStore.uid,
|
||||
uploadCurrent: 0,
|
||||
uploadStatus: 1, // 1 上传中 2 上传成功 3 上传失败
|
||||
}
|
||||
let newItem = {
|
||||
avatar: userStore.avatar,
|
||||
created_at: dayjs().format('YYYY-MM-DD HH:mm:ss'),
|
||||
extra: {
|
||||
drive: 3,
|
||||
name: res.tempFiles[0].name,
|
||||
size: res.tempFiles[0].size,
|
||||
path: res.tempFilePaths[0],
|
||||
},
|
||||
float: "right",
|
||||
isCheck: false,
|
||||
is_mark: 0,
|
||||
is_read: 0,
|
||||
is_revoke: 0,
|
||||
msg_id: randomId,
|
||||
msg_type: 6,
|
||||
nickname: userStore.nickname,
|
||||
receiver_id: dialogueStore.talk.receiver_id,
|
||||
sequence: -1,
|
||||
talk_type: dialogueStore.talk.talk_type,
|
||||
user_id: userStore.uid,
|
||||
uploadCurrent: 0,
|
||||
uploadStatus: 1, // 1 上传中 2 上传成功 3 上传失败
|
||||
}
|
||||
virtualList.value.unshift(newItem)
|
||||
uploadsStore.updateUploadStatus(true)
|
||||
uploadsStore.initUploadFile(
|
||||
res.tempFiles[0],
|
||||
props.talkParams,
|
||||
randomId,
|
||||
(status, data, msg) => {
|
||||
if (status === 0) {
|
||||
// 更新上传状态为成功
|
||||
const index = virtualList.value.findIndex(
|
||||
(item) => item.file_num === randomId,
|
||||
)
|
||||
if (index !== -1) {
|
||||
virtualList.value[index].uploadStatus = 2
|
||||
virtualList.value[index].uploadCurrent = 100
|
||||
}
|
||||
} else {
|
||||
uploadsStore.updateUploadStatus(false)
|
||||
// 更新上传状态为失败
|
||||
const index = virtualList.value.findIndex(
|
||||
(item) => item.file_num === randomId,
|
||||
)
|
||||
if (index !== -1) {
|
||||
virtualList.value[index].uploadStatus = 3
|
||||
}
|
||||
message.error(msg)
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
uploadsStore.initUploadFile(res.tempFiles[0], props.talkParams,randomId)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.emojiRoot {
|
||||
|
@ -37,7 +37,7 @@
|
||||
</div>
|
||||
<div class="user-info-main user-info-card">
|
||||
<div class="user-info-main-title">
|
||||
<img src="@/static/image/mine/ming001@3x.png" />
|
||||
<img src="/src/static/image/mine/ming001@3x.png" />
|
||||
<span class="text-[28rpx] font-medium">
|
||||
{{ $t('index.mine.basic') }}
|
||||
</span>
|
||||
@ -61,48 +61,13 @@
|
||||
<template #bottom>
|
||||
<customBtn
|
||||
:isBottom="true"
|
||||
:btnText="
|
||||
state.canSendMsg
|
||||
? $t('user.detail.sendMsg')
|
||||
: $t('addressBook.btns.addFriend')
|
||||
"
|
||||
:subBtnText="
|
||||
!state.canSendMsg || state.userInfo.sys_id === state.uid
|
||||
? ''
|
||||
: $t('user.detail.ringBell')
|
||||
"
|
||||
@clickBtn="checkSendPermission"
|
||||
@clickSubBtn="handleCall"
|
||||
:btnText="$t('user.detail.sendMsg')"
|
||||
:subBtnText="$t('user.detail.ringBell')"
|
||||
@clickBtn="toTalkUser"
|
||||
></customBtn>
|
||||
</template>
|
||||
</ZPaging>
|
||||
</div>
|
||||
<tm-drawer
|
||||
placement="bottom"
|
||||
v-model:show="state.isShowPhoneCall"
|
||||
:hideHeader="true"
|
||||
:height="416"
|
||||
:round="6"
|
||||
>
|
||||
<div class="do-phone-call">
|
||||
<div class="do-phone-call-header">
|
||||
<span>{{ $t('popup.title.phone') }}</span>
|
||||
<img
|
||||
src="@/static/image/login/check-circle-filled@3x.png"
|
||||
@click="hidePhoneCallPopup"
|
||||
/>
|
||||
</div>
|
||||
<div class="do-phone-call-number">
|
||||
<span>{{ state.phoneNumber }}</span>
|
||||
</div>
|
||||
<div class="do-phone-call-btn">
|
||||
<customBtn
|
||||
:btnText="$t('do.phone.call')"
|
||||
@clickBtn="doPhoneCall"
|
||||
></customBtn>
|
||||
</div>
|
||||
</div>
|
||||
</tm-drawer>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
@ -111,12 +76,10 @@ import ZPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { reactive } from 'vue'
|
||||
|
||||
import { useTalkStore, useUserStore } from '@/store'
|
||||
import { useTalkStore } from '@/store'
|
||||
const talkStore = useTalkStore()
|
||||
const userStore = useUserStore()
|
||||
|
||||
import { getUserInfoByClickAvatar } from '@/api/user/index'
|
||||
import { ServeCheckFriend, ServeAddFriend } from '@/api/addressBook/index'
|
||||
|
||||
import { useI18n } from 'vue-i18n'
|
||||
const { t } = useI18n()
|
||||
@ -125,10 +88,6 @@ const state = reactive({
|
||||
erpUserId: '', //erp用户id
|
||||
userInfo: null, //用户详情
|
||||
userBasicInfos: [], //用户基本信息
|
||||
isShowPhoneCall: false, //是否显示电话拨号弹框
|
||||
phoneNumber: '', //手机号
|
||||
uid: computed(() => userStore.uid), //当前用户id
|
||||
canSendMsg: false, //是否可以发送消息(是好友或同公司别),如果不可以就需要先加好友
|
||||
})
|
||||
|
||||
onLoad((options) => {
|
||||
@ -151,7 +110,6 @@ const getUserInfo = () => {
|
||||
console.log(data)
|
||||
if (code == 200) {
|
||||
state.userInfo = data
|
||||
checkNeedAddFriend()
|
||||
let department = ''
|
||||
let post = ''
|
||||
if (data?.erp_dept_position?.length > 0) {
|
||||
@ -209,7 +167,6 @@ const getUserInfo = () => {
|
||||
value: data.enter_date,
|
||||
},
|
||||
]
|
||||
state.phoneNumber = data.tel_num
|
||||
} else {
|
||||
}
|
||||
})
|
||||
@ -217,72 +174,10 @@ const getUserInfo = () => {
|
||||
resp.catch(() => {})
|
||||
}
|
||||
|
||||
//点击唤起拨号弹框
|
||||
const handleCall = () => {
|
||||
state.isShowPhoneCall = true
|
||||
}
|
||||
|
||||
//点击隐藏拨号弹框
|
||||
const hidePhoneCallPopup = () => {
|
||||
state.isShowPhoneCall = false
|
||||
}
|
||||
|
||||
//点击对用户发起单聊
|
||||
const toTalkUser = () => {
|
||||
talkStore.toTalk(1, state.userInfo.sys_id, state.erpUserId)
|
||||
}
|
||||
|
||||
//点击拨打唤起拨号
|
||||
const doPhoneCall = () => {
|
||||
uni.makePhoneCall({
|
||||
phoneNumber: state.phoneNumber,
|
||||
success: () => {
|
||||
console.log('拨号成功')
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('失败:', err)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
//检查是否可以发送消息,如果不可以要先添加好友
|
||||
const checkSendPermission = () => {
|
||||
if (state.canSendMsg) {
|
||||
toTalkUser()
|
||||
} else {
|
||||
doAddFriend()
|
||||
}
|
||||
}
|
||||
|
||||
//校验是否需要加好友
|
||||
const checkNeedAddFriend = () => {
|
||||
state.canSendMsg = true
|
||||
// let params = {
|
||||
// receiver_id: state.userInfo.sys_id, //聊天的用户id
|
||||
// talk_type: 1,
|
||||
// }
|
||||
// ServeCheckFriend(params).then((res) => {
|
||||
// console.log(res)
|
||||
// if (res?.code === 200) {
|
||||
// state.canSendMsg = res.data?.is_friend || false
|
||||
// }
|
||||
// })
|
||||
}
|
||||
|
||||
//主动加好友(单向好友)
|
||||
const doAddFriend = () => {
|
||||
let params = {
|
||||
receiver_id: state.userInfo.sys_id, //聊天的用户id
|
||||
talk_type: 1,
|
||||
}
|
||||
ServeAddFriend(params).then((res) => {
|
||||
console.log(res)
|
||||
if (res?.code === 200) {
|
||||
message.success(t('addressBook.message.addSuccess') + ' !')
|
||||
state.canSendMsg = true
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.outer-layer {
|
||||
@ -325,7 +220,6 @@ const doAddFriend = () => {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@ -396,58 +290,4 @@ const doAddFriend = () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.do-phone-call {
|
||||
.do-phone-call-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 42rpx 0;
|
||||
position: relative;
|
||||
span {
|
||||
font-size: 28rpx;
|
||||
line-height: 40rpx;
|
||||
color: #747474;
|
||||
font-weight: 400;
|
||||
}
|
||||
img {
|
||||
position: absolute;
|
||||
top: 44rpx;
|
||||
right: 30rpx;
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
}
|
||||
}
|
||||
.do-phone-call-number {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 32rpx 0;
|
||||
border-top: 2rpx solid #e7e7e7;
|
||||
border-bottom: 2rpx solid #e7e7e7;
|
||||
span {
|
||||
font-size: 32rpx;
|
||||
line-height: 44rpx;
|
||||
color: #1a1a1a;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
.do-phone-call-btn {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 50rpx 0;
|
||||
:deep(.custom-btn-class) {
|
||||
width: 690rpx;
|
||||
font-size: 32rpx;
|
||||
line-height: 44rpx;
|
||||
padding: 18rpx 0;
|
||||
height: 80rpx;
|
||||
background: linear-gradient(to right, #674bbc, #46299d);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,17 +1,14 @@
|
||||
<script lang="ts" setup>
|
||||
import zPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ServeGetForwardRecords } from '@/api/chat'
|
||||
import { MessageComponents } from '@/constant/message'
|
||||
import { ITalkRecord } from '@/types/chat'
|
||||
import WdLoading from "@/uni_modules/wot-design-uni/components/wd-loading/wd-loading.vue";
|
||||
import { useInject } from '@/hooks'
|
||||
import { parseTime } from '@/utils/datetime'
|
||||
|
||||
const emit = defineEmits(['close'])
|
||||
|
||||
const msgId = ref(null)
|
||||
const createdAt = ref(null)
|
||||
const { showUserInfoModal } = useInject()
|
||||
const isShow = ref(true)
|
||||
const items = ref<ITalkRecord[]>([])
|
||||
@ -23,18 +20,12 @@ const onMaskClick = () => {
|
||||
|
||||
const onLoadData = () => {
|
||||
ServeGetForwardRecords({
|
||||
msg_id: msgId.value,
|
||||
biz_date: createdAt.value
|
||||
msg_id: msgId.value
|
||||
}).then((res) => {
|
||||
if (res.code == 200) {
|
||||
items.value = res.data.items || []
|
||||
// 修复标题逻辑
|
||||
const uniqueNames = [...new Set(items.value.map(v => v.nickname))];
|
||||
if (uniqueNames.length <= 2) {
|
||||
title.value = uniqueNames.join('和');
|
||||
} else {
|
||||
title.value = uniqueNames.slice(0, 2).join('和') + '等';
|
||||
}
|
||||
|
||||
title.value = [...new Set(items.value.map((v) => v.nickname))].join('、')
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -44,7 +35,6 @@ onMounted(() => {
|
||||
const page = pages[pages.length - 1]
|
||||
const options = page.$page.options
|
||||
msgId.value = options.msgId
|
||||
createdAt.value = parseTime(new Date((options.created_at as any)), '{y}{m}')
|
||||
console.log(msgId.value,'msgId.value');
|
||||
|
||||
onLoadData()
|
||||
@ -52,12 +42,12 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="forward-record-page">
|
||||
<zPaging ref="zPaging" :show-scrollbar="false">
|
||||
<template #top>
|
||||
<customNavbar :title="`${title}的会话记录`"></customNavbar>
|
||||
</template>
|
||||
<div class="main-box">
|
||||
<div class="outer-layer">
|
||||
<div>
|
||||
<tm-navbar :hideBack="false" hideHome :title="`${title}的会话记录`" >
|
||||
</tm-navbar>
|
||||
</div>
|
||||
<div class="main-box">
|
||||
<div v-if="items.length === 0" class="flex justify-center items-center w-full mt-[200rpx]">
|
||||
<wd-loading />
|
||||
</div>
|
||||
@ -81,15 +71,16 @@ onMounted(() => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</zPaging>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.forward-record-page {
|
||||
.outer-layer {
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
background-image: url("@/static/image/clockIn/z3280@3x.png");
|
||||
background-size: cover;
|
||||
padding: 0 66rpx 20rpx 50rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
@ -100,7 +91,7 @@ onMounted(() => {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
padding: 28rpx 66rpx 20rpx 50rpx;
|
||||
padding-top: 28rpx;
|
||||
}
|
||||
|
||||
.message-item {
|
||||
|
@ -1,50 +1,40 @@
|
||||
<template>
|
||||
<div>
|
||||
<wd-swipe-action class="swipe_action">
|
||||
<div
|
||||
@click="cellClick"
|
||||
:class="['chatItem', props.data.is_top === 1 ? 'isTop' : '']"
|
||||
>
|
||||
<div class="avatarImg">
|
||||
<tm-badge
|
||||
:count="props.data.is_disturb === 0 ? props.data.unread_num : ''"
|
||||
:dot="
|
||||
props.data.is_disturb === 1 && props.data.unread_num
|
||||
? true
|
||||
: false
|
||||
"
|
||||
:maxCount="99"
|
||||
class="badge"
|
||||
color="#D03050"
|
||||
>
|
||||
<avatarModule
|
||||
:mode="props?.data?.group_type === 0 ? 1 : 2"
|
||||
:avatar="props?.data?.avatar"
|
||||
:groupType="props?.data?.group_type"
|
||||
:userName="props?.data?.name"
|
||||
:customStyle="{
|
||||
width: '96rpx',
|
||||
height: '96rpx',
|
||||
}"
|
||||
:customTextStyle="{
|
||||
fontSize: '32rpx',
|
||||
fontWeight: 'bold',
|
||||
color: '#fff',
|
||||
lineHeight: '44rpx',
|
||||
}"
|
||||
></avatarModule>
|
||||
</tm-badge>
|
||||
</div>
|
||||
<div class="chatInfo">
|
||||
<div class="chatInfo_1">
|
||||
<div class="name_center">
|
||||
<div
|
||||
class="text-[#000000] text-[32rpx] font-bold opacity-90 name_text"
|
||||
>
|
||||
<div>
|
||||
<wd-swipe-action class="swipe_action">
|
||||
<div
|
||||
@click="cellClick"
|
||||
:class="['chatItem', props.data.is_top === 1 ? 'isTop' : '']"
|
||||
>
|
||||
<div class="avatarImg">
|
||||
<tm-badge
|
||||
:count="props.data.unread_num"
|
||||
:maxCount="99"
|
||||
color="#D03050"
|
||||
>
|
||||
<avatarModule
|
||||
:mode="props?.data?.group_type === 0 ? 1 : 2"
|
||||
:avatar="props?.data?.avatar"
|
||||
:groupType="props?.data?.group_type"
|
||||
:userName="props?.data?.name"
|
||||
:customStyle="{
|
||||
width: '96rpx',
|
||||
height: '96rpx',
|
||||
}"
|
||||
:customTextStyle="{
|
||||
fontSize: '32rpx',
|
||||
fontWeight: 'bold',
|
||||
color: '#fff',
|
||||
lineHeight: '44rpx',
|
||||
}"
|
||||
></avatarModule>
|
||||
</tm-badge>
|
||||
</div>
|
||||
<div class="chatInfo">
|
||||
<div class="chatInfo_1">
|
||||
<div class="name_center">
|
||||
<div class="text-[#000000] text-[32rpx]
|
||||
font-bold opacity-90 name_text">
|
||||
{{ formatNameText(props.data.name) }}
|
||||
<span v-if="props.data.talk_type === 2">
|
||||
({{ props.data.group_member_num }})
|
||||
</span>
|
||||
<span v-if="props.data.group_type === 2" class="depTag tag">
|
||||
部门
|
||||
</span>
|
||||
@ -55,167 +45,153 @@
|
||||
公司
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style="flex-shrink: 0;"
|
||||
class="text-[#000000] text-[28rpx] font-medium opacity-26 ml-[24rpx] time_right"
|
||||
>
|
||||
{{ beautifyTime(props.data.updated_at) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="chatInfo_2 w-full mr-[6rpx]">
|
||||
<div class="w-full chatInfo_2_1 textEllipsis">
|
||||
<span v-if="props.data.atsign_num" style="color: red;">
|
||||
[有人@你]
|
||||
</span>
|
||||
{{ props.data.msg_text }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template #right>
|
||||
<div class="flex flex-row flex-row-center-end">
|
||||
<!-- 样式占位 -->
|
||||
<div style="width: 1px;"></div>
|
||||
<div
|
||||
@click="handleTop"
|
||||
class="w-[156rpx] h-[154rpx] text-[#ffffff] bg-[#F09F1F] flex items-center justify-center"
|
||||
>
|
||||
{{ props.data.is_top === 1 ? '取消置顶' : '置顶' }}
|
||||
</div>
|
||||
<div
|
||||
@click="handleDelete"
|
||||
class="w-[156rpx] h-[154rpx] text-[#ffffff] bg-[#CF3050] flex items-center justify-center"
|
||||
>
|
||||
删除
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</wd-swipe-action>
|
||||
<div
|
||||
v-if="props.index !== talkStore.talkItems.length - 1"
|
||||
class="divider"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div style="flex-shrink: 0;"
|
||||
class="text-[#000000] text-[28rpx] font-medium opacity-26 ml-[24rpx] time_right"
|
||||
>
|
||||
{{ beautifyTime(props.data.updated_at) }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="chatInfo_2 w-full mr-[6rpx]">
|
||||
<div class="w-full chatInfo_2_1 textEllipsis">
|
||||
{{ props.data.msg_text }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template #right>
|
||||
<div class="flex flex-row flex-row-center-end">
|
||||
<!-- 样式占位 -->
|
||||
<div style="width: 1px"></div>
|
||||
<div
|
||||
@click="handleTop"
|
||||
class="w-[156rpx] h-[154rpx] text-[#ffffff] bg-[#F09F1F] flex items-center justify-center"
|
||||
>
|
||||
{{ props.data.is_top === 1 ? "取消置顶" : "置顶" }}
|
||||
</div>
|
||||
<div
|
||||
@click="handleDelete"
|
||||
class="w-[156rpx] h-[154rpx] text-[#ffffff] bg-[#CF3050] flex items-center justify-center"
|
||||
>
|
||||
删除
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</wd-swipe-action>
|
||||
<div
|
||||
v-if="props.index !== talkStore.talkItems.length - 1"
|
||||
class="divider"
|
||||
></div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import avatarModule from '@/components/avatar-module/index.vue'
|
||||
import { ref, reactive, defineProps, computed } from 'vue'
|
||||
import dayjs from 'dayjs'
|
||||
import { beautifyTime } from '@/utils/datetime'
|
||||
import { ServeClearTalkUnreadNum } from '@/api/chat'
|
||||
import { useTalkStore, useDialogueStore } from '@/store'
|
||||
import { useSessionMenu } from '@/hooks'
|
||||
import avatarModule from "@/components/avatar-module/index.vue";
|
||||
import { ref, reactive, defineProps, computed } from "vue";
|
||||
import dayjs from "dayjs";
|
||||
import { beautifyTime } from "@/utils/datetime";
|
||||
import { ServeClearTalkUnreadNum } from "@/api/chat";
|
||||
import { useTalkStore, useDialogueStore } from "@/store";
|
||||
import { useSessionMenu } from "@/hooks";
|
||||
|
||||
const talkStore = useTalkStore()
|
||||
const { onToTopTalk, onRemoveTalk } = useSessionMenu()
|
||||
const dialogueStore = useDialogueStore()
|
||||
const dialogueParams = reactive({
|
||||
unReadNum: computed(() => dialogueStore.unreadNum),
|
||||
})
|
||||
const talkStore = useTalkStore();
|
||||
const { onToTopTalk, onRemoveTalk } = useSessionMenu();
|
||||
const dialogueStore = useDialogueStore();
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: {},
|
||||
required: true,
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
default: -1,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
data: {
|
||||
type: Object,
|
||||
default: {},
|
||||
required: true,
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
default: -1,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
// 添加格式化方法
|
||||
const formatNameText = (text, maxLength = 16) => {
|
||||
return text.length > maxLength ? `${text.slice(0, maxLength - 1)}...` : text
|
||||
}
|
||||
const formatNameText = (text, maxLength = 19) => {
|
||||
return text.length > maxLength ? `${text.slice(0, maxLength - 1)}...` : text;
|
||||
};
|
||||
|
||||
const cellClick = () => {
|
||||
console.log(props.data)
|
||||
// 更新编辑信息
|
||||
dialogueStore.setDialogue(props.data)
|
||||
console.log(props.data);
|
||||
// 更新编辑信息
|
||||
dialogueStore.setDialogue(props.data);
|
||||
|
||||
// 清空消息未读数
|
||||
if (props.data.unread_num > 0) {
|
||||
ServeClearTalkUnreadNum(
|
||||
{
|
||||
talk_type: props.data.talk_type,
|
||||
receiver_id: props.data.receiver_id,
|
||||
},
|
||||
dialogueParams.unReadNum,
|
||||
).then(() => {
|
||||
talkStore.updateItem({
|
||||
index_name: props.data.index_name,
|
||||
unread_num: 0,
|
||||
})
|
||||
dialogueStore.clearUnreadNum()
|
||||
})
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: `/pages/dialog/index?sessionId=${props.data.id}`,
|
||||
})
|
||||
}
|
||||
// 清空消息未读数
|
||||
if (props.data.unread_num > 0) {
|
||||
ServeClearTalkUnreadNum({
|
||||
talk_type: props.data.talk_type,
|
||||
receiver_id: props.data.receiver_id,
|
||||
}).then(() => {
|
||||
talkStore.updateItem({
|
||||
index_name: props.data.index_name,
|
||||
unread_num: 0,
|
||||
});
|
||||
});
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: "/pages/dialog/index?sessionId=" + props.data.id,
|
||||
});
|
||||
};
|
||||
|
||||
const handleTop = () => {
|
||||
console.log(props.data, 1)
|
||||
onToTopTalk(props.data)
|
||||
}
|
||||
console.log(props.data, 1);
|
||||
onToTopTalk(props.data);
|
||||
};
|
||||
|
||||
//点击删除会话
|
||||
const handleDelete = () => {
|
||||
console.log(props.data)
|
||||
onRemoveTalk(props.data)
|
||||
}
|
||||
console.log(props.data);
|
||||
onRemoveTalk(props.data);
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
::v-deep .swipe_action {
|
||||
// border: 1px solid #fff;
|
||||
// transform: translate3d(1px, 0px, 0px) !important;
|
||||
}
|
||||
::v-deep .badge .round-6 {
|
||||
min-width: 22rpx;
|
||||
min-height: 22rpx;
|
||||
// border: 1px solid #fff;
|
||||
// transform: translate3d(1px, 0px, 0px) !important;
|
||||
}
|
||||
.chatItem {
|
||||
width: 100%;
|
||||
height: 154rpx;
|
||||
padding: 30rpx 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 154rpx;
|
||||
padding: 30rpx 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&.isTop {
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
&.isTop {
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
}
|
||||
|
||||
.chatInfo {
|
||||
flex: 1;
|
||||
margin-left: 20rpx;
|
||||
flex: 1;
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
|
||||
.chatInfo_1 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.chatInfo_2 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 6rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 6rpx;
|
||||
}
|
||||
|
||||
.chatInfo_2_1 {
|
||||
font-size: 28rpx;
|
||||
color: rgba($color: #000000, $alpha: 0.4);
|
||||
font-size: 28rpx;
|
||||
color: rgba($color: #000000, $alpha: 0.4);
|
||||
}
|
||||
.tag {
|
||||
.tag{
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
// margin-left: 10rpx;
|
||||
margin-left: 10rpx;
|
||||
margin-top: 4rpx;
|
||||
vertical-align: top;
|
||||
height: 38rpx;
|
||||
@ -226,22 +202,22 @@ const handleDelete = () => {
|
||||
font-weight: bold;
|
||||
}
|
||||
.companyTag {
|
||||
border: 1px solid #7a58de;
|
||||
color: #7a58de;
|
||||
border: 1px solid #7a58de;
|
||||
color: #7a58de;
|
||||
}
|
||||
.depTag {
|
||||
border: 1px solid #377ec6;
|
||||
color: #377ec6;
|
||||
border: 1px solid #377ec6;
|
||||
color: #377ec6;
|
||||
}
|
||||
.projectTag {
|
||||
border: 1px solid #c1681c;
|
||||
color: #c1681c;
|
||||
border: 1px solid #c1681c;
|
||||
color: #c1681c;
|
||||
}
|
||||
.name_center {
|
||||
.name_center{
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
.name_text {
|
||||
.name_text{
|
||||
display: inline-block;
|
||||
max-height: 88rpx; // 两行文字的高度
|
||||
line-height: 44rpx;
|
||||
@ -253,24 +229,23 @@ const handleDelete = () => {
|
||||
word-break: break-all;
|
||||
}
|
||||
.time_right {
|
||||
/* white-space: nowrap;
|
||||
/* white-space: nowrap;
|
||||
max-width: 146rpx; */
|
||||
flex: 0 0 auto; /* 不伸缩,保持内容宽度 */
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.textEllipsis {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
word-break: break-all;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.divider {
|
||||
background-color: rgba(243, 243, 243, 1);
|
||||
height: 1rpx;
|
||||
margin: 0 18rpx;
|
||||
background-color: rgba(243, 243, 243, 1);
|
||||
height: 1rpx;
|
||||
margin: 0 18rpx;
|
||||
}
|
||||
</style>
|
||||
|
@ -31,25 +31,12 @@
|
||||
>
|
||||
<template v-slot:left>
|
||||
<div class="flex items-center ml-[48rpx]">
|
||||
<!-- <image
|
||||
<image
|
||||
class="w-[72rpx] h-[72rpx]"
|
||||
style="border-radius: 50%;"
|
||||
:src="userStore.avatar"
|
||||
mode="scaleToFill"
|
||||
/> -->
|
||||
<avatarModule
|
||||
:mode="1"
|
||||
:avatar="userStore.avatar"
|
||||
:groupType="0"
|
||||
:userName="userStore.nickname"
|
||||
:customStyle="{ width: '72rpx', height: '72rpx' }"
|
||||
:customTextStyle="{
|
||||
fontSize: '32rpx',
|
||||
fontWeight: 'bold',
|
||||
color: '#fff',
|
||||
lineHeight: '44rpx',
|
||||
}"
|
||||
></avatarModule>
|
||||
/>
|
||||
<div class="ml-[24rpx] text-[36rpx] font-bold">
|
||||
{{ userStore.nickname }}
|
||||
</div>
|
||||
@ -66,11 +53,11 @@
|
||||
/>
|
||||
<template v-slot:label>
|
||||
<div
|
||||
class="w-full px-[14rpx]"
|
||||
class="w-full h-[208rpx] pt-[22rpx] pb-[32rpx] pl-[14rpx] pr-[12rpx]"
|
||||
>
|
||||
<div
|
||||
@click="creatGroupChat"
|
||||
class="flex items-center pl-[22rpx] py-[32rpx]"
|
||||
class="flex items-center pl-[22rpx] mb-[32rpx]"
|
||||
>
|
||||
<div class="mr-[26rpx] flex items-center">
|
||||
<tm-image
|
||||
@ -86,27 +73,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
<!-- <div
|
||||
@click="toAddFriendPage"
|
||||
class="flex items-center pl-[22rpx] py-[32rpx]"
|
||||
>
|
||||
<div class="mr-[26rpx] flex items-center">
|
||||
<tm-image
|
||||
:width="40"
|
||||
:height="44"
|
||||
:src="addFriend"
|
||||
></tm-image>
|
||||
</div>
|
||||
<div
|
||||
class="leading-[54rpx] text-[32rpx] text-[#FFFFFF] font-bold"
|
||||
>
|
||||
添加好友
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider"></div> -->
|
||||
<div
|
||||
@click="toAddressBookPage"
|
||||
class="flex items-center pl-[22rpx] py-[32rpx]"
|
||||
class="flex items-center pl-[22rpx] mt-[32rpx]"
|
||||
>
|
||||
<div class="mr-[26rpx] flex items-center">
|
||||
<tm-image
|
||||
@ -156,23 +125,17 @@ import { ref, watch, computed } from 'vue'
|
||||
import { onShow, onLoad } from '@dcloudio/uni-app'
|
||||
import { useChatList } from '@/store/chatList/index.js'
|
||||
import { useAuth } from '@/store/auth'
|
||||
import { ServeClearTalkUnreadNum } from '@/api/chat'
|
||||
import { useTalkStore, useUserStore, useDialogueStore } from '@/store'
|
||||
import chatItem from './components/chatItem.vue'
|
||||
import addCircle from '@/static/image/chatList/addCircle.png'
|
||||
import cahtPopover from '@/static/image/chatList/cahtPopover.png'
|
||||
import zu3289 from '@/static/image/chatList/zu3289@2x.png'
|
||||
import addFriend from '@/static/image/chatList/addFriend.png'
|
||||
import ZPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
|
||||
import { handleSetWebviewStyle } from '@/utils/common'
|
||||
const paging = ref()
|
||||
const isEmptyViewShow = ref(false)
|
||||
const talkStore = useTalkStore()
|
||||
const userStore = useUserStore()
|
||||
const dialogueStore = useDialogueStore()
|
||||
const dialogueParams = reactive({
|
||||
unReadNum: computed(() => dialogueStore.unreadNum),
|
||||
})
|
||||
const { userInfo } = useAuth()
|
||||
|
||||
const topItems = computed(() => talkStore.topItems)
|
||||
@ -235,24 +198,11 @@ const toSearchPage = () => {
|
||||
})
|
||||
}
|
||||
|
||||
//点击跳转到添加好友页面
|
||||
const toAddFriendPage = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/addressBook/addFriend/index',
|
||||
})
|
||||
}
|
||||
|
||||
//点击跳转到通讯录页面
|
||||
const toAddressBookPage = () => {
|
||||
// 旧版本-按组织架构树的通讯录
|
||||
uni.navigateTo({
|
||||
url: '/pages/chooseByDeps/index?chooseMode=3',
|
||||
})
|
||||
|
||||
// 新版本-按公司别、好友、群组的通讯录
|
||||
// uni.navigateTo({
|
||||
// url: '/pages/addressBook/index',
|
||||
// })
|
||||
}
|
||||
|
||||
/* watch(
|
||||
@ -264,7 +214,6 @@ const toAddressBookPage = () => {
|
||||
); */
|
||||
|
||||
onShow(() => {
|
||||
handleSetWebviewStyle(true)
|
||||
// 页面显示时重新加载数据
|
||||
talkStore
|
||||
.loadTalkList()
|
||||
@ -286,27 +235,21 @@ onLoad((options) => {
|
||||
if (items?.value?.length > 0) {
|
||||
items.value.forEach((openSession) => {
|
||||
if (openSession.index_name === options?.openSessionIndexName) {
|
||||
setTimeout(() => {
|
||||
dialogueStore.setDialogue(openSession)
|
||||
if (openSession.unread_num > 0) {
|
||||
ServeClearTalkUnreadNum(
|
||||
{
|
||||
talk_type: openSession.talk_type,
|
||||
receiver_id: openSession.receiver_id,
|
||||
},
|
||||
dialogueParams.unReadNum,
|
||||
).then(() => {
|
||||
talkStore.updateItem({
|
||||
index_name: openSession.index_name,
|
||||
unread_num: 0,
|
||||
})
|
||||
dialogueStore.clearUnreadNum()
|
||||
dialogueStore.setDialogue(openSession)
|
||||
if (openSession.unread_num > 0) {
|
||||
ServeClearTalkUnreadNum({
|
||||
talk_type: openSession.talk_type,
|
||||
receiver_id: openSession.receiver_id,
|
||||
}).then(() => {
|
||||
talkStore.updateItem({
|
||||
index_name: openSession.index_name,
|
||||
unread_num: 0,
|
||||
})
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: `/pages/dialog/index?sessionId=${openSession.id}`,
|
||||
})
|
||||
}, 500)
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: '/pages/dialog/index?sessionId=' + openSession.id,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -96,30 +96,14 @@ import {
|
||||
import HighlightText from './highLightText.vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { beautifyTime } from '@/utils/datetime'
|
||||
import { ChatMsgTypeMapping } from '@/constant/message'
|
||||
const { t } = useI18n()
|
||||
const props = defineProps({
|
||||
searchItem: Object | Number,
|
||||
searchResultKey: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
searchText: {
|
||||
type: String,
|
||||
default: '',
|
||||
}, //搜索内容
|
||||
searchRecordDetail: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
}, //是否是搜索聊天记录详情
|
||||
pointerIconSrc: {
|
||||
type: String,
|
||||
default: '',
|
||||
}, //箭头图标
|
||||
conditionType: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
}, //搜索类型
|
||||
searchResultKey: String,
|
||||
searchText: String, //搜索内容
|
||||
searchRecordDetail: Boolean, //是否是搜索聊天记录详情
|
||||
pointerIconSrc: String, //箭头图标
|
||||
conditionType: Number, //搜索类型
|
||||
})
|
||||
// 映射表-查找对应结构下的属性名
|
||||
const keyMapping = {
|
||||
@ -176,13 +160,6 @@ const keyMapping = {
|
||||
name: 'receiver_name',
|
||||
group_num: 'group_num',
|
||||
},
|
||||
search_by_member_condition: {
|
||||
avatar: 'avatar',
|
||||
name: 'nickname',
|
||||
created_at: 'created_at',
|
||||
msg_type: 'msg_type',
|
||||
detailKey: 'chatMessageType',
|
||||
},
|
||||
}
|
||||
//获取key对应值
|
||||
const getKeyValue = (keys) => {
|
||||
@ -216,8 +193,10 @@ const imgText = computed(() => {
|
||||
})
|
||||
// 映射表-根据groupType设置对应值
|
||||
const groupTypeMapping = {
|
||||
0: {},
|
||||
1: {},
|
||||
0: {
|
||||
},
|
||||
1: {
|
||||
},
|
||||
2: {
|
||||
result_type: t('index.mine.department'),
|
||||
result_type_color: '#377EC6',
|
||||
@ -262,12 +241,6 @@ const resultDetail = computed(() => {
|
||||
case 'extra':
|
||||
result_detail = props.searchItem?.extra
|
||||
break
|
||||
case 'chatMessageType':
|
||||
result_detail =
|
||||
props.searchItem?.msg_type === 1
|
||||
? props.searchItem?.extra?.content
|
||||
: ChatMsgTypeMapping[props.searchItem?.msg_type]
|
||||
break
|
||||
default:
|
||||
result_detail = ''
|
||||
}
|
||||
@ -302,7 +275,6 @@ const resultDetail = computed(() => {
|
||||
padding: 2rpx 14rpx;
|
||||
border: 2rpx solid #000;
|
||||
border-radius: 6rpx;
|
||||
flex-shrink: 0;
|
||||
span {
|
||||
line-height: 34rpx;
|
||||
}
|
||||
@ -327,7 +299,6 @@ const resultDetail = computed(() => {
|
||||
.info-detail-searchRecordDetail {
|
||||
span {
|
||||
color: $theme-text;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
:default-page-size="props.searchResultPageSize"
|
||||
:loading-more-default-as-loading="true"
|
||||
:inside-more="true"
|
||||
:empty-view-img="searchNoData"
|
||||
:empty-view-img="'/src/static//image/search/search-no-data.png'"
|
||||
:empty-view-text="$t('search.hint')"
|
||||
:empty-view-img-style="{ width: '476rpx', height: '261rpx' }"
|
||||
:empty-view-title-style="{
|
||||
@ -19,7 +19,6 @@
|
||||
'font-size': '28rpx',
|
||||
'font-weight': 400,
|
||||
}"
|
||||
:refresher-enabled="false"
|
||||
>
|
||||
<template #top>
|
||||
<div class="searchRoot">
|
||||
@ -36,10 +35,7 @@
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<div
|
||||
class="search-record-detail"
|
||||
v-if="props.searchRecordDetail && !props?.hideFirstRecord"
|
||||
>
|
||||
<div class="search-record-detail" v-if="props.searchRecordDetail">
|
||||
<searchItem
|
||||
@click="
|
||||
clickSearchItem(
|
||||
@ -113,7 +109,6 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import searchNoData from '@/static/image/search/search-no-data.png'
|
||||
import customInput from '@/components/custom-input/custom-input.vue'
|
||||
import ZPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
|
||||
import useZPaging from '@/uni_modules/z-paging/components/z-paging/js/hooks/useZPaging.js'
|
||||
@ -122,10 +117,6 @@ import { useI18n } from 'vue-i18n'
|
||||
import { ref, reactive, defineEmits, defineProps, onMounted } from 'vue'
|
||||
import pointerIconSrc from '@/static/image/search/search-item-pointer.png'
|
||||
|
||||
import lodash from 'lodash'
|
||||
import { useUserStore } from '@/store'
|
||||
const userStore = useUserStore()
|
||||
|
||||
const zPaging = ref()
|
||||
useZPaging(zPaging)
|
||||
|
||||
@ -140,45 +131,17 @@ const state = reactive({
|
||||
searchResultList: [], //搜素结果列表
|
||||
searchResult: null, //搜索结果
|
||||
pageNum: 1, //当前请求数据页数
|
||||
uid: computed(() => userStore.uid), //当前用户id
|
||||
})
|
||||
|
||||
const props = defineProps({
|
||||
searchResultPageSize: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
}, //搜索结果每页数据量
|
||||
listLimit: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
}, //是否限制列表内数据数量
|
||||
apiParams: {
|
||||
type: String,
|
||||
default: '',
|
||||
}, //请求参数
|
||||
searchResultPageSize: Number, //搜索结果每页数据量
|
||||
listLimit: Boolean, //是否限制列表内数据数量
|
||||
apiParams: String, //请求参数
|
||||
apiRequest: Function, //请求
|
||||
searchText: {
|
||||
type: String,
|
||||
default: '',
|
||||
}, //搜索内容
|
||||
isPagination: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
}, //是否分页
|
||||
searchRecordDetail: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
}, //是否是搜索聊天记录的详情
|
||||
first_talk_record_infos: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
},
|
||||
}, //接受者信息
|
||||
hideFirstRecord: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
}, //是否隐藏前缀及搜索群/用户主体信息
|
||||
searchText: String, //搜索内容
|
||||
isPagination: Boolean, //是否分页
|
||||
searchRecordDetail: Boolean, //是否是搜索聊天记录的详情
|
||||
first_talk_record_infos: Object, //接受者信息
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
@ -191,14 +154,13 @@ onMounted(() => {
|
||||
|
||||
//输入搜索文本
|
||||
const inputSearchText = (e) => {
|
||||
// console.log(e)
|
||||
if (e.trim() != state.searchText.trim()) {
|
||||
state.pageNum = 1
|
||||
state.searchResult = null // 清空搜索结果
|
||||
emits('lastIdChange', 0, 0, 0)
|
||||
}
|
||||
state.searchText = e.trim()
|
||||
if (!e.trim()) {
|
||||
state.searchResult = null // 清空搜索结果
|
||||
emits('lastIdChange', 0, 0, 0)
|
||||
}
|
||||
zPaging.value?.reload()
|
||||
@ -235,35 +197,15 @@ const queryAllSearch = (pageNum, searchResultPageSize) => {
|
||||
})
|
||||
}
|
||||
if ((data.talk_record_infos || []).length > 0) {
|
||||
let receiverInfo = lodash.cloneDeep(data.talk_record_infos[0])
|
||||
if (receiverInfo.talk_type === 1) {
|
||||
//单聊才需此判断
|
||||
if (receiverInfo.user_id === state.uid) {
|
||||
//发送人是自己,接收人不需要变
|
||||
}
|
||||
if (receiverInfo.receiver_id === state.uid) {
|
||||
//接收人是自己,这里需要变成对方
|
||||
let temp_id = receiverInfo.receiver_id
|
||||
let temp_name = receiverInfo.receiver_name
|
||||
let temp_avatar = receiverInfo.receiver_avatar
|
||||
receiverInfo.receiver_id = receiverInfo.user_id
|
||||
receiverInfo.receiver_name = receiverInfo.user_name
|
||||
receiverInfo.receiver_avatar = receiverInfo.user_avatar
|
||||
receiverInfo.user_id = temp_id
|
||||
receiverInfo.user_name = temp_name
|
||||
receiverInfo.user_avatar = temp_avatar
|
||||
}
|
||||
}
|
||||
state.first_talk_record_infos = Object.assign(
|
||||
{},
|
||||
state.first_talk_record_infos,
|
||||
receiverInfo,
|
||||
data.talk_record_infos[0],
|
||||
)
|
||||
;(data.talk_record_infos || []).forEach((item) => {
|
||||
item.group_type = 0
|
||||
})
|
||||
}
|
||||
|
||||
let tempGeneral_infos = Array.isArray(data.general_infos)
|
||||
? [...data.general_infos]
|
||||
: data.general_infos
|
||||
@ -272,8 +214,6 @@ const queryAllSearch = (pageNum, searchResultPageSize) => {
|
||||
data.group_member_infos || [],
|
||||
)
|
||||
data.general_infos = tempGeneral_infos
|
||||
|
||||
// 检查数据是否为空
|
||||
let isEmpty = true
|
||||
let dataKeys = Object.keys(data)
|
||||
let paginationKey = ''
|
||||
@ -283,147 +223,58 @@ const queryAllSearch = (pageNum, searchResultPageSize) => {
|
||||
isEmpty = false
|
||||
}
|
||||
})
|
||||
|
||||
if (isEmpty) {
|
||||
if (pageNum === 1) {
|
||||
// 第一页请求且为空,清空结果
|
||||
state.searchResult = null
|
||||
if (pageNum == 1) {
|
||||
zPaging.value?.complete([])
|
||||
} else {
|
||||
// 加载更多且为空,保持原列表不变
|
||||
zPaging.value?.complete(
|
||||
state.searchResult ? [state.searchResult] : [],
|
||||
)
|
||||
data = state.searchResult
|
||||
zPaging.value?.complete([data])
|
||||
}
|
||||
} else {
|
||||
if (props.isPagination) {
|
||||
if (pageNum === 1) {
|
||||
// 第一页请求,直接设置新数据
|
||||
state.searchResult = data
|
||||
} else {
|
||||
// 加载更多,合并数据
|
||||
if (
|
||||
paginationKey &&
|
||||
Array.isArray(
|
||||
(state?.searchResult && state?.searchResult[paginationKey]) ||
|
||||
[],
|
||||
)
|
||||
) {
|
||||
data[paginationKey] = state.searchResult[paginationKey].concat(
|
||||
data[paginationKey],
|
||||
)
|
||||
}
|
||||
state.searchResult = data
|
||||
if (
|
||||
paginationKey &&
|
||||
Array.isArray(
|
||||
(state?.searchResult && state?.searchResult[paginationKey]) || [],
|
||||
) &&
|
||||
((state?.searchResult && state?.searchResult[paginationKey]) || [])
|
||||
.length > 0
|
||||
) {
|
||||
data[paginationKey] = state.searchResult[paginationKey].concat(
|
||||
data[paginationKey],
|
||||
)
|
||||
}
|
||||
|
||||
emits(
|
||||
'lastIdChange',
|
||||
data.last_id,
|
||||
data.last_group_id,
|
||||
data.last_member_id,
|
||||
data.last_receiver_user_name,
|
||||
data.last_receiver_group_name,
|
||||
)
|
||||
let total = data.count
|
||||
if (props.searchRecordDetail) {
|
||||
if (state?.first_talk_record_infos?.talk_type === 1) {
|
||||
total = data.user_record_count
|
||||
} else if (state?.first_talk_record_infos?.talk_type === 2) {
|
||||
total = data.group_record_count
|
||||
}
|
||||
let noMoreSearchResultRecord = true
|
||||
if (
|
||||
Object.keys(data).includes('talk_record_infos') &&
|
||||
state.searchResult['talk_record_infos']?.length > 0
|
||||
) {
|
||||
//搜聊天记录详情
|
||||
if (state.searchResult['talk_record_infos']?.length < total) {
|
||||
noMoreSearchResultRecord = false
|
||||
}
|
||||
}
|
||||
zPaging.value?.completeByNoMore([data], noMoreSearchResultRecord)
|
||||
} else {
|
||||
let noMoreSearchResultUser = true
|
||||
let noMoreSearchResultGroup = true
|
||||
let noMoreSearchResultGeneral = true
|
||||
if (
|
||||
Object.keys(data).includes('user_infos') &&
|
||||
state.searchResult['user_infos']?.length > 0
|
||||
) {
|
||||
//搜人
|
||||
if (state.searchResult['user_infos']?.length < total) {
|
||||
noMoreSearchResultUser = false
|
||||
}
|
||||
}
|
||||
if (
|
||||
Object.keys(data).includes(
|
||||
'group_member_infos' || 'group_infos',
|
||||
) &&
|
||||
state.searchResult['combinedGroup']?.length > 0
|
||||
) {
|
||||
//搜群
|
||||
if (state.searchResult['combinedGroup']?.length < total) {
|
||||
noMoreSearchResultGroup = false
|
||||
}
|
||||
}
|
||||
if (
|
||||
Object.keys(data).includes('general_infos') &&
|
||||
state.searchResult['general_infos']?.length > 0
|
||||
) {
|
||||
//搜聊天记录
|
||||
if (state.searchResult['general_infos']?.length < total) {
|
||||
noMoreSearchResultGeneral = false
|
||||
}
|
||||
}
|
||||
// zPaging.value?.completeByTotal([data], total)
|
||||
zPaging.value?.completeByNoMore(
|
||||
[data],
|
||||
noMoreSearchResultUser &&
|
||||
noMoreSearchResultGroup &&
|
||||
noMoreSearchResultGeneral,
|
||||
)
|
||||
total = data.group_record_count
|
||||
}
|
||||
zPaging.value?.completeByTotal([data], total)
|
||||
} else {
|
||||
state.searchResult = data
|
||||
zPaging.value?.complete([data])
|
||||
}
|
||||
}
|
||||
state.searchResult = data
|
||||
} else {
|
||||
if (pageNum === 1) {
|
||||
// 第一页请求失败,清空结果
|
||||
state.searchResult = null
|
||||
zPaging.value?.complete([])
|
||||
} else {
|
||||
// 加载更多失败,保持原列表不变
|
||||
zPaging.value?.complete(state.searchResult ? [state.searchResult] : [])
|
||||
}
|
||||
zPaging.value?.complete([])
|
||||
}
|
||||
})
|
||||
|
||||
resp.catch(() => {
|
||||
if (pageNum === 1) {
|
||||
// 第一页请求异常,清空结果
|
||||
state.searchResult = null
|
||||
zPaging.value?.complete([])
|
||||
} else {
|
||||
// 加载更多异常,保持原列表不变
|
||||
zPaging.value?.complete(state.searchResult ? [state.searchResult] : [])
|
||||
}
|
||||
zPaging.value?.complete([])
|
||||
})
|
||||
}
|
||||
|
||||
//点击取消搜索
|
||||
const cancelSearch = () => {
|
||||
const pages = getCurrentPages()
|
||||
if (pages.length > 1) {
|
||||
uni.navigateBack({
|
||||
delta: 1,
|
||||
})
|
||||
} else {
|
||||
uni.reLaunch({
|
||||
url: '/pages/index/index',
|
||||
})
|
||||
}
|
||||
uni.navigateBack({
|
||||
delta: 1,
|
||||
})
|
||||
}
|
||||
|
||||
//获取key对应值
|
||||
@ -491,12 +342,6 @@ const getHasMoreResult = (searchResultKey) => {
|
||||
}
|
||||
break
|
||||
case 'general_infos':
|
||||
if (
|
||||
state.searchResult['record_count'] &&
|
||||
state.searchResult['record_count'] >= 3
|
||||
) {
|
||||
has_more_result = t('has_more') + t('chat.type.record')
|
||||
}
|
||||
break
|
||||
default:
|
||||
}
|
||||
@ -510,41 +355,12 @@ const toMoreResultPage = (searchResultKey) => {
|
||||
|
||||
//点击了搜索结果项
|
||||
const clickSearchItem = (searchResultKey, searchItem) => {
|
||||
console.log(searchResultKey, searchItem)
|
||||
let talk_type = searchItem.talk_type
|
||||
let receiver_id = searchItem.receiver_id
|
||||
if (searchResultKey === 'user_infos') {
|
||||
talk_type = 1
|
||||
receiver_id = searchItem.id
|
||||
} else if (searchResultKey === 'combinedGroup') {
|
||||
talk_type = searchItem.type || 2
|
||||
receiver_id = searchItem.group_id || searchItem.id
|
||||
} else if (searchResultKey === 'general_infos') {
|
||||
if (searchItem.talk_type === 1) {
|
||||
if (searchItem.user_id === state.uid) {
|
||||
//发送人是自己,接收人不需要变
|
||||
}
|
||||
if (searchItem.receiver_id === state.uid) {
|
||||
//接收人是自己,这里需要变成对方
|
||||
let temp_id = searchItem.receiver_id
|
||||
let temp_name = searchItem.receiver_name
|
||||
let temp_avatar = searchItem.receiver_avatar
|
||||
searchItem.receiver_id = searchItem.user_id
|
||||
searchItem.receiver_name = searchItem.user_name
|
||||
searchItem.receiver_avatar = searchItem.user_avatar
|
||||
searchItem.user_id = temp_id
|
||||
searchItem.user_name = temp_name
|
||||
searchItem.user_avatar = temp_avatar
|
||||
}
|
||||
}
|
||||
}
|
||||
emits(
|
||||
'clickSearchItem',
|
||||
state.searchText,
|
||||
searchResultKey,
|
||||
talk_type,
|
||||
receiver_id,
|
||||
encodeURIComponent(JSON.stringify(searchItem)),
|
||||
searchItem.talk_type,
|
||||
searchItem.receiver_id,
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
@ -13,19 +13,7 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import searchList from './components/searchList.vue'
|
||||
import { ServeSeachQueryAll, ServeGetSessionId } from '@/api/search/index'
|
||||
import { onMounted } from 'vue'
|
||||
import { handleSetWebviewStyle } from '@/utils/common'
|
||||
|
||||
import { useDialogueStore, useTalkStore } from '@/store'
|
||||
import { ServeCreateTalkList } from '@/api/chat/index.js'
|
||||
import { formatTalkItem } from '@/utils/talk'
|
||||
|
||||
const dialogueStore = useDialogueStore()
|
||||
|
||||
onMounted(() => {
|
||||
handleSetWebviewStyle()
|
||||
})
|
||||
import { ServeSeachQueryAll } from '@/api/search/index'
|
||||
|
||||
//点击跳转到更多结果页面
|
||||
const toMoreResultPage = (searchResultKey, searchText) => {
|
||||
@ -39,49 +27,14 @@ const toMoreResultPage = (searchResultKey, searchText) => {
|
||||
}
|
||||
|
||||
//点击了搜索结果项
|
||||
const clickSearchItem = async (
|
||||
const clickSearchItem = (
|
||||
searchText,
|
||||
searchResultKey,
|
||||
talk_type,
|
||||
receiver_id,
|
||||
res,
|
||||
) => {
|
||||
console.log(searchResultKey)
|
||||
const result = JSON.parse(decodeURIComponent(res))
|
||||
console.log(result)
|
||||
console.log(talk_type, receiver_id)
|
||||
const sessionId = await getSessionId(talk_type, receiver_id)
|
||||
if (searchResultKey === 'user_infos') {
|
||||
if (useTalkStore().findTalkIndex(`${talk_type}_${receiver_id}`) === -1) {
|
||||
ServeCreateTalkList({
|
||||
talk_type,
|
||||
receiver_id,
|
||||
erp_user_id: result.erp_user_id,
|
||||
}).then(async ({ code, data }) => {
|
||||
if (code == 200) {
|
||||
let item = formatTalkItem(data)
|
||||
useTalkStore().addItem(item)
|
||||
}
|
||||
})
|
||||
}
|
||||
dialogueStore.setDialogue({
|
||||
name: result.nickname,
|
||||
talk_type: 1,
|
||||
receiver_id: receiver_id,
|
||||
})
|
||||
uni.navigateTo({
|
||||
url: '/pages/dialog/index?sessionId=' + sessionId,
|
||||
})
|
||||
} else if (searchResultKey === 'combinedGroup') {
|
||||
dialogueStore.setDialogue({
|
||||
name: result.name || result.group_name,
|
||||
talk_type: result.type || 2,
|
||||
receiver_id: result.group_id || result.id,
|
||||
})
|
||||
uni.navigateTo({
|
||||
url: '/pages/dialog/index?sessionId=' + sessionId,
|
||||
})
|
||||
} else if (searchResultKey === 'general_infos') {
|
||||
if (searchResultKey === 'general_infos') {
|
||||
uni.navigateTo({
|
||||
url:
|
||||
'/pages/search/moreResult/moreResultDetail?searchText=' +
|
||||
@ -93,25 +46,5 @@ const clickSearchItem = async (
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//获取会话Id
|
||||
const getSessionId = (talk_type, receiver_id) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let params = {
|
||||
talkType: talk_type,
|
||||
receiverId: receiver_id,
|
||||
}
|
||||
const resp = ServeGetSessionId(params)
|
||||
console.log(resp)
|
||||
resp.then(({ code, data }) => {
|
||||
console.log(data)
|
||||
if (code == 200) {
|
||||
resolve(data?.sessionId)
|
||||
} else {
|
||||
}
|
||||
})
|
||||
resp.catch(() => {})
|
||||
})
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss"></style>
|
||||
|
@ -21,21 +21,14 @@ import {
|
||||
ServeQueryUser,
|
||||
ServeQueryGroup,
|
||||
ServeTalkRecord,
|
||||
ServeGetSessionId,
|
||||
} from '@/api/search/index'
|
||||
import { reactive } from 'vue'
|
||||
|
||||
import { useDialogueStore, useTalkStore } from '@/store'
|
||||
import { ServeCreateTalkList } from '@/api/chat/index.js'
|
||||
import { formatTalkItem } from '@/utils/talk'
|
||||
|
||||
const dialogueStore = useDialogueStore()
|
||||
|
||||
const state = reactive({
|
||||
apiRequest: Function,
|
||||
apiParams: '',
|
||||
searchText: '',
|
||||
searchResultKey: '',
|
||||
apiParams: String,
|
||||
searchText: String,
|
||||
searchResultKey: String,
|
||||
})
|
||||
|
||||
onLoad((options) => {
|
||||
@ -64,8 +57,6 @@ onLoad((options) => {
|
||||
receiver_id: 0, //查详情的时候需传入
|
||||
last_group_id: 0, //最后一条群id
|
||||
last_member_id: 0, //最后一条用户id
|
||||
last_receiver_user_name: '', //最后一条用户名
|
||||
last_receiver_group_name: '', //最后一条群名
|
||||
}),
|
||||
)
|
||||
state.apiRequest = ServeTalkRecord
|
||||
@ -77,19 +68,11 @@ onLoad((options) => {
|
||||
})
|
||||
|
||||
//分页查询时,最后一条id变化
|
||||
const lastIdChange = (
|
||||
last_id,
|
||||
last_group_id,
|
||||
last_member_id,
|
||||
last_receiver_user_name,
|
||||
last_receiver_group_name,
|
||||
) => {
|
||||
const lastIdChange = (last_id, last_group_id, last_member_id) => {
|
||||
let idChanges = {
|
||||
last_id,
|
||||
last_group_id,
|
||||
last_member_id,
|
||||
last_receiver_user_name,
|
||||
last_receiver_group_name,
|
||||
}
|
||||
state.apiParams = encodeURIComponent(
|
||||
JSON.stringify(
|
||||
@ -103,79 +86,12 @@ const lastIdChange = (
|
||||
}
|
||||
|
||||
//点击了搜索结果项
|
||||
const clickSearchItem = async (
|
||||
searchText,
|
||||
searchResultKey,
|
||||
talk_type,
|
||||
receiver_id,
|
||||
res,
|
||||
) => {
|
||||
console.log(state.searchResultKey)
|
||||
const result = JSON.parse(decodeURIComponent(res))
|
||||
console.log(result)
|
||||
console.log(talk_type, receiver_id)
|
||||
const sessionId = await getSessionId(talk_type, receiver_id)
|
||||
if (state.searchResultKey === 'user_infos') {
|
||||
if (useTalkStore().findTalkIndex(`${talk_type}_${receiver_id}`) === -1) {
|
||||
ServeCreateTalkList({
|
||||
talk_type,
|
||||
receiver_id,
|
||||
erp_user_id: result.erp_user_id,
|
||||
}).then(async ({ code, data }) => {
|
||||
if (code == 200) {
|
||||
let item = formatTalkItem(data)
|
||||
useTalkStore().addItem(item)
|
||||
}
|
||||
})
|
||||
}
|
||||
dialogueStore.setDialogue({
|
||||
name: result.nickname,
|
||||
talk_type: 1,
|
||||
receiver_id: receiver_id,
|
||||
})
|
||||
const clickSearchItem = (searchText) => {
|
||||
if (state.searchResultKey === 'general_infos') {
|
||||
uni.navigateTo({
|
||||
url: '/pages/dialog/index?sessionId=' + sessionId,
|
||||
})
|
||||
} else if (state.searchResultKey === 'combinedGroup') {
|
||||
dialogueStore.setDialogue({
|
||||
name: result.name || result.group_name,
|
||||
talk_type: result.type || 2,
|
||||
receiver_id: result.group_id || result.id,
|
||||
})
|
||||
uni.navigateTo({
|
||||
url: '/pages/dialog/index?sessionId=' + sessionId,
|
||||
})
|
||||
} else if (state.searchResultKey === 'general_infos') {
|
||||
uni.navigateTo({
|
||||
url:
|
||||
'/pages/search/moreResult/moreResultDetail?searchText=' +
|
||||
searchText +
|
||||
'&talk_type=' +
|
||||
talk_type +
|
||||
'&receiver_id=' +
|
||||
receiver_id,
|
||||
url: '/pages/search/moreResult/moreResultDetail?searchText=' + searchText,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//获取会话Id
|
||||
const getSessionId = (talk_type, receiver_id) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let params = {
|
||||
talkType: talk_type,
|
||||
receiverId: receiver_id,
|
||||
}
|
||||
const resp = ServeGetSessionId(params)
|
||||
console.log(resp)
|
||||
resp.then(({ code, data }) => {
|
||||
console.log(data)
|
||||
if (code == 200) {
|
||||
resolve(data?.sessionId)
|
||||
} else {
|
||||
}
|
||||
})
|
||||
resp.catch(() => {})
|
||||
})
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss"></style>
|
||||
|
@ -7,11 +7,9 @@
|
||||
:apiRequest="ServeTalkRecord"
|
||||
:apiParams="state.apiParams"
|
||||
:searchText="state.searchText"
|
||||
:hideFirstRecord="state.hideFirstRecord"
|
||||
:isPagination="true"
|
||||
:searchRecordDetail="true"
|
||||
@lastIdChange="lastIdChange"
|
||||
@clickSearchItem="clickSearchItem"
|
||||
></searchList>
|
||||
</div>
|
||||
</div>
|
||||
@ -21,17 +19,10 @@ import searchList from '../components/searchList.vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { ServeTalkRecord } from '@/api/search/index'
|
||||
import { reactive } from 'vue'
|
||||
import { useDialogueStore, useUserStore } from '@/store'
|
||||
import lodash from 'lodash'
|
||||
|
||||
const dialogueStore = useDialogueStore()
|
||||
const userStore = useUserStore()
|
||||
|
||||
const state = reactive({
|
||||
apiParams: '',
|
||||
searchText: '',
|
||||
uid: computed(() => userStore.uid), //当前用户id
|
||||
hideFirstRecord: false, //是否隐藏前缀及搜索群/用户主体信息
|
||||
apiParams: String,
|
||||
searchText: String,
|
||||
})
|
||||
|
||||
onLoad((options) => {
|
||||
@ -57,10 +48,6 @@ onLoad((options) => {
|
||||
}
|
||||
|
||||
console.log(JSON.parse(decodeURIComponent(state.apiParams)))
|
||||
|
||||
if (options.hideFirstRecord) {
|
||||
state.hideFirstRecord = options.hideFirstRecord === '1' ? true : false
|
||||
}
|
||||
})
|
||||
|
||||
//分页查询时,最后一条id变化
|
||||
@ -80,51 +67,5 @@ const lastIdChange = (last_id, last_group_id, last_member_id) => {
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
//点击了搜索结果项
|
||||
const clickSearchItem = (
|
||||
searchText,
|
||||
searchResultKey,
|
||||
talk_type,
|
||||
receiver_id,
|
||||
res,
|
||||
) => {
|
||||
console.log(searchResultKey)
|
||||
let result = JSON.parse(decodeURIComponent(res))
|
||||
console.log(result)
|
||||
let receiverInfo = lodash.cloneDeep(result)
|
||||
if (receiverInfo.talk_type === 1) {
|
||||
//单聊才需此判断
|
||||
if (receiverInfo.user_id === state.uid) {
|
||||
//发送人是自己,接收人不需要变
|
||||
}
|
||||
if (receiverInfo.receiver_id === state.uid) {
|
||||
//接收人是自己,这里需要变成对方
|
||||
let temp_id = receiverInfo.receiver_id
|
||||
let temp_name = receiverInfo.receiver_name
|
||||
let temp_avatar = receiverInfo.receiver_avatar
|
||||
receiverInfo.receiver_id = receiverInfo.user_id
|
||||
receiverInfo.receiver_name = receiverInfo.user_name
|
||||
receiverInfo.receiver_avatar = receiverInfo.user_avatar
|
||||
receiverInfo.user_id = temp_id
|
||||
receiverInfo.user_name = temp_name
|
||||
receiverInfo.user_avatar = temp_avatar
|
||||
}
|
||||
}
|
||||
dialogueStore.setDialogue({
|
||||
name: receiverInfo.receiver_name,
|
||||
talk_type: talk_type,
|
||||
receiver_id: receiverInfo.receiver_id,
|
||||
})
|
||||
if (searchResultKey === 'talk_record_infos_receiver') {
|
||||
uni.navigateTo({
|
||||
url: '/pages/dialog/index',
|
||||
})
|
||||
} else {
|
||||
uni.navigateTo({
|
||||
url: '/pages/dialog/index?msgInfo=' + res + '&keepDialogInfo=1',
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss"></style>
|
||||
|
@ -9,7 +9,6 @@
|
||||
:auto="false"
|
||||
:loading-more-default-as-loading="true"
|
||||
:inside-more="true"
|
||||
v-model="state.flatList"
|
||||
>
|
||||
<template #top v-if="state.showPageTitle">
|
||||
<customNavbar :title="state.pageTitle"></customNavbar>
|
||||
@ -42,7 +41,7 @@
|
||||
<span class="text-[28rpx] font-regular">
|
||||
{{ state.selectedMonth }}
|
||||
</span>
|
||||
<img src="@/static/image/search/down-pointer.png" />
|
||||
<img src="/src/static/image/search/down-pointer.png" />
|
||||
</div>
|
||||
</tm-time-picker>
|
||||
<tm-calendar-view
|
||||
@ -65,8 +64,7 @@
|
||||
v-if="
|
||||
state.condition === 'imgAndVideo' ||
|
||||
state.condition === 'file' ||
|
||||
state.condition === 'link' ||
|
||||
state.condition === 'member'
|
||||
state.condition === 'link'
|
||||
"
|
||||
:style="{
|
||||
padding: state.condition === 'imgAndVideo' ? '0 27rpx' : '',
|
||||
@ -114,27 +112,11 @@
|
||||
v-for="(item, index) in conditionItem.monthResultList"
|
||||
:key="index"
|
||||
:style="{
|
||||
border:
|
||||
state.condition === 'imgAndVideo' ||
|
||||
state.condition === 'member'
|
||||
? '0'
|
||||
: '',
|
||||
border: state.condition === 'imgAndVideo' ? '0' : '',
|
||||
padding:
|
||||
state.condition === 'imgAndVideo' ? '0 0 10rpx' : '',
|
||||
}"
|
||||
>
|
||||
<div
|
||||
class="condition-result-member"
|
||||
v-if="state.condition === 'member'"
|
||||
>
|
||||
<searchItem
|
||||
@click="toDialogueByMember(item)"
|
||||
:searchResultKey="'search_by_member_condition'"
|
||||
:searchItem="item"
|
||||
:searchText="state.searchText"
|
||||
:searchRecordDetail="true"
|
||||
></searchItem>
|
||||
</div>
|
||||
<div
|
||||
class="condition-result-imgAndVideo"
|
||||
v-if="state.condition === 'imgAndVideo'"
|
||||
@ -160,27 +142,17 @@
|
||||
class="condition-result-imgAndVideo-area"
|
||||
v-if="item?.extra?.url"
|
||||
>
|
||||
<template v-if="item?.msg_type === 3">
|
||||
<tm-image
|
||||
preview
|
||||
:src="item?.extra?.url"
|
||||
model="aspectFill"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="item?.msg_type === 5">
|
||||
<div
|
||||
class="video-preview"
|
||||
@click="onPlay(item?.extra?.url)"
|
||||
>
|
||||
<tm-image
|
||||
:src="item?.extra?.cover"
|
||||
model="aspectFill"
|
||||
/>
|
||||
<div class="play-icon">
|
||||
<img :src="playCircle" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<tm-image
|
||||
preview
|
||||
:src="
|
||||
item?.msg_type === 3
|
||||
? item?.extra?.url
|
||||
: item?.msg_type === 5
|
||||
? item?.extra?.cover
|
||||
: ''
|
||||
"
|
||||
model="aspectFill"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@ -201,7 +173,6 @@
|
||||
</div>
|
||||
<div
|
||||
class="condition-each-result-attachments"
|
||||
@click="previewPDF(item)"
|
||||
v-if="
|
||||
state.condition === 'file' || state.condition === 'link'
|
||||
"
|
||||
@ -212,7 +183,7 @@
|
||||
v-if="state.condition === 'file'"
|
||||
/>
|
||||
<img
|
||||
src="@/static/image/search/result-link-icon.png"
|
||||
src="/src/static/image/search/result-link-icon.png"
|
||||
v-if="state.condition === 'link'"
|
||||
/>
|
||||
</div>
|
||||
@ -267,20 +238,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</ZPaging>
|
||||
<teleport to="body">
|
||||
<div v-show="open" class="video-container">
|
||||
<video
|
||||
:src="currentVideoUrl"
|
||||
controls
|
||||
@fullscreenchange="fullscreenchange"
|
||||
:id="currentVideoUrl"
|
||||
playsinline
|
||||
webkit-playsinline
|
||||
x5-playsinline
|
||||
class="fullscreen-video"
|
||||
></video>
|
||||
</div>
|
||||
</teleport>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -290,16 +247,15 @@ import fileType_EXCEL from '@/static/image/search/fileType_EXCEL.png'
|
||||
import fileType_WORD from '@/static/image/search/fileType_WORD.png'
|
||||
import fileType_PDF from '@/static/image/search/fileType_PDF.png'
|
||||
import fileType_Files from '@/static/image/search/fileType_Files.png'
|
||||
import playCircle from '@/static/image/chatList/playCircle@2x.png'
|
||||
import { fileFormatSize, fileSuffix } from '@/utils/strings'
|
||||
import searchItem from '../components/searchItem.vue'
|
||||
import customInput from '@/components/custom-input/custom-input.vue'
|
||||
import ZPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
|
||||
import useZPaging from '@/uni_modules/z-paging/components/z-paging/js/hooks/useZPaging.js'
|
||||
import { parseTime } from '@/utils/datetime'
|
||||
import { onMounted, reactive, computed, ref, nextTick } from 'vue'
|
||||
import { onMounted, reactive, computed, ref } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { ServeTalkDate, ServeGetSessionId } from '@/api/search/index'
|
||||
import { ServeTalkDate } from '@/api/search/index'
|
||||
import { ServeFindTalkRecords } from '@/api/chat/index'
|
||||
import { useDialogueStore } from '@/store'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
@ -317,6 +273,7 @@ const dialogueParams = reactive({
|
||||
let nowDay = new Date().setHours(0, 0, 0, 0)
|
||||
|
||||
const state = reactive({
|
||||
receiver_id: '', //目标人id
|
||||
pageTitle: '', //页面标题
|
||||
dateStyle: [], //日期样式
|
||||
nowDate: new Date(nowDay), //当前时间
|
||||
@ -332,53 +289,16 @@ const state = reactive({
|
||||
searchResultList: [], //搜索结果列表
|
||||
cursor: 0, //上次查询的游标
|
||||
msg_type: 0, //查询的消息类型
|
||||
group_member_id: 0, //群成员id
|
||||
flatList: [], // 用于存储扁平化的数据
|
||||
})
|
||||
|
||||
const videoContext = ref()
|
||||
const open = ref(false)
|
||||
const currentVideoUrl = ref('')
|
||||
|
||||
const fullscreenchange = (e) => {
|
||||
if (!e.detail.fullScreen) {
|
||||
videoContext.value.stop()
|
||||
videoContext.value.seek(0)
|
||||
open.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function onPlay(url) {
|
||||
currentVideoUrl.value = url
|
||||
open.value = true
|
||||
|
||||
// 等待 DOM 更新
|
||||
await nextTick()
|
||||
|
||||
// 创建新的视频上下文
|
||||
videoContext.value = uni.createVideoContext(url, getCurrentInstance())
|
||||
|
||||
setTimeout(() => {
|
||||
// 先请求全屏
|
||||
videoContext.value.requestFullScreen({ direction: 2 })
|
||||
|
||||
// 延迟一下再播放,确保全屏已经完成
|
||||
setTimeout(() => {
|
||||
videoContext.value.play()
|
||||
}, 100)
|
||||
}, 200)
|
||||
}
|
||||
|
||||
onLoad((options) => {
|
||||
console.log(options)
|
||||
if (options.receiver_id) {
|
||||
state.receiver_id = Number(options.receiver_id)
|
||||
}
|
||||
if (options.condition) {
|
||||
state.condition = options.condition
|
||||
if (options.condition === 'member') {
|
||||
state.showPageTitle = true
|
||||
state.pageTitle = t('search.condition.member')
|
||||
state.group_member_id = options.groupMemberId
|
||||
queryAllSearch()
|
||||
} else if (options.condition === 'date') {
|
||||
if (options.condition === 'date') {
|
||||
state.showPageTitle = true
|
||||
state.pageTitle = t('search.condition.date')
|
||||
ServeQueryTalkDate(parseTime(state.nowDate, '{y}{m}'))
|
||||
@ -431,8 +351,8 @@ onMounted(() => {
|
||||
const ServeQueryTalkDate = (month) => {
|
||||
let params = {
|
||||
month: month,
|
||||
talk_type: dialogueParams.talk_type, //1私聊2群聊
|
||||
receiver_id: dialogueParams.receiver_id, //目标人id
|
||||
talk_type: 2, //1私聊2群聊
|
||||
receiver_id: state.receiver_id, //目标人id
|
||||
}
|
||||
const resp = ServeTalkDate(params)
|
||||
console.log(resp)
|
||||
@ -467,7 +387,7 @@ const ServeQueryTalkDate = (month) => {
|
||||
}
|
||||
|
||||
//点击选择日期
|
||||
const selectDate = async (e) => {
|
||||
const selectDate = (e) => {
|
||||
if (e == parseTime(state.nowDate, '{y}/{m}/{d}')) {
|
||||
console.log('==今日')
|
||||
state.dateStyle = [
|
||||
@ -493,38 +413,6 @@ const selectDate = async (e) => {
|
||||
},
|
||||
]
|
||||
}
|
||||
const sessionId = await getSessionId(
|
||||
dialogueParams.talk_type,
|
||||
dialogueParams.receiver_id,
|
||||
)
|
||||
uni.navigateTo({
|
||||
url:
|
||||
'/pages/dialog/index?sessionId=' +
|
||||
sessionId +
|
||||
'&keepDialogInfo=1' +
|
||||
'&recordDate=' +
|
||||
parseTime(e, '{y}-{m}-{d}'),
|
||||
})
|
||||
}
|
||||
|
||||
//获取会话Id
|
||||
const getSessionId = (talk_type, receiver_id) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let params = {
|
||||
talkType: talk_type,
|
||||
receiverId: receiver_id,
|
||||
}
|
||||
const resp = ServeGetSessionId(params)
|
||||
console.log(resp)
|
||||
resp.then(({ code, data }) => {
|
||||
console.log(data)
|
||||
if (code == 200) {
|
||||
resolve(data?.sessionId)
|
||||
} else {
|
||||
}
|
||||
})
|
||||
resp.catch(() => {})
|
||||
})
|
||||
}
|
||||
|
||||
//点击确认选择月份
|
||||
@ -555,22 +443,13 @@ const getDArray = (dArray) => {
|
||||
//输入搜索内容
|
||||
const inputSearchText = (e) => {
|
||||
state.searchText = e
|
||||
state.cursor = 0
|
||||
queryAllSearch()
|
||||
}
|
||||
|
||||
//点击取消搜索
|
||||
const cancelSearch = () => {
|
||||
const pages = getCurrentPages()
|
||||
if (pages.length > 1) {
|
||||
uni.navigateBack({
|
||||
delta: 1,
|
||||
})
|
||||
} else {
|
||||
uni.reLaunch({
|
||||
url: '/pages/index/index',
|
||||
})
|
||||
}
|
||||
uni.navigateBack({
|
||||
delta: 1,
|
||||
})
|
||||
}
|
||||
|
||||
//查询数据
|
||||
@ -585,8 +464,7 @@ const queryAllSearch = () => {
|
||||
direction: 'up', //down向下查最新,up向上查老数据
|
||||
start_time: '',
|
||||
end_time: '',
|
||||
group_member_user_id: state.group_member_id, //群成员id,当查询群历史消息的时候,需要指定群成员的时候送
|
||||
file_name: state.msg_type === 6 ? state.searchText : '',
|
||||
group_member_user_id: 0, //群成员id,当查询群历史消息的时候,需要指定群成员的时候送
|
||||
}
|
||||
console.log(params)
|
||||
const resp = ServeFindTalkRecords(params)
|
||||
@ -594,20 +472,17 @@ const queryAllSearch = () => {
|
||||
resp.then(({ code, data }) => {
|
||||
console.log(data)
|
||||
if (code == 200) {
|
||||
// 当cursor为0时,清空searchResultList
|
||||
let dateList = state.cursor === 0 ? [] : state.searchResultList
|
||||
let dateList = state.searchResultList
|
||||
let noMore = false
|
||||
if (data?.items?.length > 0) {
|
||||
data.items.forEach((item) => {
|
||||
item.dateTime = parseTime(item?.created_at, '{m}/{d}')
|
||||
if (item?.extra) {
|
||||
item.extra.fileSize = fileFormatSize(item?.extra?.size)
|
||||
item.extra.typeText = item?.extra?.name
|
||||
? fileSuffix(item?.extra?.name)
|
||||
: ''
|
||||
item.extra.file_avatar = fileTypeAvatar(item?.extra?.typeText)
|
||||
console.log(item.extra.type)
|
||||
}
|
||||
item.extra.fileSize = fileFormatSize(item?.extra?.size)
|
||||
item.extra.typeText = item?.extra?.name
|
||||
? fileSuffix(item?.extra?.name)
|
||||
: ''
|
||||
item.extra.file_avatar = fileTypeAvatar(item?.extra?.typeText)
|
||||
console.log(item.extra.type)
|
||||
let year = new Date(item.created_at).getFullYear()
|
||||
let month = new Date(item.created_at).getMonth() + 1
|
||||
let dateMonth =
|
||||
@ -640,35 +515,15 @@ const queryAllSearch = () => {
|
||||
} else {
|
||||
noMore = true
|
||||
}
|
||||
|
||||
// 保存分组数据用于显示
|
||||
state.searchResultList = dateList
|
||||
|
||||
// 将分组数据扁平化,用于z-paging分页
|
||||
state.flatList = dateList.reduce((acc, group) => {
|
||||
return acc.concat(group.monthResultList)
|
||||
}, [])
|
||||
|
||||
if (state.cursor === 0) {
|
||||
zPaging.value?.complete(state.flatList)
|
||||
} else {
|
||||
zPaging.value?.completeByNoMore(state.flatList, noMore)
|
||||
}
|
||||
console.log(dateList)
|
||||
state.cursor = data?.cursor
|
||||
zPaging.value?.completeByNoMore(dateList, noMore)
|
||||
} else {
|
||||
if (state.cursor === 0) {
|
||||
state.searchResultList = []
|
||||
state.flatList = []
|
||||
}
|
||||
zPaging.value?.complete([])
|
||||
}
|
||||
})
|
||||
|
||||
resp.catch(() => {
|
||||
if (state.cursor === 0) {
|
||||
state.searchResultList = []
|
||||
state.flatList = []
|
||||
}
|
||||
zPaging.value?.complete([])
|
||||
})
|
||||
}
|
||||
@ -691,59 +546,6 @@ const fileTypeAvatar = (fileType) => {
|
||||
}
|
||||
return file_type_avatar
|
||||
}
|
||||
|
||||
const previewPDF = (item) => {
|
||||
console.log(item)
|
||||
if (typeof plus !== 'undefined') {
|
||||
downloadAndOpenFile(item)
|
||||
} else {
|
||||
document.addEventListener('plusready', () => {
|
||||
downloadAndOpenFile(item)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const downloadAndOpenFile = (item) => {
|
||||
uni.showLoading({ title: '加载中...', mask: true })
|
||||
const downloadUrl = item?.extra?.path
|
||||
const options = {
|
||||
filename: '_doc/downloads/', // 保存路径
|
||||
}
|
||||
const dtask = plus.downloader.createDownload(downloadUrl, options, function (
|
||||
d,
|
||||
status,
|
||||
) {
|
||||
if (status === 200) {
|
||||
uni.hideLoading()
|
||||
const filePath = d.filename
|
||||
plus.runtime.openFile(
|
||||
filePath,
|
||||
{},
|
||||
function () {},
|
||||
function (error) {},
|
||||
)
|
||||
} else {
|
||||
uni.hideLoading()
|
||||
}
|
||||
})
|
||||
dtask.start()
|
||||
}
|
||||
|
||||
//跳转到对应的记录位置
|
||||
const toDialogueByMember = async (msgInfo) => {
|
||||
const sessionId = await getSessionId(
|
||||
dialogueParams.talk_type,
|
||||
dialogueParams.receiver_id,
|
||||
)
|
||||
uni.navigateTo({
|
||||
url:
|
||||
'/pages/dialog/index?sessionId=' +
|
||||
sessionId +
|
||||
'&keepDialogInfo=1' +
|
||||
'&msgInfo=' +
|
||||
encodeURIComponent(JSON.stringify(msgInfo)),
|
||||
})
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.search-by-date {
|
||||
@ -850,7 +652,6 @@ body::v-deep .round-3 {
|
||||
span {
|
||||
line-height: 40rpx;
|
||||
color: $theme-text;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
.attachment-sub-info {
|
||||
@ -897,26 +698,6 @@ body::v-deep .round-3 {
|
||||
width: 164rpx !important;
|
||||
height: 164rpx !important;
|
||||
}
|
||||
.video-preview {
|
||||
position: relative;
|
||||
width: 164rpx;
|
||||
height: 164rpx;
|
||||
cursor: pointer;
|
||||
.play-icon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
img {
|
||||
width: 80rpx !important;
|
||||
height: 80rpx !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -925,23 +706,4 @@ body::v-deep .round-3 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.video-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background: #000;
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.fullscreen-video {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
</style>
|
||||
|
@ -23,7 +23,7 @@ class WsSocket {
|
||||
lockReconnect: false,
|
||||
setTimeout: null, // 计时器对象
|
||||
time: 3000, // 重连间隔时间
|
||||
number: 20 // 重连次数
|
||||
number: 10000000 // 重连次数
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,21 +1,5 @@
|
||||
import Request from '@/service/request/index.js'
|
||||
import {useAuth} from "@/store/auth";
|
||||
import { createApp } from 'vue';
|
||||
import XMessage from '@/components/x-message/index.vue';
|
||||
|
||||
// 创建消息提示实例
|
||||
const messageInstance = (() => {
|
||||
const messageNode = document.createElement('div');
|
||||
document.body.appendChild(messageNode);
|
||||
const app = createApp(XMessage);
|
||||
const instance = app.mount(messageNode);
|
||||
return {
|
||||
warning: (msg) => instance.showMessage({ type: 'warning', message: msg }),
|
||||
error: (msg) => instance.showMessage({ type: 'error', message: msg }),
|
||||
success: (msg) => instance.showMessage({ type: 'success', message: msg }),
|
||||
info: (msg) => instance.showMessage({ type: 'info', message: msg })
|
||||
};
|
||||
})();
|
||||
const { token ,refreshToken,userInfo}=useAuth()
|
||||
let isRefreshing = false;
|
||||
let refreshSubscribers = [];
|
||||
@ -40,25 +24,19 @@ const request = new Request({
|
||||
},
|
||||
responseInterceptors: async (res) => {
|
||||
if(res.data.status===1){
|
||||
// message.warning(res.data.msg)
|
||||
messageInstance.warning(res.data.msg)
|
||||
message.warning(res.data.msg)
|
||||
}
|
||||
if (res.data.status === 401) {
|
||||
return
|
||||
// return getRefreshToken(res);
|
||||
return getRefreshToken(res);
|
||||
// uni.navigateTo({
|
||||
// url:'/pages/login/index'
|
||||
// })
|
||||
}
|
||||
if ([200, 201, 204].includes(res.status)) {
|
||||
if(res.data.code !== 200 && res.data.code!== 0) {
|
||||
messageInstance.error(res.data.message || res.data.msg || 'An error occurred.');
|
||||
}
|
||||
return res.config.responseType === 'blob' ? res : res;
|
||||
} else {
|
||||
/* message.error(res.data.msg || 'An error occurred.');*/
|
||||
messageInstance.error(res.data.message || res.data.msg || 'An error occurred.');
|
||||
return Promise.reject(new Error(res.data.message || res.data.msg || 'An error occurred.'));
|
||||
return Promise.reject(new Error(res.data.msg || 'An error occurred.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -81,14 +59,13 @@ async function getRefreshToken(response) {
|
||||
})
|
||||
return request.request(response.config);
|
||||
} else {
|
||||
|
||||
messageInstance.error(res.message || res.msg);
|
||||
message.error(res.message || res.msg);
|
||||
throw new Error(res.message || res.msg);
|
||||
}
|
||||
} catch (error) {
|
||||
// uni.navigateTo({
|
||||
// url:'/pages/login/index'
|
||||
// })
|
||||
uni.navigateTo({
|
||||
url:'/pages/login/index'
|
||||
})
|
||||
throw error
|
||||
} finally {
|
||||
isRefreshing = false;
|
||||
@ -96,9 +73,9 @@ async function getRefreshToken(response) {
|
||||
refreshSubscribers = [];
|
||||
}
|
||||
} else {
|
||||
// uni.navigateTo({
|
||||
// url:'/pages/login/index'
|
||||
// })
|
||||
uni.navigateTo({
|
||||
url:'/pages/login/index'
|
||||
})
|
||||
throw new Error('No refresh token available.');
|
||||
}
|
||||
} else {
|
||||
|
Before Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 654 B |
Before Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 752 B |
Before Width: | Height: | Size: 208 B |
Before Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.7 KiB |
@ -5,7 +5,7 @@ import { userInfoApi } from "@/api/user";
|
||||
import {ref} from 'vue'
|
||||
export const useAuth = createGlobalState(() => {
|
||||
const token = useStorage('token', '', uniStorage)
|
||||
// const token = ref("2046c3941ed4959f6d988d3d4a0fe40d4b52f33f3f5fc1001406064554641d9406bb13cacb92939b0ca223c17e2c2f2fe70212ef017dbae8965d5cf86bad48ce4316e605ca187bd9ffd4aa6b56865be4ad4e422701d330b52d60cfe649cd48cf3a21a2a6e9a9cabafff364ee9c311ec634b0afc09db0d3215bedce561e9d50e5a8da6092062e2ebe35f747d77d72a68ad492a4ab218c07887c9cd4867f2c2d28e4ae1fd671144cc20ef0632f9ce067289004d67f6adf41b20d6ef5cdbfb74aadc2d2736ececf07254f1a76552bde4f1161a0fca7bfe32a29685ce1e76366116b81ae2195b3713dbb04285e5ddfd36184fe671c5524d20b4fe74a555db755f8d939b0bc46fb0cb998323d54c9925729d7ca835b7925999a677faa0cbe1cbc67b5203d85317653883aec81d3e71d865b326376bea726cc66d9f7f5a160d43f671c")
|
||||
// const token = ref('79b5c732d96d2b27a48a99dfd4a5566c43aaa5796242e854ebe3ffc198d6876b9628e7b764d9af65ab5dbb2d517ced88170491b74b048c0ba827c0d3741462cb89dc59ed46653a449af837a8262941caaef1334d640773710f8cd96473bacfb190cba595a5d6a9c87d70f0999a3ebb41147213b31b4bdccffca66a56acf3baab5af0154f0dce360079f37709f78e13711036899344bddb0fb4cf0f2890287cb62c3fcbe33368caa5e213624577be8b8420ab75b1f50775ee16142a4321c5d56995f37354a66a969da98d95ba6e65d142ed097e04b411c1ebad2f62866d0ec7e1838420530a9941dbbcd00490199f8b897937e719454eda6de1352a14497a54063c2ae13c2b1418f2689268102faffee874777ce1312eb7d9399eaa8cf58674aa86c9b85ad9300293c0a3369ed429536bbea4fcb092b78466ea53a44a2b2b1c1a')
|
||||
const refreshToken = useStorage('refreshToken', '', uniStorage)
|
||||
const userInfo = useStorage('userInfo', {}, uniStorage)
|
||||
const leaderList = useStorage('leaderList', [], uniStorage)
|
||||
|
@ -10,14 +10,10 @@ import {
|
||||
departmentV2AllPosition,
|
||||
groupCreateDept,
|
||||
departmentV2TreeAll,
|
||||
departmentV2TreeAll2,
|
||||
userHasPermission,
|
||||
userV2List,
|
||||
userV2List2,
|
||||
v2TreePositionByDepartment,
|
||||
} from '@/api/deps/index.js'
|
||||
import { useAuth } from '@/store/auth'
|
||||
const { userInfo } = useAuth()
|
||||
|
||||
export const useGroupTypeStore = createGlobalState(() => {
|
||||
const groupName = ref('')
|
||||
const groupActiveIndex = ref(-1) // 当前激活的分组索引
|
||||
@ -38,40 +34,6 @@ export const useGroupTypeStore = createGlobalState(() => {
|
||||
depTreeMyList.value = res.data.nodes
|
||||
}
|
||||
}
|
||||
// userInfo?.value?.ID
|
||||
const getDepsTreeMy2 = async (chooseMode) => {
|
||||
let params = { nowUserId: 0 }
|
||||
if (chooseMode === 1) {
|
||||
const isHasRes = await userHasPermission({
|
||||
erpUserId: userInfo?.value?.ID,
|
||||
ruleUrl: [
|
||||
'auth_chat_app_create_all_dept',
|
||||
'auth_chat_app_create_limit_dept',
|
||||
],
|
||||
})
|
||||
if (isHasRes.code === 200) {
|
||||
if (isHasRes.data.auth_chat_app_create_all_dept) {
|
||||
params = {
|
||||
nowUserId: 0,
|
||||
}
|
||||
} else {
|
||||
if (isHasRes.data.auth_chat_app_create_limit_dept) {
|
||||
params = {
|
||||
nowUserId: userInfo?.value?.ID,
|
||||
}
|
||||
} else {
|
||||
params = {
|
||||
nowUserId: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const res = await departmentV2TreeAll2(params)
|
||||
if (res.status === 0) {
|
||||
depTreeMyList.value = res.data.nodes
|
||||
}
|
||||
}
|
||||
|
||||
//获取指定部门下的所有岗位
|
||||
const getPositionByDepartment = async (params) => {
|
||||
@ -89,7 +51,7 @@ export const useGroupTypeStore = createGlobalState(() => {
|
||||
}
|
||||
|
||||
const getDepMembers = async (param) => {
|
||||
const res = await userV2List2(param)
|
||||
const res = await userV2List(param)
|
||||
return res
|
||||
}
|
||||
|
||||
@ -162,7 +124,6 @@ export const useGroupTypeStore = createGlobalState(() => {
|
||||
postTreeList,
|
||||
departmentAllPositions,
|
||||
getDepsTreeMy,
|
||||
getDepsTreeMy2,
|
||||
getPositionByDepartment,
|
||||
getPositionsTree,
|
||||
crumbs,
|
||||
|
@ -15,10 +15,6 @@ import { useAuth } from '../auth/index'
|
||||
// let keyboardTimeout = null
|
||||
|
||||
export const useDialogueStore = defineStore('dialogue', {
|
||||
// 添加持久化配置
|
||||
persist: {
|
||||
paths: ['talk.talk_type', 'talk.receiver_id']
|
||||
},
|
||||
state: () => {
|
||||
return {
|
||||
// 对话索引(聊天对话的唯一索引)
|
||||
@ -52,15 +48,6 @@ export const useDialogueStore = defineStore('dialogue', {
|
||||
// 是否显示会话列表
|
||||
isShowSessionList: true,
|
||||
|
||||
//是否已被解散
|
||||
isDismiss: false,
|
||||
|
||||
//是否退群/移出群
|
||||
isQuit: false,
|
||||
|
||||
//未读消息数量
|
||||
unreadNum:0,
|
||||
|
||||
// 群成员列表
|
||||
members: [],
|
||||
|
||||
@ -97,21 +84,6 @@ export const useDialogueStore = defineStore('dialogue', {
|
||||
this.online = status
|
||||
},
|
||||
|
||||
// 更新未读消息数量-清空未读
|
||||
clearUnreadNum() {
|
||||
this.unreadNum = 0
|
||||
},
|
||||
|
||||
// 更新群解散状态
|
||||
updateDismiss() {
|
||||
this.isDismiss = true
|
||||
},
|
||||
|
||||
// 更新群成员退出状态
|
||||
updateQuit() {
|
||||
this.isQuit = true
|
||||
},
|
||||
|
||||
// 更新对话信息
|
||||
setDialogue(data = {}) {
|
||||
this.online = data.is_online == 1
|
||||
@ -126,13 +98,8 @@ export const useDialogueStore = defineStore('dialogue', {
|
||||
this.unreadBubble = 0
|
||||
this.isShowEditor = data?.is_robot === 0
|
||||
|
||||
this.isDismiss = data?.is_dismiss === 1 ? true : false
|
||||
this.isQuit = data?.is_quit === 1 ? true : false
|
||||
|
||||
this.unreadNum = data?.unread_num || 0
|
||||
|
||||
this.members = []
|
||||
if (data.talk_type == 2 && !this.isDismiss && !this.isQuit) {
|
||||
if (data.talk_type == 2) {
|
||||
this.updateGroupMembers()
|
||||
}
|
||||
},
|
||||
@ -242,6 +209,8 @@ export const useDialogueStore = defineStore('dialogue', {
|
||||
if (res.code == 200) {
|
||||
this.batchDelDialogueRecord(msgIds)
|
||||
batchDelDialogueRecord(msgIds)
|
||||
} else {
|
||||
message.warning(res.message)
|
||||
}
|
||||
})
|
||||
},
|
||||
@ -265,6 +234,8 @@ export const useDialogueStore = defineStore('dialogue', {
|
||||
ServeRevokeRecords({ msg_id }).then((res) => {
|
||||
if (res.code == 200) {
|
||||
this.updateDialogueRecord({ msg_id, is_revoke: 1 })
|
||||
} else {
|
||||
message.warning(res.message)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
@ -5,111 +5,9 @@ import lodash from 'lodash'
|
||||
import { ref } from 'vue'
|
||||
import { createGlobalState, useStorage } from '@vueuse/core'
|
||||
import { uniStorage } from '@/utils/uniStorage.js'
|
||||
import { handleFindWebview } from '@/utils/common'
|
||||
|
||||
export const useDialogueListStore = createGlobalState(() => {
|
||||
const testDatabase = async () => {
|
||||
// 初始化数据库
|
||||
let chatDatabase = {
|
||||
eventType: 'openDatabase',
|
||||
eventParams: {
|
||||
name: 'chat',
|
||||
path: '_doc/chat.db',
|
||||
},
|
||||
}
|
||||
let chatDBexecuteSql = {
|
||||
eventType: 'executeSql',
|
||||
eventParams: {
|
||||
name: 'chat',
|
||||
sql: `CREATE TABLE IF NOT EXISTS talk_records (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
msg_id TEXT NOT NULL,
|
||||
sequence INTEGER NOT NULL,
|
||||
talk_type INTEGER NOT NULL DEFAULT 1,
|
||||
msg_type INTEGER NOT NULL DEFAULT 1,
|
||||
user_id INTEGER NOT NULL DEFAULT 0,
|
||||
receiver_id INTEGER NOT NULL DEFAULT 0,
|
||||
is_revoke INTEGER NOT NULL DEFAULT 0,
|
||||
is_mark INTEGER NOT NULL DEFAULT 0,
|
||||
quote_id TEXT NOT NULL,
|
||||
extra TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL,
|
||||
biz_date TEXT
|
||||
)`,
|
||||
},
|
||||
}
|
||||
|
||||
const content = {
|
||||
content: '我试试传送文件和图片是不是一个接口',
|
||||
name: '测试excel1.xlsx',
|
||||
path:
|
||||
'https://cdn-test.szjixun.cn/fonchain-chat/chat/file/multipart/20250307/727a2371-ffc4-46da-b953-a7d449ff82ff-测试excel1.xlsx',
|
||||
size: 9909,
|
||||
drive: 3,
|
||||
}
|
||||
const extra = JSON.stringify(content)
|
||||
|
||||
let chatDBexecuteSql2 = {
|
||||
eventType: 'executeSql',
|
||||
eventParams: {
|
||||
name: 'chat',
|
||||
sql:
|
||||
'INSERT INTO talk_records (msg_id, sequence, talk_type, msg_type, user_id, receiver_id, is_revoke, is_mark, quote_id, extra, created_at, updated_at, biz_date) VALUES ("' +
|
||||
'77b715fb30f54f739a255a915ef72445' +
|
||||
'", 166, 2, 1, 1774, 888890, 0, 0, "' +
|
||||
'' +
|
||||
'", "' +
|
||||
extra +
|
||||
'", "' +
|
||||
'2025-03-06T15:57:07.000Z' +
|
||||
'", "' +
|
||||
'2025-03-06T15:57:07.000Z' +
|
||||
'", "' +
|
||||
'20250306' +
|
||||
'")',
|
||||
},
|
||||
}
|
||||
let chatDBSelectSql = {
|
||||
eventType: 'selectSql',
|
||||
eventParams: {
|
||||
name: 'chat',
|
||||
sql: `SELECT * FROM talk_records ORDER BY sequence DESC LIMIT 20`,
|
||||
},
|
||||
}
|
||||
let chatDBIsOpenDatabase = {
|
||||
eventType: 'isOpenDatabase',
|
||||
eventParams: {
|
||||
name: 'chat',
|
||||
path: '_doc/chat.db',
|
||||
},
|
||||
}
|
||||
// handleFindWebview(
|
||||
// `operateSQLite('${encodeURIComponent(JSON.stringify(chatDatabase))}')`,
|
||||
// )
|
||||
// handleFindWebview(
|
||||
// `operateSQLite('${encodeURIComponent(
|
||||
// JSON.stringify(chatDBexecuteSql),
|
||||
// )}')`,
|
||||
// )
|
||||
// handleFindWebview(
|
||||
// `operateSQLite('${encodeURIComponent(
|
||||
// JSON.stringify(chatDBexecuteSql2),
|
||||
// )}')`,
|
||||
// )
|
||||
// handleFindWebview(
|
||||
// `operateSQLite('${encodeURIComponent(JSON.stringify(chatDBSelectSql))}')`,
|
||||
// )
|
||||
// handleFindWebview(
|
||||
// `operateSQLite('${encodeURIComponent(
|
||||
// JSON.stringify(chatDBIsOpenDatabase),
|
||||
// )}')`,
|
||||
// )
|
||||
}
|
||||
// testDatabase()
|
||||
|
||||
const dialogueList = useStorage('dialogueList', [], uniStorage)
|
||||
// const dialogueList = ref([])
|
||||
const zpagingRef = ref()
|
||||
const virtualList = ref([])
|
||||
|
||||
@ -120,49 +18,18 @@ export const useDialogueListStore = createGlobalState(() => {
|
||||
const addDialogueRecord = (newRecords, type = 'add') => {
|
||||
console.log(newRecords)
|
||||
|
||||
const dialogue = useDialogueStore()
|
||||
const dialogue = lodash.cloneDeep(useDialogueStore())
|
||||
if (!dialogue || typeof dialogue !== 'object') return
|
||||
// 检查是否已存在相同 index_name 的对话
|
||||
const existingIndex = dialogueList.value.findIndex(
|
||||
(item) => item.index_name === dialogue.index_name,
|
||||
)
|
||||
if (existingIndex === -1) {
|
||||
// 如果不存在,创建新对话,只保存需要的属性
|
||||
const newDialogue = {
|
||||
index_name: dialogue.index_name,
|
||||
talk: {
|
||||
username: dialogue.talk.username,
|
||||
talk_type: dialogue.talk.talk_type,
|
||||
receiver_id: dialogue.talk.receiver_id,
|
||||
},
|
||||
online: dialogue.online,
|
||||
records: dialogue.records || [],
|
||||
unreadBubble: dialogue.unreadBubble,
|
||||
isOpenMultiSelect: dialogue.isOpenMultiSelect,
|
||||
isShowEditor: dialogue.isShowEditor,
|
||||
isShowSessionList: dialogue.isShowSessionList,
|
||||
isDismiss: dialogue.isDismiss,
|
||||
isQuit: dialogue.isQuit,
|
||||
unreadNum: dialogue.unreadNum,
|
||||
members: dialogue.members.map((member) => ({
|
||||
id: member.id,
|
||||
nickname: member.nickname,
|
||||
avatar: member.avatar,
|
||||
gender: member.gender,
|
||||
leader: member.leader,
|
||||
remark: member.remark,
|
||||
online: member.online,
|
||||
value: member.value,
|
||||
key: member.key,
|
||||
erp_user_id: member.erp_user_id,
|
||||
is_mute: member.is_mute,
|
||||
is_mine: member.is_mine,
|
||||
})),
|
||||
forwardType: dialogue.forwardType,
|
||||
}
|
||||
dialogueList.value.push(newDialogue)
|
||||
// 如果不存在,直接添加
|
||||
dialogueList.value.push(dialogue)
|
||||
} else {
|
||||
// 如果对话存在,处理 records 数组
|
||||
const { records = [] } = dialogue
|
||||
newRecords.forEach((newRecord) => {
|
||||
const recordIndex = dialogueList.value[existingIndex].records.findIndex(
|
||||
(record) => record.msg_id === newRecord.msg_id,
|
||||
@ -177,11 +44,18 @@ export const useDialogueListStore = createGlobalState(() => {
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 更新除 records 和 index_name 外的其他属性
|
||||
const { index_name, records: _, ...updateProps } = dialogue
|
||||
dialogueList.value[existingIndex] = {
|
||||
...dialogueList.value[existingIndex],
|
||||
...updateProps,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const updateDialogueRecord = (record) => {
|
||||
const dialogue = useDialogueStore()
|
||||
const dialogue = lodash.cloneDeep(useDialogueStore())
|
||||
const item = getDialogueList(dialogue.index_name)
|
||||
const recordIndex = item.records.findIndex(
|
||||
(item) => item.msg_id === record.msg_id,
|
||||
@ -204,7 +78,7 @@ export const useDialogueListStore = createGlobalState(() => {
|
||||
}
|
||||
|
||||
const deleteDialogueRecord = (record) => {
|
||||
const dialogue = useDialogueStore()
|
||||
const dialogue = lodash.cloneDeep(useDialogueStore())
|
||||
const item = getDialogueList(dialogue.index_name)
|
||||
const recordIndex = item.records.findIndex(
|
||||
(item) => item.msg_id === record.msg_id,
|
||||
@ -230,29 +104,14 @@ export const useDialogueListStore = createGlobalState(() => {
|
||||
}
|
||||
|
||||
const addChatRecord = (indexName, item) => {
|
||||
const dialogue = useDialogueStore()
|
||||
const dialogue = lodash.cloneDeep(useDialogueStore())
|
||||
if (dialogue?.index_name === indexName) {
|
||||
if (item?.file_num) {
|
||||
const index = virtualList.value.findIndex(
|
||||
(v) => v?.file_num === item?.file_num,
|
||||
)
|
||||
if (index > -1) {
|
||||
// 保持响应性的同时替换整个对象
|
||||
virtualList.value.splice(index, 1, {
|
||||
...virtualList.value[index], // 保留原有不需要修改的字段
|
||||
...item, // 覆盖需要更新的字段
|
||||
})
|
||||
} else {
|
||||
zpagingRef.value?.addChatRecordData(item, false, false)
|
||||
}
|
||||
} else {
|
||||
zpagingRef.value?.addChatRecordData(item, false, false)
|
||||
}
|
||||
zpagingRef.value?.addChatRecordData(item, false, false)
|
||||
}
|
||||
}
|
||||
|
||||
const batchDelDialogueRecord = (msgIds) => {
|
||||
const dialogue = useDialogueStore()
|
||||
const dialogue = lodash.cloneDeep(useDialogueStore())
|
||||
const item = getDialogueList(dialogue.index_name)
|
||||
item.records = item.records.filter((item) => !msgIds.includes(item.msg_id))
|
||||
}
|
||||
@ -270,7 +129,7 @@ export const useDialogueListStore = createGlobalState(() => {
|
||||
|
||||
//清空聊天记录时,同时清空本地保存的聊天记录
|
||||
const clearDialogueRecord = () => {
|
||||
const dialogue = useDialogueStore()
|
||||
const dialogue = lodash.cloneDeep(useDialogueStore())
|
||||
const item = getDialogueList(dialogue.index_name)
|
||||
item.records = []
|
||||
virtualList.value = []
|
||||
|
@ -41,6 +41,8 @@ export const useEditorStore = defineStore('editor', {
|
||||
}).then((res) => {
|
||||
if (res.code == 200) {
|
||||
this.loadUserEmoticon()
|
||||
} else {
|
||||
message.warning(res.message)
|
||||
}
|
||||
})
|
||||
},
|
||||
@ -53,6 +55,8 @@ export const useEditorStore = defineStore('editor', {
|
||||
ServeUploadEmoticon(data).then((res) => {
|
||||
if (res.code == 200) {
|
||||
this.emoticon.items[1].children.unshift(res.data)
|
||||
} else {
|
||||
message.warning(res.message)
|
||||
}
|
||||
})
|
||||
},
|
||||
@ -65,6 +69,8 @@ export const useEditorStore = defineStore('editor', {
|
||||
if (res.code == 200) {
|
||||
this.emoticon.items[1].children.splice(resoure.index, 1)
|
||||
message.success('删除成功')
|
||||
} else {
|
||||
message.warning(res.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ import { defineStore } from 'pinia'
|
||||
import { ServeGetTalkList, ServeCreateTalkList } from '@/api/chat/index'
|
||||
import { formatTalkItem, ttime, KEY_INDEX_NAME } from '@/utils/talk'
|
||||
import { useEditorDraftStore } from './editor-draft'
|
||||
import { handleFindWebview } from '@/utils/common'
|
||||
// import { ISession } from '@/types/chat'
|
||||
|
||||
export const useTalkStore = defineStore('talk', {
|
||||
@ -103,8 +102,6 @@ export const useTalkStore = defineStore('talk', {
|
||||
// 返回 Promise 对象,使调用方可以使用 then/catch
|
||||
return resp.then(({ code, data }) => {
|
||||
if (code == 200) {
|
||||
//向OA的webview通信,改变未读消息数量
|
||||
handleFindWebview(`doUpdateUnreadNum('${data.unread_num}')`)
|
||||
this.items = data.items.map((item) => {
|
||||
const value = formatTalkItem(item)
|
||||
|
||||
@ -147,7 +144,6 @@ export const useTalkStore = defineStore('talk', {
|
||||
|
||||
ServeCreateTalkList({
|
||||
talk_type,
|
||||
receiver_id,
|
||||
erp_user_id,
|
||||
}).then(({ code, data, message }) => {
|
||||
if (code == 200) {
|
||||
|