Compare commits

...

6 Commits

Author SHA1 Message Date
e4c2b7cdcb Merge branch 'wyfMain-dev'
Some checks are pending
Check / lint (push) Waiting to run
Check / typecheck (push) Waiting to run
Check / build (build, 18.x, ubuntu-latest) (push) Waiting to run
Check / build (build, 18.x, windows-latest) (push) Waiting to run
Check / build (build:app, 18.x, ubuntu-latest) (push) Waiting to run
Check / build (build:app, 18.x, windows-latest) (push) Waiting to run
Check / build (build:mp-weixin, 18.x, ubuntu-latest) (push) Waiting to run
Check / build (build:mp-weixin, 18.x, windows-latest) (push) Waiting to run
2025-03-13 09:33:55 +08:00
adb57bda88 完成部分艾特功能 2025-03-12 19:59:54 +08:00
4b3062eefa 新增艾特功能的选择界面 2025-03-12 16:26:39 +08:00
36342b4dd9 封装按字母选人页面为自定义组件 2025-03-12 16:25:42 +08:00
ae2a447fa4 新增聊天页面选择提醒的人弹窗 2025-03-11 19:56:43 +08:00
f2abedd88e 解决会话列表持久化后,聊天页面卡死的问题 2025-03-11 13:58:25 +08:00
9 changed files with 1078 additions and 626 deletions

View File

@ -1,5 +1,5 @@
<template>
<div class="avatar-module" :style="customStyle">
<div class="avatar-module" :style="[customStyle, { background: avatar ? '#fff' : '' }]">
<img :src="avatar" v-if="avatar" />
<span v-else :style="customTextStyle">{{ text_avatar }}</span>
</div>

View File

@ -0,0 +1,748 @@
<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'">
<customInput
:searchText="state.searchText"
@inputSearchText="inputSearchText"
></customInput>
</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"
: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 {
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: '/src/static/image/chatList/groupAllMember.png',
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
}
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)
}
}
})
}
//A-Z
const assembleAlphabetMemberList = async (newMemberList) => {
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 = []
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
if (props?.manageType === 'mention' && !props?.isMulSelect) {
resultMemberList.value.unshift({
key: '0',
memberList: [
{
avatar: '/src/static/image/chatList/groupAllMember.png',
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
console.log()
zPaging.value?.scrollIntoViewById(
alphabet,
document.getElementById('topArea')?.clientHeight
? document.getElementById('topArea').clientHeight - 1
: props?.manageType === 'mention'
? 140
: 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) {
emits('updateSelectedMembersNum', 1)
} else {
emits('updateSelectedMembersNum', -1)
}
}
//
const confirmSelectMembers = () => {
let selectedUserIds = ''
let itemList = dialogueParams.memberList
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) {
useDialogueStore().updateGroupMembers()
groupStore.ServeGroupDetail()
} else {
}
})
resp.catch(() => {})
} else if (props?.manageType === 'mention') {
emits('getSelectResult', selectUserInfos)
}
}
}
//
defineExpose({
confirmSelectMembers,
})
</script>
<style lang="scss" scoped>
.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

@ -11,145 +11,19 @@
@scroll="onScroll"
>
<template #top>
<customNavbar :title="pageTitle" id="topArea"></customNavbar>
<div id="topArea">
<customNavbar :title="pageTitle"></customNavbar>
</div>
</template>
<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>
</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)"
<selectMemberByAlphabet
: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>
: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"
@ -176,126 +50,70 @@
:disabled="state.selectedMembersNum == 0 ? true : false"
></customBtn>
</div>
</div>
</template>
</ZPaging>
</div>
</div>
</template>
<script setup>
import customInput from '@/components/custom-input/custom-input.vue'
import selectMemberItem from '../components/select-member-item.vue'
import selectMemberByAlphabet from '../components/selectMemberByAlphabet.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, onMounted, reactive, ref, watch, nextTick } from 'vue'
import { computed, reactive, ref, onMounted, 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 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, //
selectAreaHeight: 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(() => {
watch(
alphabetElementRefs,
(newAlphabetElementRefs) => {
if (Array.isArray(newAlphabetElementRefs)) {
newAlphabetElementRefs.forEach((el, index) => {
observeElement(el, index)
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
}
})
.exec()
const footAreaQuery = uni.createSelectorQuery()
footAreaQuery
.select('#footArea')
.boundingClientRect((res) => {
if (res) {
// console.log(':', res.height)
selectAreaHeight = selectAreaHeight - res.height
}
},
{ immediate: true, deep: true },
)
if (alphabetElementRefs.value.length > 0) {
alphabetElementRefs.value.forEach((el, index) =>
observeElement(el, index),
)
}
})
.exec()
// console.log(selectAreaHeight)
state.selectAreaHeight = selectAreaHeight + 'px'
})
})
@ -314,341 +132,16 @@ const pageTitle = computed(() => {
return page_title
})
//
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 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 = ''
if (selectMemberByAlphabetRef.value) {
selectMemberByAlphabetRef.value.confirmSelectMembers()
}
}
//
const checkBoxChange = (e) => {
if (e) {
state.selectedMembersNum += 1
} else {
state.selectedMembersNum -= 1
}
//
const updateSelectedMembersNum = (numChange) => {
state.selectedMembersNum = state.selectedMembersNum + numChange
}
</script>
<style scoped lang="scss">
@ -659,48 +152,6 @@ const checkBoxChange = (e) => {
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;

View File

@ -111,7 +111,6 @@
<span
class="nickname pointer"
v-show="talkParams.type == 2 && item.float == 'left'"
@click="onClickNickname(item)"
>
<span class="at">@</span>
{{ item.nickname }}
@ -212,15 +211,13 @@
}}
</span>
<span
v-if="state?.quoteInfo?.msg_type === 3"
v-if="state?.quoteInfo"
class="text-[28rpx] text-[#999]"
>
{{
state?.quoteInfo?.nickname +
'' +
'[' +
$t('msg.type') +
']'
ChatMsgTypeMapping[state?.quoteInfo?.msg_type]
}}
</span>
<img
@ -344,9 +341,99 @@
</div>
</template>
</ZPaging>
<tm-drawer
placement="bottom"
v-model:show="state.isShowMentionSelect"
:hideHeader="true"
:round="5"
:height="state.mentionSelectHeight"
>
<div
class="mention-select-drawer flex flex-row flex-1 flex-row flex-row-center-between"
>
<div
class="cancel-btns flex-row flex flex-row-center-start"
style="width: 210rpx;"
>
<div
class="hide-btn"
v-if="!state.mentionIsMulSelect"
@click="hideMentionSelect"
>
<img
style="width: 40rpx; height: 40rpx;"
src="/src/static/image/chatList/mention_select_hide_bg.png"
/>
<img
style="
position: absolute;
top: 50%;
left: 50%;
margin-left: -9rpx;
margin-top: -5rpx;
"
src="/src/static/image/chatList/mention_select_hide_icon.png"
/>
</div>
<span
style="flex-shrink: 0; display: block;"
class="text-[32rpx] font-regular text-[#191919]"
v-if="state.mentionIsMulSelect"
@click="changeMentionSelectMul(false)"
>
{{ $t('cancel') }}
</span>
</div>
<div
class="flex flex-row-center-center flex-col"
style="padding: 6rpx 0;"
>
<text>{{ $t('chat.mention.select') }}</text>
</div>
<div class="flex-row flex flex-row-center-end" style="width: 210rpx;">
<div
class="mention-edit-btn"
v-if="!state.mentionIsMulSelect"
@click="changeMentionSelectMul(true)"
>
<span class="text-[32rpx] font-regular text-[#191919]">
{{ $t('button.multiple.choice') }}
</span>
</div>
<div
class="mention-done-btn"
:class="
state?.selectedMembersNum > 0 ? 'mention-done-btn-can-do' : ''
"
v-if="state.mentionIsMulSelect"
@click="confirmMentionSelect"
>
<span class="text-[32rpx] font-regular text-[#191919]">
{{ $t('button.text.done') }}
</span>
<span
class="text-[32rpx] font-regular text-[#191919]"
v-if="state?.selectedMembersNum > 0"
>
{{ '(' + state?.selectedMembersNum + ')' }}
</span>
</div>
</div>
</div>
<selectMemberByAlphabet
:manageType="'mention'"
ref="selectMemberByAlphabetRef"
:selectAreaHeight="state.selectAreaHeight"
@updateSelectedMembersNum="updateSelectedMembersNum"
:isMulSelect="state.mentionIsMulSelect"
@getSelectResult="getSelectResult"
@getMentionSelectLists="getMentionSelectLists"
></selectMemberByAlphabet>
</tm-drawer>
</div>
</template>
<script setup>
import selectMemberByAlphabet from '../chatSettings/components/selectMemberByAlphabet.vue'
import {
ref,
reactive,
@ -370,7 +457,11 @@ import {
useDialogueListStore,
} from '@/store'
import addCircleGray from '@/static/image/chatList/addCircleGray.png'
import { MessageComponents, ForwardableMessageType } from '@/constant/message'
import {
MessageComponents,
ForwardableMessageType,
ChatMsgTypeMapping,
} from '@/constant/message'
import { formatTime, parseTime } from '@/utils/datetime'
import { deltaToMessage, deltaToString, isEmptyDelta } from './util'
import smile from '@/static/image/chatList/smile@2x.png'
@ -399,6 +490,8 @@ import { onLoad as uniOnload } from '@dcloudio/uni-app'
Quill.register('formats/emoji', EmojiBlot)
const selectMemberByAlphabetRef = ref(null)
const {
getDialogueList,
updateZpagingRef,
@ -433,6 +526,11 @@ const state = ref({
sessionId: '',
localPageLoadDone: true, //
quoteInfo: null, //
mentionIsMulSelect: false, //
selectedMembersNum: 0, //
mentionSelectHeight: 0, //
selectAreaHeight: 0, //
isShowMentionSelect: false, //
})
uniOnload((options) => {
@ -487,6 +585,15 @@ const onSendMessage = (data = {}, callBack) => {
const onSendMessageClick = () => {
let delta = getQuill().getContents()
if (state.value.quoteInfo) {
delta.ops.unshift({
insert: {
quote: {
id: state.value.quoteInfo.msg_id,
},
},
})
}
let data = deltaToMessage(delta)
if (data.items.length === 0) {
@ -503,6 +610,9 @@ const onSendMessageClick = () => {
callBack: (ok) => {
if (!ok) return
getQuill().setContents([], Quill.sources.USER)
if (state.value.quoteInfo) {
state.value.quoteInfo = null
}
},
})
break
@ -623,6 +733,14 @@ const onEditorChange = () => {
let text = deltaToString(delta)
if (
text.length > 0 &&
text.slice(-2).trim() === '@' &&
talkParams.type === 2
) {
state.value.isShowMentionSelect = true
}
if (!isEmptyDelta(delta)) {
editorDraftStore.items[indexName.value || ''] = JSON.stringify({
text: text,
@ -855,6 +973,11 @@ const handleDelete = () => {
})
}
//
const updateSelectedMembersNum = (numChange) => {
state.value.selectedMembersNum = state.value.selectedMembersNum + numChange
}
watch(
() => zpagingRef.value,
(newValue, oldValue) => {
@ -934,9 +1057,77 @@ const toUserDetailPage = (userItem) => {
})
}
//
const changeMentionSelectMul = (status) => {
state.value.mentionIsMulSelect = status
}
//
const hideMentionSelect = () => {
state.value.isShowMentionSelect = false
}
//
const confirmMentionSelect = () => {
if (state?.value.selectedMembersNum > 0) {
if (selectMemberByAlphabetRef.value) {
selectMemberByAlphabetRef.value.confirmSelectMembers()
}
hideMentionSelect()
}
}
//
const getSelectResult = (mentionSelect) => {
console.log(mentionSelect)
getMentionSelectLists(mentionSelect)
}
//
const getMentionSelectLists = (mentionSelectList) => {
console.log(mentionSelectList)
let mentionUserIds = []
let mentionUsers = getQuill().getContents().ops //
mentionUsers[0].insert =
mentionUsers[0].insert.slice(0, -2) + mentionUsers[0].insert.slice(-1)
console.log(mentionUsers[0].insert)
mentionSelectList.forEach((mentionSelectItem) => {
mentionUserIds.push(mentionSelectItem.erp_user_id)
mentionUserIds.push(mentionSelectItem.id)
mentionUsers.push({
insert: '@' + mentionSelectItem.nickname + ' ',
attributes: {
color: '#1890ff',
},
})
})
getQuill().setContents(mentionUsers)
hideMentionSelect()
}
onMounted(async () => {
initData()
nextTick(() => {
state.value.mentionSelectHeight = pxTorPx(
uni.getSystemInfoSync().windowHeight * 0.86,
)
state.value.selectAreaHeight =
rpxToPx(state.value.mentionSelectHeight) - rpxToPx(90) + 'px'
})
})
const pxTorPx = (px) => {
const sysInfo = uni.getSystemInfoSync()
const rpx = px / (sysInfo.screenWidth / 750)
return rpx
}
const rpxToPx = (rpx) => {
const sysInfo = uni.getSystemInfoSync()
const px = (sysInfo.screenWidth / 750) * rpx
return px
}
onUnmounted(() => {
dialogueStore.setDialogue({})
@ -1256,4 +1447,66 @@ onUnmounted(() => {
height: 1rpx;
background-color: #e7e7e7;
}
.mention-select-drawer {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 36rpx 32rpx 0;
.cancel-btns {
flex-shrink: 0;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
.hide-btn {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
position: relative;
text {
}
img {
width: 18rpx;
height: 10rpx;
}
}
}
.mention-done-btn {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding: 6rpx 24rpx;
background-color: #f3f3f3;
border-radius: 8rpx;
flex-shrink: 0;
span {
color: #bababa;
line-height: 40rpx;
flex-shrink: 0;
}
}
.mention-done-btn-can-do {
background-color: #46299d;
span {
color: #fff;
}
}
.mention-edit-btn {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-end;
flex-shrink: 0;
span {
flex-shrink: 0;
}
}
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 752 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 B

View File

@ -7,7 +7,7 @@ import { createGlobalState, useStorage } from '@vueuse/core'
import { uniStorage } from '@/utils/uniStorage.js'
export const useDialogueListStore = createGlobalState(() => {
const dialogueList = useStorage('dialogueList', [], uniStorage)
const dialogueList = ref([])
const zpagingRef = ref()
const virtualList = ref([])

View File

@ -150,5 +150,5 @@
"button.text.close": "关闭",
"choose.deps.all": "全部",
"choose.deps.current": "当前",
"msg.type": "图片"
"chat.mention.select": "选择提醒的人"
}