Compare commits

..

No commits in common. "main" and "zhangyuanshan-20250310" have entirely different histories.

115 changed files with 6907 additions and 15785 deletions

2
auto-imports.d.ts vendored
View File

@ -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')
}

6
components.d.ts vendored
View File

@ -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,17 +36,13 @@ 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']
SysTextMessage: typeof import('./src/components/talk/message/system/SysTextMessage.vue')['default']

6
env/.env.dev vendored
View File

@ -5,8 +5,4 @@ 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'
# EPRAPI baseUrl
VITE_EPR_BASEURL = 'http://114.218.158.24:9020'
VITE_BASEURL = 'http://warehouse.szjixun.cn/oa_backend'

19
env/.env.prod vendored
View File

@ -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
View File

@ -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'

View File

@ -2,15 +2,14 @@
"name": "unihelper",
"version": "0.0.0",
"private": true,
"type": "module",
"packageManager": "pnpm@8.14.1",
"license": "MIT",
"scripts": {
"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",
@ -39,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"
@ -65,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",

File diff suppressed because it is too large Load Diff

View File

@ -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 {

View File

@ -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,
})
}

View File

@ -1,6 +1,5 @@
import request from '@/service/index.js'
import qs from 'qs'
import { useTalkStore, useDialogueStore } from '@/store'
// 获取聊天列表服务接口
export const ServeGetTalkList = (data) => {
@ -39,31 +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
) {
if (typeof plus !== 'undefined') {
let OAWebView = plus.webview.all()
OAWebView.forEach((webview) => {
if (webview.id === 'webviewId1') {
webview.evalJS(`updateUnreadMsgNumReduce('${unReadNum}')`)
}
})
} else {
document.addEventListener('plusready', () => {
let OAWebView = plus.webview.all()
OAWebView.forEach((webview) => {
if (webview.id === 'webviewId1') {
webview.evalJS(`updateUnreadMsgNumReduce('${unReadNum}')`)
}
})
})
}
}
export const ServeClearTalkUnreadNum = (data) => {
return request({
url: '/api/v1/talk/unread/clear',
method: 'POST',
@ -83,7 +58,7 @@ 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,
})
@ -194,58 +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,
})
}

View File

@ -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({

View File

@ -4,7 +4,7 @@ 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,
})
@ -13,7 +13,7 @@ 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,
})
@ -45,12 +45,3 @@ export const ServeTalkDate = (data) => {
data,
})
}
//获取会话Id
export const ServeGetSessionId = (data) => {
return request({
url: '/api/v1/talk/session/getId',
method: 'POST',
data,
})
}

View File

@ -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,
})
}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -90,6 +90,8 @@ const onSubmit = () => {
ServeUploadAvatar(form).then((res) => {
if (res.code == 200) {
emit('success', res.data.avatar)
} else {
message.warning(res.message)
}
})
})

View File

@ -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')
}

View File

@ -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: '', //

View File

@ -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>

View File

@ -25,15 +25,7 @@
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.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>
</div>
<div
@click="() => itemClick('multipleChoose')"
@ -44,7 +36,7 @@
:height="40"
:src="multipleChoices"
></tm-image>
<div class="mt-1">多选</div>
<div>多选</div>
</div>
<div
v-if="props.isShowCite"
@ -52,7 +44,7 @@
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"
@ -60,14 +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
@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>
@ -86,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,
@ -95,18 +87,18 @@ import {
computed,
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'
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";
// pinia
const systemInfo = uni.getSystemInfoSync()
const bubbleRef = ref(null)
const systemInfo = uni.getSystemInfoSync();
const bubbleRef = ref(null);
const props = defineProps({
isShowCopy: {
@ -121,114 +113,109 @@ const props = defineProps({
type: Boolean,
default: true,
},
isShowConvertText: {
//
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) {
@ -237,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>

View File

@ -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>

View File

@ -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;
}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -24,7 +24,7 @@ const { showUserInfoModal } = useInject()
<em v-show="index < extra.members.length - 1"></em>
</template>
<span>出群聊</span>
<span>出群聊</span>
</div>
</div>
</template>

View File

@ -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>

View File

@ -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

View File

@ -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,9 +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 ChatMsgTypeMapping = {
[ChatMsgTypeText]: '[文本消息]',
@ -49,18 +46,14 @@ export const ChatMsgTypeMapping = {
[ChatMsgSysGroupCreate]: '[创建群消息]',
[ChatMsgSysGroupMemberJoin]: '[加入群消息]',
[ChatMsgSysGroupMemberQuit]: '[退出群消息]',
[ChatMsgSysGroupMemberKicked]: '[出群消息]',
[ChatMsgSysGroupMemberKicked]: '[出群消息]',
[ChatMsgSysGroupMessageRevoke]: '[撤回消息]',
[ChatMsgSysGroupDismissed]: '[群解散消息]',
[ChatMsgSysGroupMuted]: '[群禁言消息]',
[ChatMsgSysGroupCancelMuted]: '[群解除禁言消息]',
[ChatMsgSysGroupMemberMuted]: '[群成员禁言消息]',
[ChatMsgSysGroupMemberCancelMuted]: '[群成员解除禁言消息]',
[ChatMsgSysGroupNotice]: '[群公告]',
[ChatMsgSysGroupTransfer]: '[转让群主]',
[ChatMsgSysGroupAdmin]: '[设置管理员]',
[ChatMsgSysGroupMemberRemoved]: '[移出群成员消息]',
[ChatMsgSysGroupInfoChange]: '[群信息更新]'
[ChatMsgSysGroupNotice]: '[群公告]'
}
// 消息类型 - 消息组件 映射关系
@ -85,15 +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'
[ChatMsgSysGroupTransfer]: 'sys-group-transfer-message'
}
// 可转发的消息类型

View File

@ -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
})
}
}

View File

@ -5,16 +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 { ServeClearTalkUnreadNum, ServeCreateTalkList } from '@/api/chat/index.js'
import { useTalkStore, useDialogueStore,useDialogueListStore } from '@/store'
/**
* 好友状态事件
@ -40,11 +32,6 @@ class Talk extends Base {
*/
talk_type = 0
/**
* 文件上传唯一随机值
*/
fileNum = ''
/**
* 初始化构造方法
*
@ -56,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()
@ -106,10 +89,11 @@ class Talk extends Base {
play() {
// 客户端有消息提示
// if (isElectronMode()) return
// useSettingsStore().isPromptTone && palyMusic()
}
async handle() {
handle() {
// 不是自己发送的消息则需要播放提示音
if (!this.isCurrSender()) {
this.play()
@ -117,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()
}
// 判断当前是否正在和好友对话
@ -139,37 +109,6 @@ 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() {
if (typeof plus !== 'undefined') {
let OAWebView = plus.webview.all()
OAWebView.forEach((webview) => {
if (webview.id === 'webviewId1') {
webview.evalJS(`updateUnreadMsgNumAdd()`)
}
})
} else {
document.addEventListener('plusready', () => {
let OAWebView = plus.webview.all()
OAWebView.forEach((webview) => {
if (webview.id === 'webviewId1') {
webview.evalJS(`updateUnreadMsgNumAdd()`)
}
})
})
}
}
@ -184,6 +123,7 @@ class Talk extends Base {
// lang: 'zh-CN',
// body: '您有新的消息请注意查收'
// })
// notification.onclick = () => {
// notification.close()
// }
@ -211,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()
}
})
}
@ -230,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)
}
// 获取聊天面板元素节点
@ -307,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(() => {
@ -321,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
})
}
}
@ -340,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()
}
}
}

View File

@ -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 }
}

View File

@ -10,18 +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"
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() {
@ -33,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) => {
@ -47,92 +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('===准备创建本地通知栏消息')
let OAWebView = plus.webview.all()
OAWebView.forEach((webview, index) => {
if (webview.id === 'webviewId1') {
webview.evalJS(`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 = () => {
let OAWebView = plus.webview.all()
OAWebView.forEach((webview, index) => {
if (webview.id === 'webviewId1') {
webview.evalJS(`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)
}
}
window.message = ['success', 'error', 'warning'].reduce((acc, type) => {
acc[type] = (message) => {
if (typeof message === 'string') {

View File

@ -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": {

View File

@ -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>

View File

@ -1,950 +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"
@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: 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"
@click="toUserDetail(item)"
>
<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">
<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 } 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()
let OAWebView = plus.webview.all()
OAWebView.forEach((webview) => {
if (webview.id === 'webviewId1') {
webview.evalJS(`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.error(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 (newVal === '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;
.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>

View File

@ -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;
// leaderleader
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)

View File

@ -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"

View File

@ -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, //12
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, //12
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>

View File

@ -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"

View File

@ -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
})
)
}
})
},

View File

@ -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;

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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, // 123
isManager: 'false', //
editMode: 1, // 123
})
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
}
})

View File

@ -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, //12
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, //12
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;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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 {

View File

@ -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)
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;

View File

@ -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 {

View File

@ -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)
})

View File

@ -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>

View File

@ -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>

View File

@ -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 {

View File

@ -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'
@ -62,167 +43,82 @@ 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 {
// let OAWebView = plus.webview.all()
// OAWebView.forEach((webview, index) => {
// if (webview.id === 'webviewId1') {
// webview.evalJS(`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,
@ -230,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('您的系统暂不支持发送原图哦')
}
// base64file
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 {

View File

@ -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: '', //erpid
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,71 +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 = () => {
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 {
@ -324,7 +220,6 @@ const doAddFriend = () => {
flex-direction: row;
align-items: center;
justify-content: center;
flex-shrink: 0;
img {
width: 100%;
height: 100%;
@ -395,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>

File diff suppressed because it is too large Load Diff

View File

@ -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 {

View File

@ -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>

View File

@ -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,23 +198,10 @@ 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',
url: '/pages/chooseByDeps/index?chooseMode=3',
})
}
@ -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,
})
}
})
}

View File

@ -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;
}
}
}

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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, //12
receiver_id: dialogueParams.receiver_id, //id
talk_type: 2, //12
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', //downup
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) {
// cursor0searchResultList
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>

View File

@ -23,7 +23,7 @@ class WsSocket {
lockReconnect: false,
setTimeout: null, // 计时器对象
time: 3000, // 重连间隔时间
number: 20 // 重连次数
number: 10000000 // 重连次数
}
}

View File

@ -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 {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 654 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 752 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -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)

View File

@ -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,

View File

@ -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)
}
})
},

View File

@ -7,105 +7,7 @@ import { createGlobalState, useStorage } from '@vueuse/core'
import { uniStorage } from '@/utils/uniStorage.js'
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',
},
}
document.addEventListener('plusready', () => {
let OAWebView = plus.webview.all()
OAWebView.forEach((webview, index) => {
if (webview.id === 'webviewId1') {
webview.evalJS(
`operateSQLite('${encodeURIComponent(
JSON.stringify(chatDatabase),
)}')`,
)
webview.evalJS(
`operateSQLite('${encodeURIComponent(
JSON.stringify(chatDBexecuteSql),
)}')`,
)
webview.evalJS(
`operateSQLite('${encodeURIComponent(
JSON.stringify(chatDBexecuteSql2),
)}')`,
)
webview.evalJS(
`operateSQLite('${encodeURIComponent(
JSON.stringify(chatDBSelectSql),
)}')`,
)
webview.evalJS(
`operateSQLite('${encodeURIComponent(
JSON.stringify(chatDBIsOpenDatabase),
)}')`,
)
}
})
})
}
// testDatabase()
const dialogueList = useStorage('dialogueList', [], uniStorage)
// const dialogueList = ref([])
const zpagingRef = ref()
const virtualList = ref([])
@ -116,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,
@ -173,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,
@ -200,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,
@ -226,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))
}
@ -266,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 = []

View File

@ -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,7 +69,9 @@ 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)
}
})
}
}

View File

@ -102,24 +102,6 @@ export const useTalkStore = defineStore('talk', {
// 返回 Promise 对象,使调用方可以使用 then/catch
return resp.then(({ code, data }) => {
if (code == 200) {
//向OA的webview通信改变未读消息数量
if (typeof plus !== 'undefined') {
let OAWebView = plus.webview.all()
OAWebView.forEach((webview) => {
if (webview.id === 'webviewId1') {
webview.evalJS(`doUpdateUnreadNum('${data.unread_num}')`)
}
})
} else {
document.addEventListener('plusready', () => {
let OAWebView = plus.webview.all()
OAWebView.forEach((webview) => {
if (webview.id === 'webviewId1') {
webview.evalJS(`doUpdateUnreadNum('${data.unread_num}')`)
}
})
})
}
this.items = data.items.map((item) => {
const value = formatTalkItem(item)
@ -162,7 +144,6 @@ export const useTalkStore = defineStore('talk', {
ServeCreateTalkList({
talk_type,
receiver_id,
erp_user_id,
}).then(({ code, data, message }) => {
if (code == 200) {

View File

@ -33,9 +33,7 @@ export const useUploadsStore = defineStore('uploads', {
state: () => {
return {
isShow: false,
items: [],
isUploading: false,//当前是否正在上传
uploadingNum: 0//当前正在上传数量
items: []
}
},
getters: {
@ -77,10 +75,8 @@ export const useUploadsStore = defineStore('uploads', {
this.triggerUpload(upload_id,msgId)
this.isShow = true
} else {
this.updateUploadStatus(false)
message.error(res.message)
}
}).catch(()=> {
this.updateUploadStatus(false)
})
},
@ -97,19 +93,7 @@ export const useUploadsStore = defineStore('uploads', {
item.status = 1
// 开始上传时就更新进度
const currentPercentage = (item.uploadIndex / item.files.length) * 100
item.percentage = currentPercentage.toFixed(1)
updateUploadProgress(msgId, currentPercentage)
ServeFileSubareaUpload(form, (progressEvent) => {
// 计算当前分片的进度
const currentChunkProgress = (progressEvent.loaded / progressEvent.total) * 100
// 计算总体进度:已完成分片 + 当前分片的进度
const totalProgress = ((item.uploadIndex + currentChunkProgress / 100) / item.files.length) * 100
item.percentage = totalProgress.toFixed(1)
updateUploadProgress(msgId, totalProgress)
})
ServeFileSubareaUpload(form)
.then((res) => {
if (res.code == 200) {
item.uploadIndex++
@ -120,81 +104,32 @@ export const useUploadsStore = defineStore('uploads', {
console.log(msgId,'msgId');
updateUploadProgress(msgId,100)
this.sendUploadMessage(item, msgId)
// 更新虚拟列表中的状态
const { virtualList } = useDialogueListStore()
const index = virtualList.value.findIndex(item => item.file_num === msgId)
if (index !== -1) {
virtualList.value[index].uploadStatus = 2
virtualList.value[index].uploadCurrent = 100
}
this.sendUploadMessage(item)
} else {
// 继续上传下一个分片
this.triggerUpload(uploadId, msgId)
const percentage = (item.uploadIndex / item.files.length) * 100
item.percentage = percentage.toFixed(1)
console.log(msgId,'msgId');
console.log(percentage,'percentage');
updateUploadProgress(msgId,percentage)
this.triggerUpload(uploadId,msgId)
}
} else {
this.updateUploadStatus(false)
item.status = 3
// 更新虚拟列表中的状态为失败
const { virtualList } = useDialogueListStore()
const index = virtualList.value.findIndex(item => item.file_num === msgId)
if (index !== -1) {
virtualList.value[index].uploadStatus = 3
}
}
})
.catch(() => {
this.updateUploadStatus(false)
item.status = 3
// 更新虚拟列表中的状态为失败
const { virtualList } = useDialogueListStore()
const index = virtualList.value.findIndex(item => item.file_num === msgId)
if (index !== -1) {
virtualList.value[index].uploadStatus = 3
}
})
},
// 发送上传消息
sendUploadMessage(item: any, file_num: String) {
sendUploadMessage(item: any) {
ServeSendTalkFile({
upload_id: item.upload_id,
receiver_id: item.receiver_id,
talk_type: item.talk_type,
file_num: file_num
}).then((res) => {
console.log(res, 'res')
if(res.code == 200){
this.updateUploadStatus(false)
}else{
this.updateUploadStatus(false)
}
}).catch(() => {
this.updateUploadStatus(false)
talk_type: item.talk_type
})
},
//更新资源上传状态
updateUploadStatus(isUploading: boolean){
if(isUploading){
this.uploadingNum++
this.isUploading = true
}else{
this.uploadingNum--
if(this.uploadingNum < 0){
this.uploadingNum = 0
}
if(this.uploadingNum === 0){
this.isUploading = false
}
}
},
// 清除上传
clearUpload(){
this.isUploading = false
this.uploadingNum = 0
}
}
})

File diff suppressed because one or more lines are too long

View File

@ -1,19 +0,0 @@
## 1.0.2503312025-03-31
增加RecordApp.UniNativeUtsPlugin_OnJsCall接口App端搭配原生插件使用时可绑定接收配套原生录音插件事件原生插件新增PcmPlayer播放器支持流式播放、完整播放App端边录音边播放更流畅
## 1.0.2501112025-01-11
修复vue3 Fragments(multi-root 多个根节点)的兼容性问题修复uniapp Android自带的XXPermissions库在后台无法请求权限的问题仅限搭配原生录音插件可用
## 1.0.2410202024-10-20
适配HBuilder4.28 vue3 setup编译环境下$root.$scope无法读取的bugHBuilder4.29已修复此编译bug但似乎还是有不能使用的问题。如果setup内不能使用可尝试新建个vue组件然后使用选项式api来调用录音功能页面的setup内使用此vue组件
## 1.0.2409102024-09-10
- 新增RecordApp.UniMainCallBack_Register接口允许App renderjs层多次回调数据给逻辑层
- iOS App请求权限时会预先检查NSMicrophoneUsageDescription是否声明避免无声明时调用录音会崩溃
- 新增appNativePlugin_sampleRate原生插件录音选项
- Android App已提供后台录音保活功能启用后App在后台或锁屏后可继续正常录音
## 1.0.2406252024-06-25
调整UniWebViewCallAsync调用失败时返回更详细信息。android_audioSource默认值由1改成0新增ios_categoryOptions原生插件录音选项
## 1.0.2404092024-04-09
增加功能调用完善demo项目
## 1.0.2312082023-12-08
完善文档增加asr语音识别示例
## 1.0.2312012023-12-04
第一次发布

View File

@ -1,6 +0,0 @@
<template>
<view>
<view style="font-weight: bold;">Recorder-UniCore Vue Component</view>
<view style="font-size:14px; color:#f60">无需手动显示本UI组件只需在script中正常引入 RecordApp + app-uni-support.js 即可实现 H5iOS Android App微信小程序 多端录音</view>
</view>
</template>

View File

@ -1,495 +0,0 @@
/*
Recorder ../app-support-sample/demo_UniApp/uni_modules/Recorder-UniCore/i18n/Template.js
https://github.com/xiangyuecn/Recorder
Usage: Recorder.i18n.lang="Your-Language-Name" or "your-language"
Desc: This file is a language translation template file. After copying and renaming, translate the text into the corresponding language. 此文件为语言翻译模板文件复制并改名后将文本翻译成对应语言即可
注意请勿修改//@@打头的文本行;以下代码结构由/src/package-i18n.js自动生成只允许在字符串中填写翻译后的文本请勿改变代码结构翻译的文本如果需要明确的空值请填写"=Empty";文本中的变量用{n}表示n代表第几个变量所有变量必须都出现至少一次如果不要某变量用{n!}表示
Note: Do not modify the text lines starting with //@@; The following code structure is automatically generated by /src/package-i18n.js, only the translated text is allowed to be filled in the string, please do not change the code structure; If the translated text requires an explicit empty value, please fill in "=Empty"; Variables in the text are represented by {n} (n represents the number of variables), all variables must appear at least once, if a variable is not required, it is represented by {n!}
*/
(function(factory){
var browser=typeof window=="object" && !!window.document;
var win=browser?window:Object; //非浏览器环境Recorder挂载在Object下面
factory(win.Recorder,browser);
}(function(Recorder,isBrowser){
"use strict";
var i18n=Recorder.i18n;
//@@User Code-1 Begin 手写代码放这里 Put the handwritten code here @@
//@@User Code-1 End @@
//@@Exec i18n.lang="Your-Language-Name";
Recorder.CLog('Import Page[Recorder_UniCore] lang="Your-Language-Name"');
//@@Exec i18n.alias["Your-Language-Name"]="your-language";
var putSet={lang:"your-language"};
//@@Exec i18n.data["rtl$your-language"]=false;
i18n.data["desc-page-Recorder_UniCore$your-language"]="This file is a language translation template file. After copying and renaming, translate the text into the corresponding language. 此文件为语言翻译模板文件,复制并改名后,将文本翻译成对应语言即可。";
//@@Exec i18n.GenerateDisplayEnglish=true;
//*************** Begin srcFile=../app-support-sample/demo_UniApp/uni_modules/Recorder-UniCore/app-uni-support.js ***************
i18n.put(putSet,
[ //@@PutList
//@@zh="微信小程序中需要:{1}"
//@@en="WeChat miniProgram requires: {1}"
//@@Put0
"RXs7:"+ //args: {1}
"" /** TODO: translate to your-language **/
//@@zh="Recorder-UniCore目前只支持H5、APP(Android iOS)、MP-WEIXIN其他平台环境需要自行编写适配文件实现接入"
//@@en="Recorder-UniCore currently only supports: H5, APP (Android iOS), MP-WEIXIN, other platform environments need to write their own adaptation files to achieve access"
,"4ATo:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="RecordApp.UniWebViewActivate 需要传入当前页面或组件的this对象作为参数"
//@@en="RecordApp.UniWebViewActivate needs to pass in the this object of the current page or component as a parameter"
,"GwCz:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="RecordApp.UniWebViewActivate 发生不应该出现的错误(可能需要升级插件代码):"
//@@en="An error occurred in RecordApp.UniWebViewActivate that should not occur (the plug-in code may need to be upgraded): "
,"ipB3:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="RecordApp.UniWebViewActivate 已切换当前页面或组件的renderjs所在的WebView"
//@@en="RecordApp.UniWebViewActivate has switched the WebView where the renderjs of the current page or component is located"
,"WpKg:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="RecordApp.UniRenderjsRegister 发生不应该出现的错误(可能需要升级插件代码):"
//@@en="An error occurred in RecordApp.UniRenderjsRegister that should not occur (the plugin code may need to be upgraded): "
,"Uc9E:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="RecordApp.UniRenderjsRegister 重复注册当前页面renderjs模块一个组件内只允许一个renderjs模块进行注册"
//@@en="RecordApp.UniRenderjsRegister repeatedly registers the renderjs module of the current page. Only one renderjs module is allowed to be registered in a component"
,"mzKj:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="RecordApp.UniRenderjsRegister 已注册当前页面renderjs模块"
//@@en="RecordApp.UniRenderjsRegister has registered the renderjs module of the current page"
,"7kJS:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="严重兼容性问题无法获取页面或组件this.$root.$scope或.$page"
//@@en="Serious compatibility issue: Unable to get page or component this.$root.$scope or .$page"
,"KpY6:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="需要先调用RecordApp.UniWebViewActivate方法"
//@@en="You need to call the RecordApp.UniWebViewActivate method first"
,"AGd7:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="需先调用RecordApp.RequestPermission方法"
//@@en="You need to call the RecordApp.RequestPermission method first"
,"7ot0:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="需重新调用RecordApp.RequestPermission方法"
//@@en="The RecordApp.RequestPermission method needs to be called again"
,"VsdN:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="检测到有其他页面或组件调用了RecordApp.UniPageOnShowWvCid={1}但未调用过RecordApp.UniWebViewActivate当前WvCid={2}部分功能会继续使用之前Activate的WebView和组件请确保这是符合你的业务逻辑不是因为忘记了调用UniWebViewActivate"
//@@en="It is detected that another page or component has called RecordApp.UniPageOnShow (WvCid={1}), but RecordApp.UniWebViewActivate (current WvCid={2}) has not been called. Some functions will continue to use the previously Activated WebView and components. Please make sure This is in line with your business logic, not because you forgot to call UniWebViewActivate"
,"SWsy:"+ //args: {1}-{2}
"" /** TODO: translate to your-language **/
//@@zh="{1}未正确查询到节点将使用传入的当前页面或组件this的$el.parentNode作为组件根节点。如果template下存在多个根节点(vue3 multi-root)尽量在最外面再套一层view来避免兼容性问题"
//@@en="{1} does not query the node correctly, and will use the current page or component this's $el.parentNode as the component root node. If there are multiple root nodes under the template (vue3 multi-root), try to add another layer of view on the outermost to avoid compatibility issues"
,"dX7B:"+ //args: {1}
"" /** TODO: translate to your-language **/
//@@zh="{1}需在renderjs中调用并且传入当前模块的this"
//@@en="{1} needs to be called in renderjs and pass in this of the current module"
,"dX5B:"+ //args: {1}
"" /** TODO: translate to your-language **/
//@@zh="{1}需要传入当前页面或组件的this对象作为参数"
//@@en="{1} needs to pass in the this object of the current page or component as a parameter"
,"dX6B:"+ //args: {1}
"" /** TODO: translate to your-language **/
//@@zh="当前不是App逻辑层"
//@@en="Currently it is not the App logic layer"
,"TfJX:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="当前还未调用过RecordApp.UniWebViewActivate"
//@@en="RecordApp.UniWebViewActivate has not been called yet"
,"peIm:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="未找到此页面renderjs所在的WebView"
//@@en="The WebView where renderjs for this page is not found"
,"qDo1:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="不可以调用RecordApp.UniWebViewEval"
//@@en=", RecordApp.UniWebViewEval cannot be called"
,"igw2:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="当前不是App逻辑层"
//@@en="Currently it is not the App logic layer"
,"lU1W:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="当前还未调用过RecordApp.UniWebViewActivate"
//@@en="RecordApp.UniWebViewActivate has not been called yet"
,"mSbR:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="未找到此页面renderjs所在的WebView Cid"
//@@en="The WebView Cid where renderjs for this page is not found"
,"6Iql:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="不可以调用RecordApp.UniWebViewVueCall"
//@@en=", RecordApp.UniWebViewVueCall cannot be called"
,"TtoS:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="renderjs中未import导入RecordApp"
//@@en="RecordApp is not imported in renderjs"
,"U1Be:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="renderjs中的mounted内需要调用RecordApp.UniRenderjsRegister"
//@@en="RecordApp.UniRenderjsRegister needs to be called in mounted in renderjs"
,"Bcgi:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="没有找到组件的renderjs模块"
//@@en="The renderjs module for the component was not found"
,"URyD:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="{1}连接renderjs超时"
//@@en="{1} connection renderjs timeout"
,"KQhJ:"+ //args: {1}
"" /** TODO: translate to your-language **/
//@@zh="{1}处理超时"
//@@en="{1} processing timeout"
,"RDcZ:"+ //args: {1}
"" /** TODO: translate to your-language **/
//@@zh="需要在页面中提供一个renderjs在里面import导入RecordApp、录音格式编码器、可视化插件等"
//@@en="You need to provide a renderjs in the page, and import RecordApp, recording format encoder, visualization plug-in, etc."
,"TSmQ:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="需在renderjs中import {1}"
//@@en="Need to import {1} in renderjs"
,"AN0e:"+ //args: {1}
"" /** TODO: translate to your-language **/
//@@zh="不应该出现的MainReceiveBind重复绑定"
//@@en="MainReceiveBind duplicate binding that should not occur"
,"vEgr:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="从renderjs发回数据但UniMainCallBack回调不存在"
//@@en="Sending data back from renderjs but UniMainCallBack callback does not exist: "
,"kZx6:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="[MainReceive]从renderjs发回未知数据"
//@@en="[MainReceive] Unknown data sent back from renderjs: "
,"ZHwv:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="只允许在renderjs中调用RecordApp.UniWebViewSendBigBytesToMain"
//@@en="Only allowed to call RecordApp.UniWebViewSendBigBytesToMain in renderjs"
,"MujG:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="renderjs中的mounted内需要调用RecordApp.UniRenderjsRegister才能调用RecordApp.UniWebViewSendBigBytesToMain"
//@@en="RecordApp.UniRenderjsRegister needs to be called in mounted in renderjs to call RecordApp.UniWebViewSendBigBytesToMain"
,"kE91:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="无效的BigBytes回传数据"
//@@en="Invalid BigBytes return data"
,"CjMb:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="保存文件{1}失败:"
//@@en="Failed to save file {1}: "
,"UqfI:"+ //args: {1}
"" /** TODO: translate to your-language **/
//@@zh="当前环境未支持保存本地文件"
//@@en="The current environment does not support saving local files"
,"kxOd:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh=" | RecordApp的uni-app支持文档和示例{1} "
//@@en=" | RecordApps uni-app support documentation and examples: {1}"
,"1f2V:"+ //args: {1}
"" /** TODO: translate to your-language **/
//@@zh="当前录音由原生录音插件提供支持"
//@@en="Current recording is powered by native recording plug-in"
,"XSYY:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="当前录音由uts插件提供支持"
//@@en="Current recording is powered by uts plugin"
,"nnM6:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="当前已配置RecordApp.UniWithoutAppRenderjs必须提供原生录音插件或uts插件才能录音请参考RecordApp.UniNativeUtsPlugin配置"
//@@en="RecordApp.UniWithoutAppRenderjs is currently configured. A native recording plug-in or uts plug-in must be provided to record. Please refer to the RecordApp.UniNativeUtsPlugin configuration"
,"fqhr:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="当前RecordApp运行在逻辑层中性能会略低一些可视化等插件不可用"
//@@en="Currently RecordApp runs in the logical layer (performance will be slightly lower, and plug-ins such as visualization are not available) "
,"xYRb:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="未找到当前页面renderjs所在的WebView"
//@@en="The WebView where renderjs of the current page is located is not found"
,"S3eF:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="当前RecordApp运行在renderjs所在的WebView中逻辑层中只能做有限的实时处理可视化等插件均需要在renderjs中进行调用"
//@@en="The current RecordApp runs in the WebView where renderjs is located (only limited real-time processing can be done in the logic layer, and visualization and other plug-ins need to be called in renderjs) "
,"0hyi:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="请检查此页面代码中是否编写了lang=renderjs的module并且调用了RecordApp.UniRenderjsRegister如果确实没有renderjs比如nvue页面请设置RecordApp.UniWithoutAppRenderjs=true并且搭配配套的原生插件在逻辑层中直接录音"
//@@en=", please check whether the module with lang=renderjs is written in the code of this page and RecordApp.UniRenderjsRegister is called; if there is indeed no renderjs, such as nvue page, please set RecordApp.UniWithoutAppRenderjs=true and use the matching native plug-in to record directly in the logic layer"
,"e6Mo:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="【在App内使用{1}的授权许可】"
//@@en="[License for use of {1} within the App] "
,"FabE:"+ //args: {1}
"" /** TODO: translate to your-language **/
//@@zh="已购买原生录音插件,获得授权许可"
//@@en="Purchased the native recording plug-in and obtained the license"
,"w37G:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="已购买uts插件获得授权许可"
//@@en="Purchased uts plug-in and obtained license"
,"e71S:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="UniAppUseLicense填写无效如果已获取到了商用授权请填写{1},否则请使用空字符串"
//@@en="UniAppUseLicense is invalid. If you have obtained a commercial license, please fill in: {1}, otherwise please use an empty string"
,"aPoj:"+ //args: {1}
"" /** TODO: translate to your-language **/
//@@zh="未找到Canvas{1}请确保此DOM已挂载可尝试用$nextTick等待DOM更新"
//@@en="Canvas not found: {1}, please make sure this DOM is mounted (try $nextTick to wait for DOM update) "
,"k7im:"+ //args: {1}
"" /** TODO: translate to your-language **/
//@@zh="RecordApp.UniFindCanvas未适配当前环境"
//@@en="RecordApp.UniFindCanvas does not adapt to the current environment"
,"yI24:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="未配置RecordApp.UniNativeUtsPlugin原生录音插件"
//@@en="RecordApp.UniNativeUtsPlugin native recording plug-in is not configured"
,"H753:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="renderjs中不支持设置RecordApp.UniNativeUtsPlugin"
//@@en="Setting RecordApp.UniNativeUtsPlugin is not supported in renderjs"
,"l6sY:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="当前App未打包进双端原生插件[{1}],尝试加载单端[{2}]"
//@@en="The current App is not packaged into the dual-end native plug-in [{1}], try to load the single-end [{2}]"
,"kSjQ:"+ //args: {1}-{2}
"" /** TODO: translate to your-language **/
//@@zh="已加载原生录音插件[{1}]"
//@@en="Native recording plugin loaded [{1}]"
,"Xh1W:"+ //args: {1}
"" /** TODO: translate to your-language **/
//@@zh="配置了RecordApp.UniNativeUtsPlugin但当前App未打包进原生录音插件[{1}]"
//@@en="RecordApp.UniNativeUtsPlugin is configured, but the current App is not packaged with the native recording plug-in [{1}]"
,"SCW9:"+ //args: {1}
"" /** TODO: translate to your-language **/
//@@zh="提供的RecordApp.UniNativeUtsPlugin值不是RecordApp的uts原生录音插件"
//@@en="The provided RecordApp.UniNativeUtsPlugin value is not RecordApps uts native recording plug-in"
,"TGMm:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="需在App逻辑层中调用原生插件功能"
//@@en="The native plug-in function needs to be called in the App logic layer"
,"MrBx:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="未开始录音,不可以调用{1}"
//@@en="Recording has not started and {1} cannot be called"
,"0FGq:"+ //args: {1}
"" /** TODO: translate to your-language **/
//@@zh="需先调用RecordApp.UniWebViewActivate然后才可以调用RequestPermission"
//@@en="RecordApp.UniWebViewActivate needs to be called first, and then RequestPermission can be called"
,"PkQ2:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="不应当出现的非H5权限请求"
//@@en="Non-H5 permission requests that should not appear"
,"Jk72:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="正在调用plus.ios@AVAudioSession请求iOS原生录音权限"
//@@en="Calling plus.ios@AVAudioSession to request iOS native recording permissions"
,"Y3rC:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="项目配置中未声明iOS录音权限{1}"
//@@en="iOS recording permission {1} is not declared in the project configuration"
,"9xoE:"+ //args: {1}
"" /** TODO: translate to your-language **/
//@@zh="已获得iOS原生录音权限"
//@@en="Obtained iOS native recording permissions"
,"j15C:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="plus.ios请求录音权限状态值: "
//@@en="plus.ios requests recording permission, status value: "
,"iKhe:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="正在调用plus.android.requestPermissions请求Android原生录音权限"
//@@en="Calling plus.android.requestPermissions to request Android native recording permissions"
,"7Noe:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="已获得Android原生录音权限"
//@@en="Obtained Android native recording permission: "
,"Bgls:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="plus.android请求录音权限无权限"
//@@en="plus.android requests recording permission: No permission"
,"Ruxl:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="plus.android请求录音权限出错"
//@@en="plus.android error in requesting recording permission: "
,"0JQw:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="调用plus的权限请求出错"
//@@en="An error occurred in the permission request to call plus: "
,"Mvl7:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="用户拒绝了录音权限"
//@@en="User denied recording permission"
,"0caE:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="正在调用原生插件请求录音权限"
//@@en="Calling the native plug-in to request recording permission"
,"Lx5r:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="已获得录音权限"
//@@en="Recording permission obtained"
,"Lx6r:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="无录音权限"
//@@en="No recording permission"
,"Lx7r:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="无法调用RequestPermission"
//@@en="Unable to call RequestPermission: "
,"ksoA:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="无法连接到renderjs"
//@@en="Unable to connect to renderjs"
,"KnF0:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="需先调用RecordApp.UniWebViewActivate然后才可以调用Start"
//@@en="RecordApp.UniWebViewActivate needs to be called first, and then Start can be called"
,"XCMU:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="不应当出现的非H5录音Start"
//@@en="Start of non-H5 recordings that should not appear"
,"rSLO:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="无法调用Start"
//@@en="Unable to call Start: "
,"Bjx9:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="未开始录音但收到renderjs回传的onRecEncodeChunk"
//@@en="Recording did not start, but onRecEncodeChunk returned by renderjs was received"
,"MTdp:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="未开始录音但收到Uni Native PCM数据"
//@@en="Recording did not start, but Uni Native PCM data was received"
,"BjGP:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="未开始录音但收到UniNativeUtsPlugin PCM数据"
//@@en="Recording did not start, but UniNativeUtsPlugin PCM data was received"
,"byzO:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="未开始录音"
//@@en="Recording not started"
,"YP4V:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="不应当出现的非H5录音Stop"
//@@en="Stop non-H5 recordings that should not appear"
,"TPhg:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="未开始录音"
//@@en="Recording not started"
,"pP4O:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="无法调用Stop"
//@@en="Unable to call Stop: "
,"H6cq:"+ //no args
"" /** TODO: translate to your-language **/
//@@zh="不应该出现的renderjs发回的文件数据丢失"
//@@en="The file data sent back by renderjs should not be lost"
,"gomD:"+ //no args
"" /** TODO: translate to your-language **/
]);
//*************** End srcFile=../app-support-sample/demo_UniApp/uni_modules/Recorder-UniCore/app-uni-support.js ***************
//@@User Code-2 Begin 手写代码放这里 Put the handwritten code here @@
//@@User Code-2 End @@
}));

View File

@ -1,406 +0,0 @@
/*
Recorder ../app-support-sample/demo_UniApp/uni_modules/Recorder-UniCore/i18n/en-US.js
https://github.com/xiangyuecn/Recorder
Usage: Recorder.i18n.lang="en-US" or "en"
Desc: English, 英语This translation mainly comes from: google translation + Baidu translation, translated from Chinese to English. 此翻译主要来自google翻译+百度翻译由中文翻译成英文
注意请勿修改//@@打头的文本行;以下代码结构由/src/package-i18n.js自动生成只允许在字符串中填写翻译后的文本请勿改变代码结构翻译的文本如果需要明确的空值请填写"=Empty";文本中的变量用{n}表示n代表第几个变量所有变量必须都出现至少一次如果不要某变量用{n!}表示
Note: Do not modify the text lines starting with //@@; The following code structure is automatically generated by /src/package-i18n.js, only the translated text is allowed to be filled in the string, please do not change the code structure; If the translated text requires an explicit empty value, please fill in "=Empty"; Variables in the text are represented by {n} (n represents the number of variables), all variables must appear at least once, if a variable is not required, it is represented by {n!}
*/
(function(factory){
var browser=typeof window=="object" && !!window.document;
var win=browser?window:Object; //非浏览器环境Recorder挂载在Object下面
factory(win.Recorder,browser);
}(function(Recorder,isBrowser){
"use strict";
var i18n=Recorder.i18n;
//@@User Code-1 Begin 手写代码放这里 Put the handwritten code here @@
//@@User Code-1 End @@
//@@Exec i18n.lang="en-US";
Recorder.CLog('Import Page[Recorder_UniCore] lang="en-US"');
//@@Exec i18n.alias["en-US"]="en";
var putSet={lang:"en"};
//@@Exec i18n.data["rtl$en"]=false;
i18n.data["desc-page-Recorder_UniCore$en"]="English, 英语。This translation mainly comes from: google translation + Baidu translation, translated from Chinese to English. 此翻译主要来自google翻译+百度翻译,由中文翻译成英文。";
//@@Exec i18n.GenerateDisplayEnglish=false;
//*************** Begin srcFile=../app-support-sample/demo_UniApp/uni_modules/Recorder-UniCore/app-uni-support.js ***************
i18n.put(putSet,
[ //@@PutList
//@@zh="微信小程序中需要:{1}"
//@@Put0
"RXs7:"+ //args: {1}
"WeChat miniProgram requires: {1}"
//@@zh="Recorder-UniCore目前只支持H5、APP(Android iOS)、MP-WEIXIN其他平台环境需要自行编写适配文件实现接入"
,"4ATo:"+ //no args
"Recorder-UniCore currently only supports: H5, APP (Android iOS), MP-WEIXIN, other platform environments need to write their own adaptation files to achieve access"
//@@zh="RecordApp.UniWebViewActivate 需要传入当前页面或组件的this对象作为参数"
,"GwCz:"+ //no args
"RecordApp.UniWebViewActivate needs to pass in the this object of the current page or component as a parameter"
//@@zh="RecordApp.UniWebViewActivate 发生不应该出现的错误(可能需要升级插件代码):"
,"ipB3:"+ //no args
"An error occurred in RecordApp.UniWebViewActivate that should not occur (the plug-in code may need to be upgraded): "
//@@zh="RecordApp.UniWebViewActivate 已切换当前页面或组件的renderjs所在的WebView"
,"WpKg:"+ //no args
"RecordApp.UniWebViewActivate has switched the WebView where the renderjs of the current page or component is located"
//@@zh="RecordApp.UniRenderjsRegister 发生不应该出现的错误(可能需要升级插件代码):"
,"Uc9E:"+ //no args
"An error occurred in RecordApp.UniRenderjsRegister that should not occur (the plugin code may need to be upgraded): "
//@@zh="RecordApp.UniRenderjsRegister 重复注册当前页面renderjs模块一个组件内只允许一个renderjs模块进行注册"
,"mzKj:"+ //no args
"RecordApp.UniRenderjsRegister repeatedly registers the renderjs module of the current page. Only one renderjs module is allowed to be registered in a component"
//@@zh="RecordApp.UniRenderjsRegister 已注册当前页面renderjs模块"
,"7kJS:"+ //no args
"RecordApp.UniRenderjsRegister has registered the renderjs module of the current page"
//@@zh="严重兼容性问题无法获取页面或组件this.$root.$scope或.$page"
,"KpY6:"+ //no args
"Serious compatibility issue: Unable to get page or component this.$root.$scope or .$page"
//@@zh="需要先调用RecordApp.UniWebViewActivate方法"
,"AGd7:"+ //no args
"You need to call the RecordApp.UniWebViewActivate method first"
//@@zh="需先调用RecordApp.RequestPermission方法"
,"7ot0:"+ //no args
"You need to call the RecordApp.RequestPermission method first"
//@@zh="需重新调用RecordApp.RequestPermission方法"
,"VsdN:"+ //no args
"The RecordApp.RequestPermission method needs to be called again"
//@@zh="检测到有其他页面或组件调用了RecordApp.UniPageOnShowWvCid={1}但未调用过RecordApp.UniWebViewActivate当前WvCid={2}部分功能会继续使用之前Activate的WebView和组件请确保这是符合你的业务逻辑不是因为忘记了调用UniWebViewActivate"
,"SWsy:"+ //args: {1}-{2}
"It is detected that another page or component has called RecordApp.UniPageOnShow (WvCid={1}), but RecordApp.UniWebViewActivate (current WvCid={2}) has not been called. Some functions will continue to use the previously Activated WebView and components. Please make sure This is in line with your business logic, not because you forgot to call UniWebViewActivate"
//@@zh="{1}未正确查询到节点将使用传入的当前页面或组件this的$el.parentNode作为组件根节点。如果template下存在多个根节点(vue3 multi-root)尽量在最外面再套一层view来避免兼容性问题"
,"dX7B:"+ //args: {1}
"{1} does not query the node correctly, and will use the current page or component this's $el.parentNode as the component root node. If there are multiple root nodes under the template (vue3 multi-root), try to add another layer of view on the outermost to avoid compatibility issues"
//@@zh="{1}需在renderjs中调用并且传入当前模块的this"
,"dX5B:"+ //args: {1}
"{1} needs to be called in renderjs and pass in this of the current module"
//@@zh="{1}需要传入当前页面或组件的this对象作为参数"
,"dX6B:"+ //args: {1}
"{1} needs to pass in the this object of the current page or component as a parameter"
//@@zh="当前不是App逻辑层"
,"TfJX:"+ //no args
"Currently it is not the App logic layer"
//@@zh="当前还未调用过RecordApp.UniWebViewActivate"
,"peIm:"+ //no args
"RecordApp.UniWebViewActivate has not been called yet"
//@@zh="未找到此页面renderjs所在的WebView"
,"qDo1:"+ //no args
"The WebView where renderjs for this page is not found"
//@@zh="不可以调用RecordApp.UniWebViewEval"
,"igw2:"+ //no args
", RecordApp.UniWebViewEval cannot be called"
//@@zh="当前不是App逻辑层"
,"lU1W:"+ //no args
"Currently it is not the App logic layer"
//@@zh="当前还未调用过RecordApp.UniWebViewActivate"
,"mSbR:"+ //no args
"RecordApp.UniWebViewActivate has not been called yet"
//@@zh="未找到此页面renderjs所在的WebView Cid"
,"6Iql:"+ //no args
"The WebView Cid where renderjs for this page is not found"
//@@zh="不可以调用RecordApp.UniWebViewVueCall"
,"TtoS:"+ //no args
", RecordApp.UniWebViewVueCall cannot be called"
//@@zh="renderjs中未import导入RecordApp"
,"U1Be:"+ //no args
"RecordApp is not imported in renderjs"
//@@zh="renderjs中的mounted内需要调用RecordApp.UniRenderjsRegister"
,"Bcgi:"+ //no args
"RecordApp.UniRenderjsRegister needs to be called in mounted in renderjs"
//@@zh="没有找到组件的renderjs模块"
,"URyD:"+ //no args
"The renderjs module for the component was not found"
//@@zh="{1}连接renderjs超时"
,"KQhJ:"+ //args: {1}
"{1} connection renderjs timeout"
//@@zh="{1}处理超时"
,"RDcZ:"+ //args: {1}
"{1} processing timeout"
//@@zh="需要在页面中提供一个renderjs在里面import导入RecordApp、录音格式编码器、可视化插件等"
,"TSmQ:"+ //no args
"You need to provide a renderjs in the page, and import RecordApp, recording format encoder, visualization plug-in, etc."
//@@zh="需在renderjs中import {1}"
,"AN0e:"+ //args: {1}
"Need to import {1} in renderjs"
//@@zh="不应该出现的MainReceiveBind重复绑定"
,"vEgr:"+ //no args
"MainReceiveBind duplicate binding that should not occur"
//@@zh="从renderjs发回数据但UniMainCallBack回调不存在"
,"kZx6:"+ //no args
"Sending data back from renderjs but UniMainCallBack callback does not exist: "
//@@zh="[MainReceive]从renderjs发回未知数据"
,"ZHwv:"+ //no args
"[MainReceive] Unknown data sent back from renderjs: "
//@@zh="只允许在renderjs中调用RecordApp.UniWebViewSendBigBytesToMain"
,"MujG:"+ //no args
"Only allowed to call RecordApp.UniWebViewSendBigBytesToMain in renderjs"
//@@zh="renderjs中的mounted内需要调用RecordApp.UniRenderjsRegister才能调用RecordApp.UniWebViewSendBigBytesToMain"
,"kE91:"+ //no args
"RecordApp.UniRenderjsRegister needs to be called in mounted in renderjs to call RecordApp.UniWebViewSendBigBytesToMain"
//@@zh="无效的BigBytes回传数据"
,"CjMb:"+ //no args
"Invalid BigBytes return data"
//@@zh="保存文件{1}失败:"
,"UqfI:"+ //args: {1}
"Failed to save file {1}: "
//@@zh="当前环境未支持保存本地文件"
,"kxOd:"+ //no args
"The current environment does not support saving local files"
//@@zh=" | RecordApp的uni-app支持文档和示例{1} "
,"1f2V:"+ //args: {1}
" | RecordApps uni-app support documentation and examples: {1}"
//@@zh="当前录音由原生录音插件提供支持"
,"XSYY:"+ //no args
"Current recording is powered by native recording plug-in"
//@@zh="当前录音由uts插件提供支持"
,"nnM6:"+ //no args
"Current recording is powered by uts plugin"
//@@zh="当前已配置RecordApp.UniWithoutAppRenderjs必须提供原生录音插件或uts插件才能录音请参考RecordApp.UniNativeUtsPlugin配置"
,"fqhr:"+ //no args
"RecordApp.UniWithoutAppRenderjs is currently configured. A native recording plug-in or uts plug-in must be provided to record. Please refer to the RecordApp.UniNativeUtsPlugin configuration"
//@@zh="当前RecordApp运行在逻辑层中性能会略低一些可视化等插件不可用"
,"xYRb:"+ //no args
"Currently RecordApp runs in the logical layer (performance will be slightly lower, and plug-ins such as visualization are not available) "
//@@zh="未找到当前页面renderjs所在的WebView"
,"S3eF:"+ //no args
"The WebView where renderjs of the current page is located is not found"
//@@zh="当前RecordApp运行在renderjs所在的WebView中逻辑层中只能做有限的实时处理可视化等插件均需要在renderjs中进行调用"
,"0hyi:"+ //no args
"The current RecordApp runs in the WebView where renderjs is located (only limited real-time processing can be done in the logic layer, and visualization and other plug-ins need to be called in renderjs) "
//@@zh="请检查此页面代码中是否编写了lang=renderjs的module并且调用了RecordApp.UniRenderjsRegister如果确实没有renderjs比如nvue页面请设置RecordApp.UniWithoutAppRenderjs=true并且搭配配套的原生插件在逻辑层中直接录音"
,"e6Mo:"+ //no args
", please check whether the module with lang=renderjs is written in the code of this page and RecordApp.UniRenderjsRegister is called; if there is indeed no renderjs, such as nvue page, please set RecordApp.UniWithoutAppRenderjs=true and use the matching native plug-in to record directly in the logic layer"
//@@zh="【在App内使用{1}的授权许可】"
,"FabE:"+ //args: {1}
"[License for use of {1} within the App] "
//@@zh="已购买原生录音插件,获得授权许可"
,"w37G:"+ //no args
"Purchased the native recording plug-in and obtained the license"
//@@zh="已购买uts插件获得授权许可"
,"e71S:"+ //no args
"Purchased uts plug-in and obtained license"
//@@zh="UniAppUseLicense填写无效如果已获取到了商用授权请填写{1},否则请使用空字符串"
,"aPoj:"+ //args: {1}
"UniAppUseLicense is invalid. If you have obtained a commercial license, please fill in: {1}, otherwise please use an empty string"
//@@zh="未找到Canvas{1}请确保此DOM已挂载可尝试用$nextTick等待DOM更新"
,"k7im:"+ //args: {1}
"Canvas not found: {1}, please make sure this DOM is mounted (try $nextTick to wait for DOM update) "
//@@zh="RecordApp.UniFindCanvas未适配当前环境"
,"yI24:"+ //no args
"RecordApp.UniFindCanvas does not adapt to the current environment"
//@@zh="未配置RecordApp.UniNativeUtsPlugin原生录音插件"
,"H753:"+ //no args
"RecordApp.UniNativeUtsPlugin native recording plug-in is not configured"
//@@zh="renderjs中不支持设置RecordApp.UniNativeUtsPlugin"
,"l6sY:"+ //no args
"Setting RecordApp.UniNativeUtsPlugin is not supported in renderjs"
//@@zh="当前App未打包进双端原生插件[{1}],尝试加载单端[{2}]"
,"kSjQ:"+ //args: {1}-{2}
"The current App is not packaged into the dual-end native plug-in [{1}], try to load the single-end [{2}]"
//@@zh="已加载原生录音插件[{1}]"
,"Xh1W:"+ //args: {1}
"Native recording plugin loaded [{1}]"
//@@zh="配置了RecordApp.UniNativeUtsPlugin但当前App未打包进原生录音插件[{1}]"
,"SCW9:"+ //args: {1}
"RecordApp.UniNativeUtsPlugin is configured, but the current App is not packaged with the native recording plug-in [{1}]"
//@@zh="提供的RecordApp.UniNativeUtsPlugin值不是RecordApp的uts原生录音插件"
,"TGMm:"+ //no args
"The provided RecordApp.UniNativeUtsPlugin value is not RecordApps uts native recording plug-in"
//@@zh="需在App逻辑层中调用原生插件功能"
,"MrBx:"+ //no args
"The native plug-in function needs to be called in the App logic layer"
//@@zh="未开始录音,不可以调用{1}"
,"0FGq:"+ //args: {1}
"Recording has not started and {1} cannot be called"
//@@zh="需先调用RecordApp.UniWebViewActivate然后才可以调用RequestPermission"
,"PkQ2:"+ //no args
"RecordApp.UniWebViewActivate needs to be called first, and then RequestPermission can be called"
//@@zh="不应当出现的非H5权限请求"
,"Jk72:"+ //no args
"Non-H5 permission requests that should not appear"
//@@zh="正在调用plus.ios@AVAudioSession请求iOS原生录音权限"
,"Y3rC:"+ //no args
"Calling plus.ios@AVAudioSession to request iOS native recording permissions"
//@@zh="项目配置中未声明iOS录音权限{1}"
,"9xoE:"+ //args: {1}
"iOS recording permission {1} is not declared in the project configuration"
//@@zh="已获得iOS原生录音权限"
,"j15C:"+ //no args
"Obtained iOS native recording permissions"
//@@zh="plus.ios请求录音权限状态值: "
,"iKhe:"+ //no args
"plus.ios requests recording permission, status value: "
//@@zh="正在调用plus.android.requestPermissions请求Android原生录音权限"
,"7Noe:"+ //no args
"Calling plus.android.requestPermissions to request Android native recording permissions"
//@@zh="已获得Android原生录音权限"
,"Bgls:"+ //no args
"Obtained Android native recording permission: "
//@@zh="plus.android请求录音权限无权限"
,"Ruxl:"+ //no args
"plus.android requests recording permission: No permission"
//@@zh="plus.android请求录音权限出错"
,"0JQw:"+ //no args
"plus.android error in requesting recording permission: "
//@@zh="调用plus的权限请求出错"
,"Mvl7:"+ //no args
"An error occurred in the permission request to call plus: "
//@@zh="用户拒绝了录音权限"
,"0caE:"+ //no args
"User denied recording permission"
//@@zh="正在调用原生插件请求录音权限"
,"Lx5r:"+ //no args
"Calling the native plug-in to request recording permission"
//@@zh="已获得录音权限"
,"Lx6r:"+ //no args
"Recording permission obtained"
//@@zh="无录音权限"
,"Lx7r:"+ //no args
"No recording permission"
//@@zh="无法调用RequestPermission"
,"ksoA:"+ //no args
"Unable to call RequestPermission: "
//@@zh="无法连接到renderjs"
,"KnF0:"+ //no args
"Unable to connect to renderjs"
//@@zh="需先调用RecordApp.UniWebViewActivate然后才可以调用Start"
,"XCMU:"+ //no args
"RecordApp.UniWebViewActivate needs to be called first, and then Start can be called"
//@@zh="不应当出现的非H5录音Start"
,"rSLO:"+ //no args
"Start of non-H5 recordings that should not appear"
//@@zh="无法调用Start"
,"Bjx9:"+ //no args
"Unable to call Start: "
//@@zh="未开始录音但收到renderjs回传的onRecEncodeChunk"
,"MTdp:"+ //no args
"Recording did not start, but onRecEncodeChunk returned by renderjs was received"
//@@zh="未开始录音但收到Uni Native PCM数据"
,"BjGP:"+ //no args
"Recording did not start, but Uni Native PCM data was received"
//@@zh="未开始录音但收到UniNativeUtsPlugin PCM数据"
,"byzO:"+ //no args
"Recording did not start, but UniNativeUtsPlugin PCM data was received"
//@@zh="未开始录音"
,"YP4V:"+ //no args
"Recording not started"
//@@zh="不应当出现的非H5录音Stop"
,"TPhg:"+ //no args
"Stop non-H5 recordings that should not appear"
//@@zh="未开始录音"
,"pP4O:"+ //no args
"Recording not started"
//@@zh="无法调用Stop"
,"H6cq:"+ //no args
"Unable to call Stop: "
//@@zh="不应该出现的renderjs发回的文件数据丢失"
,"gomD:"+ //no args
"The file data sent back by renderjs should not be lost"
]);
//*************** End srcFile=../app-support-sample/demo_UniApp/uni_modules/Recorder-UniCore/app-uni-support.js ***************
//@@User Code-2 Begin 手写代码放这里 Put the handwritten code here @@
//@@User Code-2 End @@
}));

View File

@ -1,19 +0,0 @@
《许可及服务协议》
**您以下称“用户”下载、使用我以下称“作者”提供的Recorder-UniCore组件含原生录音插件、uts插件以下统称“本组件”应当阅读并遵守本许可协议。请用户务必审慎阅读、充分理解各条款内容特别是免除或者限制责任的条款并选择接受或不接受。除非用户已阅读并接受本协议所有条款否则用户无权下载、使用本组件及相关服务用户的下载、使用等行为即视为用户已阅读并同意本许可协议的约束。**
1. 用户应当直接从作者许可的途径如作者的GitHub、Gitee仓库、已上架的DCloud插件市场、QQ群等途径中获取本组件其他途径获取到的组件代码是未经过作者授权的存在安全隐患可能会导致你的程序、资产受到侵害作者对因此给用户造成的损失不予负责。
2. 作者将积极并采取措施保护用户的信息和隐私;组件本身不会搜集存储任何用户信息。
3. 除法律法规有明确规定外,作者将尽最大努力确保本组件及其所涉及的技术及信息安全、有效、准确、可靠,但受限于现有技术,用户理解作者不能对此进行担保。
4. 用户理解,对于不可抗力及第三方原因导致的您的直接或间接损失,作者无法承担责任。
5. 用户因使用本组件进行生成、处理数据,由此引起或与有关的包括但不限于利润损失、资料损失、业务中断的损害赔偿或其它商业损害赔偿或损失,需由用户自行承担。
6. 如若发生赔偿、退款等行为,赔偿、退款等累计金额不得超过用户实际支付给作者的总金额。
7. 已授予的授权许可包括免费授权和已购买的原生录音插件、uts插件均仅限在授权指定的uni-app的应用标识AppID对应的项目上使用不可在其他项目上使用用户不得对本组件及其中的相关信息擅自出租、出借、销售、逆向工程、破解不得在未取得作者授权的情况下借助本组件发展与本组件有关联的衍生软件产品、服务、插件、外挂等。
8. 用户不得使用本组件从事违反法律法规政策、破坏公序良俗、损害公共利益的行为。

Some files were not shown because too many files have changed in this diff Show More