Compare commits
13 Commits
d516ce00ef
...
b58f25945e
Author | SHA1 | Date | |
---|---|---|---|
b58f25945e | |||
bdfd84bd35 | |||
a0578c57ad | |||
e9bd2bf6cd | |||
2c063f3d4f | |||
5c55411aa3 | |||
df07c953bf | |||
dafe65bb72 | |||
78ca543946 | |||
2e3b0b994b | |||
8704691821 | |||
4f57419c5d | |||
489fb71be5 |
4
env/.env.test
vendored
@ -6,8 +6,8 @@ VITE_SHOW_CONSOLE = true
|
||||
VITE_SHOW_SOURCEMAP = true
|
||||
# baseUrl
|
||||
# VITE_BASEURL = 'https://warehouse.szjixun.cn/oa_backend'
|
||||
VITE_BASEURL = 'http://172.16.100.93:9503'
|
||||
VITE_BASEURL = 'http://172.16.100.93:8503'
|
||||
#VITE_SOCKET_API
|
||||
VITE_SOCKET_API = 'ws://172.16.100.93:9504'
|
||||
VITE_SOCKET_API = 'ws://172.16.100.93:8504'
|
||||
# EPRAPI baseUrl
|
||||
VITE_EPR_BASEURL = 'http://114.218.158.24:9020'
|
||||
|
@ -135,6 +135,15 @@ export const ServeRemoveRecords = (data) => {
|
||||
})
|
||||
}
|
||||
|
||||
//清空聊天记录
|
||||
export const ServeEmptyMessage = (data) => {
|
||||
return request({
|
||||
url: '/api/v1/talk/message/empty',
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
// 收藏表情包服务接口
|
||||
export const ServeCollectEmoticon = (data) => {
|
||||
return request({
|
||||
|
@ -1,29 +1,94 @@
|
||||
<script setup>
|
||||
import TmImage from "@/uni_modules/tmui/components/tm-image/tm-image.vue";
|
||||
import {useAuth} from "@/store/auth";
|
||||
import { useClockIn } from "@/store/clockIn/index.js";
|
||||
const {userInfo}=useAuth()
|
||||
const {workingTimeInfoData,actionTypeData} = useClockIn()
|
||||
</script>
|
||||
<template>
|
||||
<div class="flex-shrink-0 pl-[16rpx] pr-[40rpx] flex items-center rounded-[8rpx] w-[686rpx] h-[154rpx] bg-white">
|
||||
<div class="rounded-full overflow-hidden w-[96rpx] h-[96rpx]">
|
||||
<tm-image preview :width="96" :height="96" :src="userInfo.Avatar"></tm-image>
|
||||
</div>
|
||||
<div class="ml-[20rpx]">
|
||||
<div class="flex items-center">
|
||||
<div class="text-[32rpx] text-black">{{ userInfo.NickName }}</div>
|
||||
<div class="mx-[14rpx] h-[30rpx] w-[1rpx] bg-[#F7F7F7]"></div>
|
||||
<div class="w-[40rpx] h-[40rpx]">
|
||||
<img v-if="actionTypeData.isWorkDay ===1" class="w-[40rpx] h-[40rpx]" src="@/static/image/clockIn/zu3275@3x.png" alt="">
|
||||
<img v-else class="w-[40rpx] h-[40rpx]" src="@/static/image/clockIn/rest3275@2x.png" alt="">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-[5rpx] flex">
|
||||
<div class="text-[24rpx] text-[#999999]">{{ workingTimeInfoData.WorkTimeTemplateName }}</div>
|
||||
<div class="text-[#46299D] text-[24rpx]">(考勤规则)</div>
|
||||
</div>
|
||||
</div>
|
||||
<slot name="right"></slot>
|
||||
<div class="avatar-module" :style="customStyle">
|
||||
<img :src="avatar" v-if="avatar" />
|
||||
<span v-else :style="customTextStyle">{{ text_avatar }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
//群聊默认头像
|
||||
import groupNormal from '@/static/image/chatList/groupNormal.png'
|
||||
import groupDepartment from '@/static/image/chatList/groupDepartment.png'
|
||||
import groupProject from '@/static/image/chatList/groupProject.png'
|
||||
import groupCompany from '@/static/image/chatList/groupCompany.png'
|
||||
import { computed, defineProps } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
mode: {
|
||||
//模式:1=人;2=群
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
avatar: {
|
||||
//头像
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
userName: {
|
||||
//用户名称
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
groupType: {
|
||||
//群类型:1=普通群;2=部门群;3=项目群;4=总群/公司群
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
customStyle: {
|
||||
//自定义样式
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
},
|
||||
},
|
||||
customTextStyle: {
|
||||
//自定义文字样式
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
//头像
|
||||
const avatar = computed(() => {
|
||||
let avatar_img = props?.avatar
|
||||
if (!avatar_img) {
|
||||
if (props?.mode === 1) {
|
||||
} else if (props?.mode === 2) {
|
||||
if (props?.groupType === 1) {
|
||||
avatar_img = groupNormal
|
||||
} else if (props?.groupType === 2) {
|
||||
avatar_img = groupDepartment
|
||||
} else if (props?.groupType === 3) {
|
||||
avatar_img = groupProject
|
||||
} else if (props?.groupType === 4) {
|
||||
avatar_img = groupCompany
|
||||
}
|
||||
}
|
||||
}
|
||||
return avatar_img
|
||||
})
|
||||
|
||||
//文字头像
|
||||
const text_avatar = computed(() => {
|
||||
return props?.userName.length >= 2
|
||||
? props?.userName.slice(-2)
|
||||
: props?.userName
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.avatar-module {
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: linear-gradient(to right, #674bbc, #46299d);
|
||||
flex-shrink: 0;
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div @click="onCheck" >
|
||||
<div @click.stop="onCheck" >
|
||||
<tm-image :width="size" :height="size" :src="imageSrc"></tm-image>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -9,7 +9,13 @@
|
||||
<wd-button custom-class="custom-sub-btn-class" v-if="props.subBtnText">
|
||||
{{ props.subBtnText }}
|
||||
</wd-button>
|
||||
<wd-button custom-class="custom-btn-class" @click="clickBtn">
|
||||
<wd-button
|
||||
custom-class="custom-btn-class"
|
||||
@click="clickBtn"
|
||||
:disabled="props?.disabled"
|
||||
:class="[props?.disabled ? 'custom-btn-class-disabled' : '']"
|
||||
:plain="props?.plain"
|
||||
>
|
||||
{{ props.btnText }}
|
||||
</wd-button>
|
||||
</div>
|
||||
@ -23,6 +29,8 @@ const props = defineProps({
|
||||
isBottom: false, //是否底部按钮
|
||||
btnText: '', //按钮文字
|
||||
subBtnText: '', //次要按钮文字
|
||||
disabled: false, //是否禁用
|
||||
plain: false, //是否镂空
|
||||
})
|
||||
|
||||
//点击
|
||||
@ -38,28 +46,33 @@ const clickBtn = () => {
|
||||
justify-content: center;
|
||||
.custom-sub-btn-class {
|
||||
background-color: #eee9f8;
|
||||
padding: 18rpx 185rpx;
|
||||
padding: 18rpx 0;
|
||||
border-radius: 8rpx;
|
||||
box-shadow: 0 6px 12px 2px rgba(188, 188, 188, 0.08);
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
line-height: 40rpx;
|
||||
color: $theme-primary;
|
||||
width: 426rpx;
|
||||
height: 76rpx;
|
||||
}
|
||||
.custom-btn-class {
|
||||
background-color: $theme-primary;
|
||||
padding: 18rpx 185rpx;
|
||||
padding: 18rpx 0;
|
||||
border-radius: 8rpx;
|
||||
box-shadow: 0 6px 12px 2px rgba(188, 188, 188, 0.08);
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
line-height: 40rpx;
|
||||
width: 426rpx;
|
||||
height: 76rpx;
|
||||
}
|
||||
}
|
||||
.custom-btn-bottom {
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
padding: 14rpx 0 72rpx;
|
||||
box-shadow: 0 1px 0 2px rgba(231, 231, 231, 1);
|
||||
}
|
||||
.apposition-btn-style {
|
||||
padding: 14rpx 30rpx 72rpx;
|
||||
@ -71,4 +84,8 @@ const clickBtn = () => {
|
||||
padding: 18rpx 124rpx;
|
||||
}
|
||||
}
|
||||
.custom-btn-class-disabled {
|
||||
background-color: #e6e6e6 !important;
|
||||
color: #bebebe !important;
|
||||
}
|
||||
</style>
|
||||
|
50
src/components/custom-navbar/index.vue
Normal file
@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<tm-navbar
|
||||
:hideBack="props.hideBack"
|
||||
hideHome
|
||||
:title="props.title"
|
||||
:shadow="props.shadowNum"
|
||||
:fontSize="34"
|
||||
>
|
||||
<template #left>
|
||||
<slot name="left"></slot>
|
||||
</template>
|
||||
<template #subTitle>
|
||||
<slot name="subTitle"></slot>
|
||||
</template>
|
||||
<template #right>
|
||||
<slot name="right"></slot>
|
||||
</template>
|
||||
</tm-navbar>
|
||||
</template>
|
||||
<script setup>
|
||||
import tmNavbar from '@/uni_modules/tmui/components/tm-navbar/tm-navbar.vue'
|
||||
import { defineProps } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
title: {
|
||||
//页面标题
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
hideBack: {
|
||||
//是否隐藏返回按钮
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
shadowNum: {
|
||||
//投影
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
::v-deep .text-view {
|
||||
text {
|
||||
font-weight: 500;
|
||||
line-height: 48rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,79 +1,114 @@
|
||||
<script setup>
|
||||
import {ref,nextTick} from 'vue'
|
||||
import WdPopup from "@/uni_modules/wot-design-uni/components/wd-popup/wd-popup.vue";
|
||||
import TmButton from "@/uni_modules/tmui/components/tm-button/tm-button.vue";
|
||||
import {language} from "@/uni_modules/tmui/tool/lib/language"
|
||||
const confirmState=ref(false)
|
||||
const cancel=ref(true)
|
||||
let onConfirm=null
|
||||
let onCancel=null
|
||||
const confirm=ref(true)
|
||||
const contentText=ref('')
|
||||
const imageRef=ref('')
|
||||
const confirmLabel=ref('确定')
|
||||
const cancelLabel=ref('取消')
|
||||
const confirmC=ref('#46299D')
|
||||
const cancelC=ref('#1A1A1A')
|
||||
const sendCancel=()=>{
|
||||
confirmState.value=false
|
||||
if (typeof onCancel==='function'){
|
||||
import { ref, nextTick } from 'vue'
|
||||
import WdPopup from '@/uni_modules/wot-design-uni/components/wd-popup/wd-popup.vue'
|
||||
import TmButton from '@/uni_modules/tmui/components/tm-button/tm-button.vue'
|
||||
import { language } from '@/uni_modules/tmui/tool/lib/language'
|
||||
const confirmState = ref(false)
|
||||
const cancel = ref(true)
|
||||
let onConfirm = null
|
||||
let onCancel = null
|
||||
const confirm = ref(true)
|
||||
const contentText = ref('')
|
||||
const subContentText = ref('')
|
||||
const subContentC = ref('#000')
|
||||
const imageRef = ref('')
|
||||
const confirmLabel = ref('确定')
|
||||
const cancelLabel = ref('取消')
|
||||
const confirmC = ref('#46299D')
|
||||
const cancelC = ref('#1A1A1A')
|
||||
const sendCancel = () => {
|
||||
confirmState.value = false
|
||||
if (typeof onCancel === 'function') {
|
||||
onCancel()
|
||||
}
|
||||
}
|
||||
const sendConfirm=()=>{
|
||||
confirmState.value=false
|
||||
if (typeof onConfirm==='function'){
|
||||
const sendConfirm = () => {
|
||||
confirmState.value = false
|
||||
if (typeof onConfirm === 'function') {
|
||||
onConfirm()
|
||||
}
|
||||
}
|
||||
const showConfirm=({content,image,onConfirm:confirm,onCancel:cancel,confirmText,cancelText,confirmColor,cancelColor})=>{
|
||||
confirmState.value=true
|
||||
contentText.value=content
|
||||
imageRef.value = image?image:''
|
||||
onConfirm=confirm
|
||||
onCancel=cancel
|
||||
const showConfirm = ({
|
||||
content,
|
||||
image,
|
||||
onConfirm: confirm,
|
||||
onCancel: cancel,
|
||||
confirmText,
|
||||
cancelText,
|
||||
confirmColor,
|
||||
cancelColor,
|
||||
subContent,
|
||||
subContentColor,
|
||||
}) => {
|
||||
confirmState.value = true
|
||||
contentText.value = content
|
||||
imageRef.value = image ? image : ''
|
||||
onConfirm = confirm
|
||||
onCancel = cancel
|
||||
confirmLabel.value = confirmText || confirmLabel.value
|
||||
cancelLabel.value = cancelText || cancelLabel.value
|
||||
confirmC.value = confirmColor || confirmC.value
|
||||
cancelC.value = cancelColor || cancelC.value
|
||||
subContentText.value = subContent || subContentText.value
|
||||
subContentC.value = subContentColor || subContentC.value
|
||||
}
|
||||
defineExpose({
|
||||
showConfirm
|
||||
showConfirm,
|
||||
})
|
||||
</script>
|
||||
<template>
|
||||
<wd-popup custom-style="border-radius: 16rpx;" modal-style="background-color: #000000;opacity: 0.6;" v-model="confirmState">
|
||||
<wd-popup
|
||||
custom-style="border-radius: 16rpx;"
|
||||
modal-style="background-color: #000000;opacity: 0.6;"
|
||||
v-model="confirmState"
|
||||
>
|
||||
<div class="flex flex-col w-[640rpx]">
|
||||
<div v-if="imageRef===''" class="flex justify-center items-center h-[288rpx] text-[32rpx] font-bold text-[#1A1A1A]">
|
||||
{{contentText}}
|
||||
<div
|
||||
v-if="imageRef === ''"
|
||||
class="flex flex-col justify-center items-center h-[288rpx] text-[#1A1A1A] popup-content"
|
||||
>
|
||||
<span class="text-[32rpx] font-bold">{{ contentText }}</span>
|
||||
<span
|
||||
class="text-[28rpx] font-regular"
|
||||
v-if="subContentText"
|
||||
:style="{ color: subContentC }"
|
||||
>
|
||||
{{ subContentText }}
|
||||
</span>
|
||||
</div>
|
||||
<div v-else class="flex flex-col items-center h-[456rpx] text-[32rpx] font-bold text-[#1A1A1A]" >
|
||||
<div class="wrap1 mt-[32rpx] mb-[44rpx]" >
|
||||
<img :src="imageRef" alt="">
|
||||
<div
|
||||
v-else
|
||||
class="flex flex-col items-center h-[456rpx] text-[32rpx] font-bold text-[#1A1A1A]"
|
||||
>
|
||||
<div class="wrap1 mt-[32rpx] mb-[44rpx]">
|
||||
<img :src="imageRef" alt="" />
|
||||
</div>
|
||||
<div class="mb-[56rpx]" > {{contentText}} </div>
|
||||
<div class="mb-[56rpx]">{{ contentText }}</div>
|
||||
</div>
|
||||
<div class="flex flex-grow border-t-solid border-[#E7E7E7] border-1rpx text-[32rpx]">
|
||||
<div
|
||||
class="flex flex-grow border-t-solid border-[#E7E7E7] border-1rpx text-[32rpx]"
|
||||
>
|
||||
<div class="flex justify-center items-center text-[#1A1A1A]">
|
||||
<tm-button
|
||||
@click="sendCancel"
|
||||
:width="319"
|
||||
@touchstart="cancel=false"
|
||||
@touchend="cancel=true"
|
||||
@touchstart="cancel = false"
|
||||
@touchend="cancel = true"
|
||||
:fontSize="32"
|
||||
:height="112"
|
||||
:margin="[0]"
|
||||
:font-color="cancelC"
|
||||
:transprent="cancel"
|
||||
text
|
||||
:label="cancelLabel"></tm-button>
|
||||
:label="cancelLabel"
|
||||
></tm-button>
|
||||
</div>
|
||||
<div class="h-[112rpx] w-[1rpx] bg-[#E7E7E7]"></div>
|
||||
<div class="flex justify-center items-center text-[#CF3050]">
|
||||
<tm-button
|
||||
@click="sendConfirm"
|
||||
@touchstart="confirm=false"
|
||||
@touchend="confirm=true"
|
||||
@touchstart="confirm = false"
|
||||
@touchend="confirm = true"
|
||||
:width="319"
|
||||
:fontSize="32"
|
||||
:transprent="confirm"
|
||||
@ -81,17 +116,23 @@ defineExpose({
|
||||
:margin="[0]"
|
||||
:font-color="confirmC"
|
||||
text
|
||||
:label="confirmLabel"></tm-button>
|
||||
:label="confirmLabel"
|
||||
></tm-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</wd-popup>
|
||||
</template>
|
||||
<style scoped lang="scss">
|
||||
.popup-content {
|
||||
span {
|
||||
line-height: 44rpx;
|
||||
}
|
||||
}
|
||||
.wrap1 {
|
||||
img {
|
||||
width: 381.59rpx;
|
||||
height: 280.14rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,11 +0,0 @@
|
||||
<script setup>
|
||||
import tmNavbar from '@/uni_modules/tmui/components/tm-navbar/tm-navbar.vue';
|
||||
import {useStatus} from "@/store/status"
|
||||
const {currentNavbar} = useStatus()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<tm-navbar :hideBack="false" hideHome :title="currentNavbar.title"/>
|
||||
</template>
|
||||
<style scoped lang="scss">
|
||||
</style>
|
@ -12,7 +12,7 @@ import { reactive, nextTick, computed, h, inject } from 'vue'
|
||||
// IdCard
|
||||
// } from '@icon-park/vue-next'
|
||||
import { ServeTopTalkList, ServeDeleteTalkList, ServeSetNotDisturb } from '@/api/chat'
|
||||
import { useDialogueStore, useTalkStore } from '@/store'
|
||||
import { useDialogueStore, useTalkStore, useDialogueListStore } from '@/store'
|
||||
import { ServeSecedeGroup } from '@/api/group'
|
||||
// import { ServeDeleteContact, ServeEditContactRemark } from '@/api/contact'
|
||||
// import { NInput } from 'naive-ui'
|
||||
@ -28,6 +28,7 @@ export function useSessionMenu() {
|
||||
|
||||
const dialogueStore = useDialogueStore()
|
||||
const talkStore = useTalkStore()
|
||||
const dialogueListStore = useDialogueListStore()
|
||||
|
||||
const user = inject('$user')
|
||||
|
||||
@ -105,6 +106,7 @@ export function useSessionMenu() {
|
||||
|
||||
const onDeleteTalk = (index_name = '') => {
|
||||
talkStore.delItem(index_name)
|
||||
dialogueListStore.delDialogueStorage(index_name)
|
||||
|
||||
index_name === indexName.value && dialogueStore.$reset()
|
||||
}
|
||||
@ -264,5 +266,5 @@ export function useSessionMenu() {
|
||||
evnets[key] && evnets[key](dropdown.item)
|
||||
}
|
||||
|
||||
return { dropdown, onCloseContextMenu, onContextMenuTalkHandle, onToTopTalk }
|
||||
return { dropdown, onCloseContextMenu, onContextMenuTalkHandle, onToTopTalk, onRemoveTalk }
|
||||
}
|
||||
|
47
src/main.js
@ -1,52 +1,55 @@
|
||||
import customNavbar from '@/components/custom-navbar/index'
|
||||
|
||||
import { createSSRApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import dayjs from "dayjs";
|
||||
import dayjs from 'dayjs'
|
||||
import 'virtual:uno.css'
|
||||
import VConsole from "vconsole";
|
||||
import VConsole from 'vconsole'
|
||||
import '@/utils/uni.webview.js'
|
||||
import tmui from "@/uni_modules/tmui"
|
||||
import {config} from "@/config/tmui/index.js";
|
||||
import 'dayjs/locale/zh-cn';
|
||||
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 { vLoading } from "@/components/x-loading/index.js"
|
||||
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'
|
||||
const {showMessage}=messagePopup()
|
||||
const { showMessage } = messagePopup()
|
||||
dayjs.locale('zh-cn')
|
||||
if (import.meta.env.VITE_SHOW_CONSOLE){
|
||||
if (import.meta.env.VITE_SHOW_CONSOLE) {
|
||||
new VConsole()
|
||||
}
|
||||
export function createApp() {
|
||||
const app = createSSRApp(App)
|
||||
plugins.setPinia(app)
|
||||
plugins.setComponents(app)
|
||||
app.use(tmui,{...config})
|
||||
app.directive("loading", vLoading)
|
||||
app.use(tmui, { ...config })
|
||||
app.directive('loading', vLoading)
|
||||
app.mixin(pageAnimation)
|
||||
app.component('x-loaderror',xLoaderror)
|
||||
app.component('customNavbar', customNavbar)
|
||||
app.component('x-loaderror', xLoaderror)
|
||||
app.directive('no-space', {
|
||||
mounted(el) {
|
||||
el.addEventListener('input', (e) => {
|
||||
const originalValue = e.target.value;
|
||||
const newValue = originalValue.replace(/\s/g, '');
|
||||
const originalValue = e.target.value
|
||||
const newValue = originalValue.replace(/\s/g, '')
|
||||
if (originalValue !== newValue) {
|
||||
e.target.value = newValue;
|
||||
e.target.dispatchEvent(new Event('input'));
|
||||
}
|
||||
});
|
||||
e.target.value = newValue
|
||||
e.target.dispatchEvent(new Event('input'))
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
window.message = ['success', 'error', 'warning'].reduce((acc, type) => {
|
||||
acc[type] = (message) => {
|
||||
if (typeof message === 'string') {
|
||||
showMessage({ type, message });
|
||||
showMessage({ type, message })
|
||||
} else if (typeof message === 'object') {
|
||||
showMessage({ type, ...message });
|
||||
showMessage({ type, ...message })
|
||||
}
|
||||
};
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
return acc
|
||||
}, {})
|
||||
return {
|
||||
app,
|
||||
}
|
||||
|
@ -38,22 +38,6 @@
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/chooseDeps/index",
|
||||
"type": "page",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/chooseMembers/index",
|
||||
"type": "page",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"enablePullDownRefresh":false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/chooseGroupAdmin/index",
|
||||
"type": "page",
|
||||
@ -186,6 +170,14 @@
|
||||
"navigationStyle": "custom",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/chooseByDeps/index",
|
||||
"type": "page",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
|
@ -19,12 +19,12 @@
|
||||
"
|
||||
>
|
||||
<div class="group-member-avatar">
|
||||
<img v-if="memberItem.avatar" :src="memberItem.avatar" />
|
||||
<span v-if="!memberItem.avatar" class="text-[24rpx] font-bold">
|
||||
<img v-if="memberItem?.avatar" :src="memberItem?.avatar" />
|
||||
<span v-if="!memberItem?.avatar" class="text-[24rpx] font-bold">
|
||||
{{
|
||||
memberItem.nickname.length >= 2
|
||||
? memberItem.nickname.slice(-2)
|
||||
: memberItem.nickname
|
||||
(memberItem?.nickname || memberItem?.nickName)?.length >= 2
|
||||
? (memberItem?.nickname || memberItem?.nickName).slice(-2)
|
||||
: memberItem?.nickname || memberItem?.nickName
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
@ -38,12 +38,19 @@
|
||||
</div>
|
||||
<div class="group-member-name">
|
||||
<span class="text-[24rpx] font-regular">
|
||||
{{ memberItem.nickname }}
|
||||
{{ memberItem.nickname || memberItem.nickName }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="group-member-list-each">
|
||||
<div
|
||||
class="group-member-list-each"
|
||||
@click="groupAddMembers"
|
||||
v-if="
|
||||
(props?.groupType == 1 || props?.groupType == 3) &&
|
||||
!props?.hideAddRemoveBtns
|
||||
"
|
||||
>
|
||||
<div class="group-member-each">
|
||||
<div class="group-member-avatar" :style="{ background: 'unset' }">
|
||||
<img src="/src/static/image/chatSettings/add-member.png" />
|
||||
@ -53,7 +60,15 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="group-member-list-each" v-if="props?.is_manager">
|
||||
<div
|
||||
class="group-member-list-each"
|
||||
@click="groupRemoveMembers"
|
||||
v-if="
|
||||
props?.is_manager &&
|
||||
(props?.groupType == 1 || props?.groupType == 3) &&
|
||||
!props?.hideAddRemoveBtns
|
||||
"
|
||||
>
|
||||
<div class="group-member-each">
|
||||
<div class="group-member-avatar" :style="{ background: 'unset' }">
|
||||
<img src="/src/static/image/chatSettings/remove-member.png" />
|
||||
@ -71,14 +86,32 @@ const props = defineProps({
|
||||
memberList: Array, //人员列表
|
||||
memberListsLimit: Number, //人员列表数量限制
|
||||
is_manager: Boolean, //是否管理员
|
||||
groupType: Number, //群类型
|
||||
hideAddRemoveBtns: Boolean, //是否隐藏添加移除按钮
|
||||
})
|
||||
|
||||
//点击跳转到用户详情页面
|
||||
const toUserDetailPage = (userItem) => {
|
||||
console.log(userItem.erp_user_id)
|
||||
console.log(userItem)
|
||||
uni.navigateTo({
|
||||
url:
|
||||
'/pages/dialog/dialogDetail/userDetail?erpUserId=' + userItem.erp_user_id,
|
||||
'/pages/dialog/dialogDetail/userDetail?erpUserId=' +
|
||||
(userItem.erp_user_id || userItem.ID),
|
||||
})
|
||||
}
|
||||
|
||||
//点击群聊拉人
|
||||
const groupAddMembers = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/chooseByDeps/index?chooseMode=2',
|
||||
})
|
||||
}
|
||||
|
||||
//点击群聊移除人
|
||||
const groupRemoveMembers = () => {
|
||||
uni.navigateTo({
|
||||
url:
|
||||
'/pages/chatSettings/groupManage/selectMembers?manageType=removeMembers',
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
@ -7,6 +7,10 @@
|
||||
? 'select-member-item-card'
|
||||
: 'select-member-item-list'
|
||||
"
|
||||
:style="{
|
||||
padding:
|
||||
props?.manageType === 'removeMembers' ? '18rpx 62rpx 18rpx 34rpx' : '',
|
||||
}"
|
||||
>
|
||||
<div class="member-info">
|
||||
<slot name="left"></slot>
|
||||
@ -39,12 +43,21 @@
|
||||
{{ $t('silence.tag.hasDone') }}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="is-mine-tag"
|
||||
v-if="
|
||||
props?.memberItem?.is_mine && props?.manageType === 'removeMembers'
|
||||
"
|
||||
>
|
||||
<img src="/src/static/image/chatSettings/is-mine.png" />
|
||||
</div>
|
||||
<div
|
||||
class="is-admin-tag"
|
||||
v-if="
|
||||
(props?.memberItem?.leader === 1 ||
|
||||
props?.memberItem?.leader === 2) &&
|
||||
props?.manageType === 'admin'
|
||||
(props?.manageType === 'admin' ||
|
||||
props?.manageType === 'removeMembers')
|
||||
"
|
||||
>
|
||||
<span class="text-[28rpx] font-regular">
|
||||
@ -135,16 +148,31 @@ const clickItem = () => {
|
||||
}
|
||||
}
|
||||
.select-member-tags {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
.done-silence-tag {
|
||||
span {
|
||||
color: #b4b4b4;
|
||||
line-height: 40rpx;
|
||||
}
|
||||
}
|
||||
.is-mine-tag {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
img {
|
||||
width: 52rpx;
|
||||
height: 52rpx;
|
||||
}
|
||||
}
|
||||
.is-admin-tag {
|
||||
padding: 6rpx 12rpx;
|
||||
border: 2rpx solid #b4b4b4;
|
||||
border-radius: 8rpx;
|
||||
margin: 0 0 0 20rpx;
|
||||
span {
|
||||
color: #b4b4b4;
|
||||
line-height: 40rpx;
|
||||
|
@ -3,51 +3,114 @@
|
||||
<div class="root">
|
||||
<ZPaging ref="zPaging" :show-scrollbar="false">
|
||||
<template #top>
|
||||
<tm-navbar :hideBack="false" hideHome title="" :leftWidth="220">
|
||||
<div class="navBar-title flex flex-col items-center justify-center">
|
||||
<span class="text-[34rpx] font-medium">
|
||||
{{ $t('chat.settings.editAvatar') }}
|
||||
</span>
|
||||
</div>
|
||||
</tm-navbar>
|
||||
<customNavbar :title="$t('chat.settings.editAvatar')"></customNavbar>
|
||||
</template>
|
||||
<div class="edit-group-info">
|
||||
<div class="group-avatar">
|
||||
<img :src="state.groupAvatar" />
|
||||
<avatarModule
|
||||
:mode="2"
|
||||
:avatar="groupParams?.groupInfo?.avatar"
|
||||
:groupType="groupParams?.groupInfo?.group_type"
|
||||
:customStyle="{
|
||||
width: '460rpx',
|
||||
height: '460rpx',
|
||||
borderRadius: '0',
|
||||
}"
|
||||
></avatarModule>
|
||||
</div>
|
||||
</div>
|
||||
<customBtn :btnText="$t('button.text.edit')"></customBtn>
|
||||
<customBtn
|
||||
:btnText="$t('button.text.edit')"
|
||||
@click="editAvatar"
|
||||
></customBtn>
|
||||
</ZPaging>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import avatarModule from '@/components/avatar-module/index.vue'
|
||||
import customBtn from '@/components/custom-btn/custom-btn.vue'
|
||||
import { ServeEditGroup } from '@/api/group/index.js'
|
||||
import ZPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { reactive } from 'vue'
|
||||
import { reactive, computed, watch } from 'vue'
|
||||
import { useGroupStore, useDialogueStore, useDialogueListStore } from '@/store'
|
||||
|
||||
const state = reactive({
|
||||
pageTitle: '', //页面标题
|
||||
groupAvatar: '', //群头像
|
||||
groupName: '', //群名称
|
||||
import { uploadImg } from '@/api/chat'
|
||||
import { uniqueId } from '@/utils'
|
||||
|
||||
const groupStore = useGroupStore()
|
||||
const groupParams = reactive({
|
||||
groupInfo: computed(() => groupStore.groupInfo),
|
||||
})
|
||||
|
||||
const dialogueStore = useDialogueStore()
|
||||
const dialogueParams = reactive({
|
||||
receiver_id: computed(() => dialogueStore.talk.receiver_id),
|
||||
})
|
||||
|
||||
const onProgressFn = (progress, id) => {
|
||||
console.log((progress.loaded / progress.total) * 100, 'progress')
|
||||
|
||||
useDialogueListStore().updateUploadProgress(
|
||||
id,
|
||||
(progress.loaded / progress.total) * 100,
|
||||
)
|
||||
}
|
||||
|
||||
onLoad((options) => {
|
||||
console.log(options)
|
||||
if (options.groupAvatar) {
|
||||
state.groupAvatar = options.groupAvatar
|
||||
}
|
||||
})
|
||||
|
||||
//点击清空群名称输入
|
||||
const clearGroupNameInput = () => {
|
||||
state.groupName = ''
|
||||
}
|
||||
|
||||
//点击确认修改
|
||||
const confirmEdit = () => {
|
||||
console.log(state.groupName)
|
||||
//点击修改
|
||||
const editAvatar = () => {
|
||||
uni.chooseImage({
|
||||
sourceType: ['album'],
|
||||
count: 1,
|
||||
success: async (res) => {
|
||||
console.log(res, 'res')
|
||||
res.tempFiles.forEach(async (file) => {
|
||||
console.log(file)
|
||||
let image = new Image()
|
||||
image.src = URL.createObjectURL(file)
|
||||
image.onload = () => {
|
||||
const form = new FormData()
|
||||
form.append('file', file)
|
||||
form.append('source', 'fonchain-chat')
|
||||
form.append('urlParam', `width=${image.width}&height=${image.height}`)
|
||||
let randomId = uniqueId()
|
||||
uploadImg(form, (e) => onProgressFn(e, randomId)).then(
|
||||
({ status, data, msg }) => {
|
||||
console.log(status, data, msg)
|
||||
if (status == 0) {
|
||||
let avatar = data.ori_url
|
||||
let params = {
|
||||
group_id: dialogueParams.receiver_id,
|
||||
group_name: groupParams.groupInfo.group_name,
|
||||
avatar: avatar,
|
||||
}
|
||||
console.log(params)
|
||||
const resp = ServeEditGroup(params)
|
||||
resp.then(({ code }) => {
|
||||
if (code == 200) {
|
||||
groupStore.updateGroupInfo({
|
||||
avatar: data.ori_url,
|
||||
})
|
||||
// uni.navigateBack({
|
||||
// delta: 1,
|
||||
// })
|
||||
} else {
|
||||
}
|
||||
})
|
||||
resp.catch(() => {})
|
||||
} else {
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
@ -56,7 +119,7 @@ const confirmEdit = () => {
|
||||
background-image: url('@/static/image/mine/background.png');
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position:center bottom;
|
||||
background-position: center bottom;
|
||||
}
|
||||
|
||||
.edit-group-info {
|
||||
@ -70,10 +133,6 @@ const confirmEdit = () => {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
img {
|
||||
width: 460rpx;
|
||||
height: 460rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -3,17 +3,21 @@
|
||||
<div class="root">
|
||||
<ZPaging ref="zPaging" :show-scrollbar="false">
|
||||
<template #top>
|
||||
<tm-navbar :hideBack="false" hideHome title="" :leftWidth="220">
|
||||
<div class="navBar-title flex flex-col items-center justify-center">
|
||||
<span class="text-[34rpx] font-medium">
|
||||
{{ $t('chat.settings.editGroupName') }}
|
||||
</span>
|
||||
</div>
|
||||
</tm-navbar>
|
||||
<customNavbar
|
||||
:title="$t('chat.settings.editGroupName')"
|
||||
></customNavbar>
|
||||
</template>
|
||||
<div class="edit-group-info">
|
||||
<div class="group-avatar">
|
||||
<img :src="state.groupAvatar" />
|
||||
<avatarModule
|
||||
:mode="2"
|
||||
:avatar="groupParams?.groupInfo?.avatar"
|
||||
:groupType="groupParams?.groupInfo?.group_type"
|
||||
:customStyle="{
|
||||
width: '192rpx',
|
||||
height: '192rpx',
|
||||
}"
|
||||
></avatarModule>
|
||||
</div>
|
||||
<div class="group-name">
|
||||
<span class="text-[28rpx] font-medium">
|
||||
@ -46,6 +50,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import avatarModule from '@/components/avatar-module/index.vue'
|
||||
import customBtn from '@/components/custom-btn/custom-btn.vue'
|
||||
import { ServeEditGroup } from '@/api/group/index.js'
|
||||
import { useGroupStore, useDialogueStore } from '@/store'
|
||||
@ -65,15 +70,11 @@ const dialogueParams = reactive({
|
||||
|
||||
const state = reactive({
|
||||
pageTitle: '', //页面标题
|
||||
groupAvatar: '', //群头像
|
||||
groupName: '', //群名称
|
||||
})
|
||||
|
||||
onLoad((options) => {
|
||||
console.log(options)
|
||||
if (options.groupAvatar) {
|
||||
state.groupAvatar = options.groupAvatar
|
||||
}
|
||||
state.groupName = groupParams.groupInfo.group_name
|
||||
})
|
||||
|
||||
@ -91,6 +92,7 @@ const confirmEdit = () => {
|
||||
let params = {
|
||||
group_id: dialogueParams.receiver_id,
|
||||
group_name: state.groupName,
|
||||
avatar: groupParams.groupInfo.avatar,
|
||||
}
|
||||
console.log(params)
|
||||
const resp = ServeEditGroup(params)
|
||||
@ -125,15 +127,10 @@ const confirmEdit = () => {
|
||||
.edit-group-info {
|
||||
.group-avatar {
|
||||
padding: 250rpx 0 88rpx;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
img {
|
||||
width: 192rpx;
|
||||
height: 192rpx;
|
||||
}
|
||||
}
|
||||
.group-name {
|
||||
padding: 0 32rpx;
|
||||
|
@ -3,13 +3,7 @@
|
||||
<div class="root">
|
||||
<ZPaging ref="zPaging" :show-scrollbar="false">
|
||||
<template #top>
|
||||
<tm-navbar :hideBack="false" hideHome title="" :leftWidth="220">
|
||||
<div class="navBar-title flex flex-col items-center justify-center">
|
||||
<span class="text-[34rpx] font-medium">
|
||||
{{ $t('chat.settings.groupAdmin') }}
|
||||
</span>
|
||||
</div>
|
||||
</tm-navbar>
|
||||
<customNavbar :title="$t('chat.settings.groupAdmin')"></customNavbar>
|
||||
</template>
|
||||
<div class="manage-group-admin">
|
||||
<span class="manage-group-admin-title text-[28rpx] font-regular">
|
||||
@ -23,10 +17,32 @@
|
||||
>
|
||||
<div
|
||||
class="group-admin-list-each"
|
||||
:style="{
|
||||
padding:
|
||||
groupParams?.groupInfo?.group_type == 1 ||
|
||||
groupParams?.groupInfo?.group_type == 3
|
||||
? '18rpx 26rpx 18rpx 14rpx'
|
||||
: '',
|
||||
}"
|
||||
v-for="(item, index) in state?.groupAdminList"
|
||||
:key="index"
|
||||
>
|
||||
<span>{{ item.deptPos }}</span>
|
||||
<selectMemberItem
|
||||
v-if="
|
||||
groupParams?.groupInfo?.group_type == 1 ||
|
||||
groupParams?.groupInfo?.group_type == 3
|
||||
"
|
||||
:groupType="groupParams.groupInfo.group_type"
|
||||
:memberItem="item"
|
||||
></selectMemberItem>
|
||||
<span
|
||||
v-if="
|
||||
groupParams?.groupInfo?.group_type == 2 ||
|
||||
groupParams?.groupInfo?.group_type == 4
|
||||
"
|
||||
>
|
||||
{{ item.deptPos }}
|
||||
</span>
|
||||
<div class="group-admin-list-each-btns">
|
||||
<img
|
||||
v-if="item.is_mine"
|
||||
@ -62,11 +78,15 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import selectMemberItem from '../components/select-member-item.vue'
|
||||
import ZPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { computed, onMounted, reactive, watch } from 'vue'
|
||||
|
||||
import { ServeEditGroupAdmin } from '@/api/group/index.js'
|
||||
import {
|
||||
ServeEditGroupAdmin,
|
||||
ServeGroupAssignAdmin,
|
||||
} from '@/api/group/index.js'
|
||||
import { useGroupStore, useDialogueStore } from '@/store'
|
||||
|
||||
import { useAuth } from '@/store/auth'
|
||||
@ -90,7 +110,7 @@ const state = reactive({
|
||||
})
|
||||
|
||||
watch(
|
||||
() => groupParams?.groupInfo,
|
||||
[() => groupParams?.groupInfo, , () => dialogueParams?.adminList],
|
||||
(newGroupInfo) => {
|
||||
getGroupAdminList()
|
||||
},
|
||||
@ -107,13 +127,33 @@ onMounted(async () => {
|
||||
|
||||
//加载群聊管理员列表
|
||||
const getGroupAdminList = () => {
|
||||
if (
|
||||
groupParams?.groupInfo?.group_type == 1 ||
|
||||
groupParams?.groupInfo?.group_type == 3
|
||||
) {
|
||||
if (dialogueParams?.adminList?.length > 0) {
|
||||
dialogueParams?.adminList.forEach((item) => {
|
||||
if (item?.erp_user_id == userInfo?.value?.ID) {
|
||||
item.is_mine = true
|
||||
item.cannotRemove = true
|
||||
}
|
||||
})
|
||||
}
|
||||
state.groupAdminList = dialogueParams?.adminList
|
||||
} else if (
|
||||
groupParams?.groupInfo?.group_type == 2 ||
|
||||
groupParams?.groupInfo?.group_type == 4
|
||||
) {
|
||||
let myPositionsList = []
|
||||
if (groupParams?.groupInfo?.groupAdminList?.length > 0) {
|
||||
groupParams?.groupInfo?.groupAdminList.forEach((groupAdminItem) => {
|
||||
if (userInfo?.value?.PositionUsers?.length > 0) {
|
||||
userInfo?.value?.PositionUsers.forEach((item) => {
|
||||
// console.log(item.DepartmentId + '-' + item.PositionID)
|
||||
if (item.PositionID === groupAdminItem.position_id) {
|
||||
if (
|
||||
item.PositionID === groupAdminItem.position_id &&
|
||||
item.DepartmentId === groupAdminItem.dept_id
|
||||
) {
|
||||
myPositionsList.push(groupAdminItem)
|
||||
}
|
||||
})
|
||||
@ -136,9 +176,10 @@ const getGroupAdminList = () => {
|
||||
})
|
||||
}
|
||||
state.groupAdminList = groupParams?.groupInfo?.groupAdminList
|
||||
}
|
||||
}
|
||||
|
||||
//点击跳转到添加禁言成员页面
|
||||
//点击跳转到添加管理员页面
|
||||
const toSelectMembersPage = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/chatSettings/groupManage/selectMembers?manageType=admin',
|
||||
@ -147,18 +188,36 @@ const toSelectMembersPage = () => {
|
||||
|
||||
//点击移除群管理员
|
||||
const removeGroupAdmin = (adminItem) => {
|
||||
if (
|
||||
groupParams?.groupInfo?.group_type == 1 ||
|
||||
groupParams?.groupInfo?.group_type == 3
|
||||
) {
|
||||
let params = {
|
||||
mode: 2, //1管理员,2不是管理员
|
||||
group_id: dialogueParams.receiverId, //群id
|
||||
user_ids: String(adminItem.id),
|
||||
}
|
||||
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 positionInfos = []
|
||||
if (state?.groupAdminList?.length > 0) {
|
||||
state?.groupAdminList.forEach((item) => {
|
||||
if (
|
||||
positionInfos = state?.groupAdminList.filter((item) => {
|
||||
return (
|
||||
item.dept_id != adminItem.dept_id ||
|
||||
item.position_id != adminItem.position_id
|
||||
) {
|
||||
positionInfos.push({
|
||||
position_id: item.position_id,
|
||||
position_name: item.deptPos,
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
let params = {
|
||||
source: 'app',
|
||||
@ -177,6 +236,7 @@ const removeGroupAdmin = (adminItem) => {
|
||||
})
|
||||
resp.catch(() => {})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
@ -196,18 +256,23 @@ const removeGroupAdmin = (adminItem) => {
|
||||
|
||||
.group-admin-list {
|
||||
.group-admin-list-each {
|
||||
padding: 36rpx 0;
|
||||
margin: 0 32rpx;
|
||||
padding: 36rpx 26rpx 36rpx 14rpx;
|
||||
margin: 0 18rpx;
|
||||
border-bottom: 1px solid $theme-border-color;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
::v-deep .select-member-item {
|
||||
padding: 0;
|
||||
border-bottom: 0;
|
||||
}
|
||||
.group-admin-list-each-btns {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
flex-shrink: 0;
|
||||
img {
|
||||
width: 52rpx;
|
||||
height: 52rpx;
|
||||
|
@ -11,16 +11,12 @@
|
||||
:loading-more-enabled="false"
|
||||
>
|
||||
<template #top>
|
||||
<tm-navbar :hideBack="false" hideHome title="" :leftWidth="220">
|
||||
<div class="navBar-title flex flex-col items-center justify-center">
|
||||
<span class="text-[34rpx] font-medium">
|
||||
{{ $t('chat.settings.groupMember') }}
|
||||
</span>
|
||||
</div>
|
||||
</tm-navbar>
|
||||
<customNavbar :title="$t('chat.settings.groupMember')"></customNavbar>
|
||||
</template>
|
||||
<div class="group-members-list">
|
||||
<groupMemberList
|
||||
:groupType="groupParams?.groupInfo?.group_type"
|
||||
:is_manager="groupParams?.groupInfo?.is_manager"
|
||||
:memberList="talkParams?.memberList"
|
||||
></groupMemberList>
|
||||
</div>
|
||||
@ -33,13 +29,18 @@ import groupMemberList from '../components/groupMembersList.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 { ref, computed, reactive } from 'vue'
|
||||
import { useDialogueStore } from '@/store'
|
||||
import { useDialogueStore, useGroupStore } from '@/store'
|
||||
|
||||
const dialogueStore = useDialogueStore()
|
||||
const talkParams = reactive({
|
||||
memberList: computed(() => dialogueStore.members),
|
||||
})
|
||||
|
||||
const groupStore = useGroupStore()
|
||||
const groupParams = reactive({
|
||||
groupInfo: computed(() => groupStore.groupInfo),
|
||||
})
|
||||
|
||||
const zPaging = ref()
|
||||
useZPaging(zPaging)
|
||||
</script>
|
||||
|
@ -3,13 +3,7 @@
|
||||
<div class="root">
|
||||
<ZPaging ref="zPaging" :show-scrollbar="false">
|
||||
<template #top>
|
||||
<tm-navbar :hideBack="false" hideHome title="" :leftWidth="220">
|
||||
<div class="navBar-title flex flex-col items-center justify-center">
|
||||
<span class="text-[34rpx] font-medium">
|
||||
{{ $t('chat.settings.groupGag') }}
|
||||
</span>
|
||||
</div>
|
||||
</tm-navbar>
|
||||
<customNavbar :title="$t('chat.settings.groupGag')"></customNavbar>
|
||||
</template>
|
||||
<div class="manage-group-silence">
|
||||
<span class="manage-group-silence-title text-[28rpx] font-regular">
|
||||
|
@ -3,11 +3,9 @@
|
||||
<div class="root">
|
||||
<ZPaging ref="zPaging" :show-scrollbar="false">
|
||||
<template #top>
|
||||
<tm-navbar
|
||||
<customNavbar
|
||||
:hideBack="state.editMode === 3 ? false : true"
|
||||
hideHome
|
||||
title=""
|
||||
:leftWidth="220"
|
||||
:title="$t('chat.settings.groupNotice')"
|
||||
>
|
||||
<template #left v-if="state.editMode !== 3">
|
||||
<div class="nav-bar-cancel-btn">
|
||||
@ -16,11 +14,6 @@
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<div class="navBar-title flex flex-col items-center justify-center">
|
||||
<span class="text-[34rpx] font-medium">
|
||||
{{ $t('chat.settings.groupNotice') }}
|
||||
</span>
|
||||
</div>
|
||||
<template #right>
|
||||
<div
|
||||
v-if="state.editMode !== 3"
|
||||
@ -42,7 +35,7 @@
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</tm-navbar>
|
||||
</customNavbar>
|
||||
</template>
|
||||
<div class="notice-text-area">
|
||||
<div class="notice-view-area">
|
||||
|
@ -11,37 +11,13 @@
|
||||
@scroll="onScroll"
|
||||
>
|
||||
<template #top>
|
||||
<tm-navbar
|
||||
:hideBack="false"
|
||||
hideHome
|
||||
title=""
|
||||
:leftWidth="220"
|
||||
id="topArea"
|
||||
>
|
||||
<div class="navBar-title flex flex-col items-center justify-center">
|
||||
<span
|
||||
class="text-[34rpx] font-medium"
|
||||
v-if="state.manageType === 'silence'"
|
||||
>
|
||||
{{ $t('chat.manage.addSilenceMember') }}
|
||||
</span>
|
||||
<span
|
||||
class="text-[34rpx] font-medium"
|
||||
v-if="state.manageType === 'admin'"
|
||||
>
|
||||
{{ $t('chat.manage.addAdmin') }}
|
||||
</span>
|
||||
<span
|
||||
class="text-[34rpx] font-medium"
|
||||
v-if="state.manageType === 'searchRecord'"
|
||||
>
|
||||
{{ $t('search.condition.member') }}
|
||||
</span>
|
||||
</div>
|
||||
</tm-navbar>
|
||||
<customNavbar :title="pageTitle" id="topArea"></customNavbar>
|
||||
</template>
|
||||
<div class="select-members">
|
||||
<div class="search-member">
|
||||
<div
|
||||
class="search-member"
|
||||
v-if="state.manageType !== 'removeMembers'"
|
||||
>
|
||||
<customInput
|
||||
:searchText="state.searchText"
|
||||
@inputSearchText="inputSearchText"
|
||||
@ -83,7 +59,10 @@
|
||||
class="member-list-alphabet-key"
|
||||
:style="{
|
||||
padding:
|
||||
state.manageType === 'searchRecord' ? '10rpx 30rpx' : '',
|
||||
state.manageType === 'searchRecord' ||
|
||||
state.manageType === 'removeMembers'
|
||||
? '10rpx 30rpx'
|
||||
: '',
|
||||
}"
|
||||
v-if="alphabetItem?.memberList?.length > 0"
|
||||
:id="alphabetItem.key"
|
||||
@ -110,14 +89,36 @@
|
||||
@clickItem="handleClickItem(item)"
|
||||
:manageType="state.manageType"
|
||||
:itemStyle="
|
||||
state.manageType === 'searchRecord' ? 'list' : 'card'
|
||||
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'
|
||||
@ -138,6 +139,7 @@
|
||||
(state.manageType === 'admin' &&
|
||||
(item.leader === 1 || item.leader === 2))
|
||||
"
|
||||
@change="checkBoxChange"
|
||||
></tm-checkbox>
|
||||
</template>
|
||||
</selectMemberItem>
|
||||
@ -149,10 +151,31 @@
|
||||
</div>
|
||||
<template #bottom v-if="state.manageType !== 'searchRecord'">
|
||||
<customBtn
|
||||
v-if="state.manageType !== 'removeMembers'"
|
||||
:isBottom="true"
|
||||
:btnText="$t('ok')"
|
||||
@clickBtn="confirmSilenceMember"
|
||||
@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>
|
||||
</div>
|
||||
@ -166,8 +189,15 @@ 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 { onLoad } from '@dcloudio/uni-app'
|
||||
import { ServeGroupNoSpeak, ServeEditGroupAdmin } from '@/api/group/index.js'
|
||||
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 zPaging = ref()
|
||||
useZPaging(zPaging)
|
||||
@ -203,6 +233,8 @@ const state = reactive({
|
||||
currentAlphabet: 'A', //当前A-Z位置
|
||||
scrollDirection: '', //当前列表滚动方向
|
||||
isAssign: false, //是否指定view
|
||||
selectedMembersNum: 0, //当前选中数量
|
||||
isCreateDepGroup: 0, //是否是创建部门群
|
||||
})
|
||||
|
||||
watch(
|
||||
@ -232,6 +264,10 @@ onLoad((options) => {
|
||||
state.manageType = options.manageType
|
||||
assembleAlphabetMemberList(dialogueParams?.memberList)
|
||||
}
|
||||
if (options.isCreateDepGroup) {
|
||||
state.isCreateDepGroup = Number(options.isCreateDepGroup)
|
||||
assembleAlphabetMemberList()
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
@ -263,6 +299,21 @@ onMounted(() => {
|
||||
})
|
||||
})
|
||||
|
||||
//页面标题
|
||||
const pageTitle = computed(() => {
|
||||
let page_title = ''
|
||||
if (state.manageType === 'silence') {
|
||||
page_title = t('chat.manage.addSilenceMember')
|
||||
} else if (state.manageType === 'admin') {
|
||||
page_title = t('chat.manage.addAdmin')
|
||||
} else if (state.manageType === 'searchRecord') {
|
||||
page_title = t('search.condition.member')
|
||||
} else if (state.manageType === 'removeMembers') {
|
||||
page_title = t('select.member.remove')
|
||||
}
|
||||
return page_title
|
||||
})
|
||||
|
||||
// 观察者函数
|
||||
const handleIntersection = (entries) => {
|
||||
if (state.isAssign) {
|
||||
@ -303,7 +354,9 @@ const inputSearchText = (e) => {
|
||||
const handleClickItem = (item) => {
|
||||
if (
|
||||
(state.manageType === 'silence' && item.is_mute === 1) ||
|
||||
(state.manageType === 'admin' && (item.leader === 1 || item.leader === 2))
|
||||
(state.manageType === 'admin' &&
|
||||
(item.leader === 1 || item.leader === 2)) ||
|
||||
(state.manageType === 'removeMembers' && item.is_mine)
|
||||
) {
|
||||
return
|
||||
}
|
||||
@ -311,26 +364,33 @@ const handleClickItem = (item) => {
|
||||
if (
|
||||
state.manageType === 'admin' &&
|
||||
(groupParams.groupInfo.group_type == 2 ||
|
||||
groupParams.groupInfo.group_type == 4)
|
||||
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 confirmSilenceMember = () => {
|
||||
//点击确认选择的成员
|
||||
const confirmSelectMembers = () => {
|
||||
let selectedUserIds = ''
|
||||
let itemList = dialogueParams.memberList
|
||||
let positionInfos = []
|
||||
if (
|
||||
state.manageType === 'admin' &&
|
||||
(groupParams.groupInfo.group_type == 2 ||
|
||||
groupParams.groupInfo.group_type == 4)
|
||||
groupParams.groupInfo.group_type == 4 ||
|
||||
state.isCreateDepGroup === 1)
|
||||
) {
|
||||
itemList = state.resultMemberList[0].memberList
|
||||
}
|
||||
@ -346,8 +406,16 @@ const confirmSilenceMember = () => {
|
||||
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) {
|
||||
@ -368,7 +436,33 @@ const confirmSilenceMember = () => {
|
||||
})
|
||||
resp.catch(() => {})
|
||||
} else if (state.manageType === 'admin') {
|
||||
if (state.isCreateDepGroup === 1) {
|
||||
// console.log(positionInfos)
|
||||
groupTypeStore.groupAdmins.value = positionInfos
|
||||
uni.navigateBack({
|
||||
delta: 1,
|
||||
})
|
||||
} else {
|
||||
if (
|
||||
groupParams.groupInfo.group_type == 1 ||
|
||||
groupParams.groupInfo.group_type == 3
|
||||
) {
|
||||
let params = {
|
||||
mode: 1, //1管理员,2不是管理员
|
||||
group_id: dialogueParams.receiverId, //群id
|
||||
user_ids: selectedUserIds,
|
||||
}
|
||||
console.log(params)
|
||||
const resp = ServeGroupAssignAdmin(params)
|
||||
resp.then(({ code, data }) => {
|
||||
console.log(data)
|
||||
if (code == 200) {
|
||||
useDialogueStore().updateGroupMembers()
|
||||
} else {
|
||||
}
|
||||
})
|
||||
resp.catch(() => {})
|
||||
} else if (
|
||||
groupParams.groupInfo.group_type == 2 ||
|
||||
groupParams.groupInfo.group_type == 4
|
||||
) {
|
||||
@ -390,12 +484,32 @@ const confirmSilenceMember = () => {
|
||||
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') {
|
||||
if (
|
||||
state.manageType === 'searchRecord' ||
|
||||
state.manageType === 'removeMembers'
|
||||
) {
|
||||
const resultMemberList = ref([])
|
||||
const alphabet = Array.from({ length: 26 }, (_, i) =>
|
||||
String.fromCharCode(i + 65),
|
||||
@ -425,6 +539,27 @@ const assembleAlphabetMemberList = async (newMemberList) => {
|
||||
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,
|
||||
})
|
||||
@ -437,6 +572,8 @@ const assembleAlphabetMemberList = async (newMemberList) => {
|
||||
id: item.ID + '-' + positionItem.ID,
|
||||
checkArr: [],
|
||||
positionInfo: {
|
||||
dept_id: item.ID,
|
||||
dept_name: item.name,
|
||||
position_id: positionItem.ID,
|
||||
position_name: positionItem.name,
|
||||
},
|
||||
@ -453,22 +590,32 @@ const assembleAlphabetMemberList = async (newMemberList) => {
|
||||
})
|
||||
})
|
||||
}
|
||||
// console.log(departmentAllPositions)
|
||||
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,
|
||||
},
|
||||
]
|
||||
} else {
|
||||
state.resultMemberList = [
|
||||
{
|
||||
key: '',
|
||||
memberList: newMemberList,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//滚动到指定的view
|
||||
@ -494,6 +641,15 @@ const onScroll = (e) => {
|
||||
state.scrollDirection = ''
|
||||
}
|
||||
}
|
||||
|
||||
//选中的人员改变
|
||||
const checkBoxChange = (e) => {
|
||||
if (e) {
|
||||
state.selectedMembersNum += 1
|
||||
} else {
|
||||
state.selectedMembersNum -= 1
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.outer-layer {
|
||||
@ -544,4 +700,28 @@ const onScroll = (e) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.confirm-btn-area {
|
||||
background-color: #fff;
|
||||
padding: 14rpx 32rpx 72rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
.confirm-btn-area-statistic-text {
|
||||
margin: 18rpx 0 0;
|
||||
span {
|
||||
line-height: 40rpx;
|
||||
color: $theme-text;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .custom-btn-class {
|
||||
padding: 18rpx 104rpx !important;
|
||||
}
|
||||
::v-deep .is-disabled {
|
||||
background-color: #e6e6e6 !important;
|
||||
color: #bebebe !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -3,18 +3,20 @@
|
||||
<div class="root">
|
||||
<ZPaging ref="zPaging" :show-scrollbar="false">
|
||||
<template #top>
|
||||
<tm-navbar :hideBack="false" hideHome title="" :leftWidth="220">
|
||||
<div class="navBar-title flex flex-col items-center justify-center">
|
||||
<span class="text-[34rpx] font-medium">
|
||||
{{ $t('index.chat.settings') }}
|
||||
</span>
|
||||
</div>
|
||||
</tm-navbar>
|
||||
<customNavbar :title="$t('index.chat.settings')"></customNavbar>
|
||||
</template>
|
||||
<div class="chat-settings">
|
||||
<div class="chat-group-base-infos chat-settings-card">
|
||||
<div
|
||||
class="chat-group-base-infos chat-settings-card"
|
||||
v-if="dialogueParams.type === 2"
|
||||
>
|
||||
<div class="base-info-avatar" @click="toEditAvatarPage">
|
||||
<img :src="groupAvatar" />
|
||||
<avatarModule
|
||||
:mode="2"
|
||||
:avatar="groupParams?.groupInfo?.avatar"
|
||||
:groupType="groupParams?.groupInfo?.group_type"
|
||||
:customStyle="{ width: '96rpx', height: '96rpx' }"
|
||||
></avatarModule>
|
||||
</div>
|
||||
<div class="base-info">
|
||||
<div class="base-info-name">
|
||||
@ -24,6 +26,7 @@
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="groupParams?.groupInfo?.group_type !== 1"
|
||||
class="base-info-tag"
|
||||
:style="{
|
||||
borderColor:
|
||||
@ -62,9 +65,17 @@
|
||||
@toManagePage="toManagePage"
|
||||
></settingFormItem>
|
||||
<groupMemberList
|
||||
:groupType="groupParams?.groupInfo?.group_type"
|
||||
:is_manager="groupParams?.groupInfo?.is_manager"
|
||||
:memberList="dialogueParams?.memberList"
|
||||
:memberListsLimit="groupParams?.groupInfo?.is_manager ? 13 : 14"
|
||||
:memberListsLimit="
|
||||
groupParams?.groupInfo?.group_type == 1 ||
|
||||
groupParams?.groupInfo?.group_type == 3
|
||||
? groupParams?.groupInfo?.is_manager
|
||||
? 13
|
||||
: 14
|
||||
: 15
|
||||
"
|
||||
></groupMemberList>
|
||||
</div>
|
||||
</div>
|
||||
@ -125,22 +136,31 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="clear-chat-record-btn chat-settings-card">
|
||||
<div class="clear-chat-record-btn-each">
|
||||
<div
|
||||
@click="showConfirmPrompt(1)"
|
||||
class="clear-chat-record-btn-each"
|
||||
>
|
||||
<span class="text-[32rpx] font-regular">
|
||||
{{ $t('chat.settings.clearChatRecord') }}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
@click="showConfirmPrompt(2)"
|
||||
class="clear-chat-record-btn-each"
|
||||
v-if="groupParams?.groupInfo?.is_manager"
|
||||
v-if="
|
||||
groupParams?.groupInfo?.is_manager && dialogueParams.type === 2
|
||||
"
|
||||
>
|
||||
<span class="text-[32rpx] font-regular">
|
||||
{{ $t('group.disband.btn') }}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
@click="showConfirmPrompt(3)"
|
||||
class="clear-chat-record-btn-each"
|
||||
v-if="groupParams?.groupInfo?.is_manager"
|
||||
v-if="
|
||||
groupParams?.groupInfo?.is_manager && dialogueParams.type === 2
|
||||
"
|
||||
>
|
||||
<span class="text-[32rpx] font-regular">
|
||||
{{ $t('group.quit.btn') }}
|
||||
@ -153,10 +173,8 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import zu4992 from '@/static/image/chatList/zu4992@2x.png'
|
||||
import zu4991 from '@/static/image/chatList/zu4991@2x.png'
|
||||
import zu4989 from '@/static/image/chatList/zu4989@2x.png'
|
||||
import zu5296 from '@/static/image/chatList/zu5296@2x.png'
|
||||
import avatarModule from '@/components/avatar-module/index.vue'
|
||||
import useConfirm from '@/components/x-confirm/useConfirm.js'
|
||||
import recordSearchTypeIcon_groupMember from '@/static/image/chatSettings/recordSearchTypeGroupMembers.png'
|
||||
import recordSearchTypeIcon_date from '@/static/image/chatSettings/recordSearchTypeDate.png'
|
||||
import recordSearchTypeIcon_imgAndVideo from '@/static/image/chatSettings/recordSearchTypeImgAndVideo.png'
|
||||
@ -171,12 +189,19 @@ import {
|
||||
useTalkStore,
|
||||
useDialogueStore,
|
||||
useGroupStore,
|
||||
useGroupTypeStore,
|
||||
} from '@/store'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import {
|
||||
ServeInviteGroup,
|
||||
ServeDismissGroup,
|
||||
ServeSecedeGroup,
|
||||
} from '@/api/group/index'
|
||||
import { ServeTopTalkList, ServeSetNotDisturb } from '@/api/chat/index'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
const { t } = useI18n()
|
||||
import customInput from '@/components/custom-input/custom-input.vue'
|
||||
const { showConfirm } = useConfirm()
|
||||
const userStore = useUserStore()
|
||||
const talkStore = useTalkStore()
|
||||
const dialogueStore = useDialogueStore()
|
||||
@ -218,6 +243,14 @@ watch(
|
||||
{ deep: true },
|
||||
)
|
||||
|
||||
watch(
|
||||
() => useGroupTypeStore()?.allChooseMembers?.value,
|
||||
(newMembers) => {
|
||||
// console.log(newMembers)
|
||||
inviteMembersInGroup(newMembers)
|
||||
},
|
||||
)
|
||||
|
||||
onLoad(async (options) => {
|
||||
console.log(dialogueParams)
|
||||
if (options.groupId) {
|
||||
@ -247,10 +280,6 @@ onLoad(async (options) => {
|
||||
|
||||
onMounted(() => {
|
||||
state.recordSearchTypeList = [
|
||||
{
|
||||
value: t('chat.settings.groupMember'),
|
||||
typeIcon: recordSearchTypeIcon_groupMember,
|
||||
},
|
||||
{
|
||||
value: t('record.searchType.date'),
|
||||
typeIcon: recordSearchTypeIcon_date,
|
||||
@ -268,6 +297,12 @@ onMounted(() => {
|
||||
typeIcon: recordSearchTypeIcon_link,
|
||||
},
|
||||
]
|
||||
if (dialogueParams.type === 2) {
|
||||
state.recordSearchTypeList.unshift({
|
||||
value: t('chat.settings.groupMember'),
|
||||
typeIcon: recordSearchTypeIcon_groupMember,
|
||||
})
|
||||
}
|
||||
state.chatSettings = [
|
||||
{
|
||||
label: t('chat.settings.topSession'),
|
||||
@ -286,14 +321,6 @@ onMounted(() => {
|
||||
]
|
||||
})
|
||||
|
||||
//群头像
|
||||
const groupAvatar = computed(() => {
|
||||
return (
|
||||
groupParams?.groupInfo?.avatar ||
|
||||
groupTypeMapping[groupParams?.groupInfo?.group_type]?.defaultImg
|
||||
)
|
||||
})
|
||||
|
||||
//群名称
|
||||
const groupName = computed(() => {
|
||||
return groupParams?.groupInfo?.group_name
|
||||
@ -306,26 +333,21 @@ const groupNum = computed(() => {
|
||||
|
||||
// 映射表-根据groupType设置对应值
|
||||
const groupTypeMapping = {
|
||||
0: {
|
||||
defaultImg: 'textImg',
|
||||
},
|
||||
0: {},
|
||||
1: {
|
||||
defaultImg: zu4992,
|
||||
result_type: t('index.mine.normal'),
|
||||
},
|
||||
2: {
|
||||
result_type: t('index.mine.department'),
|
||||
result_type_color: '#377EC6',
|
||||
defaultImg: zu4989,
|
||||
},
|
||||
3: {
|
||||
result_type: t('index.mine.project'),
|
||||
result_type_color: '#C1681C',
|
||||
defaultImg: zu4991,
|
||||
},
|
||||
4: {
|
||||
result_type: t('index.type.company'),
|
||||
result_type_color: '#7A58DE',
|
||||
defaultImg: zu5296,
|
||||
},
|
||||
}
|
||||
|
||||
@ -399,9 +421,7 @@ const updateGroupInfos = () => {
|
||||
//点击跳转到修改群信息页面
|
||||
const toEditGroupInfoPage = () => {
|
||||
uni.navigateTo({
|
||||
url:
|
||||
'/pages/chatSettings/groupManage/editGroupName?groupAvatar=' +
|
||||
groupAvatar.value,
|
||||
url: '/pages/chatSettings/groupManage/editGroupName',
|
||||
})
|
||||
}
|
||||
|
||||
@ -411,9 +431,7 @@ const toEditAvatarPage = () => {
|
||||
return
|
||||
}
|
||||
uni.navigateTo({
|
||||
url:
|
||||
'/pages/chatSettings/groupManage/editAvatar?groupAvatar=' +
|
||||
groupAvatar.value,
|
||||
url: '/pages/chatSettings/groupManage/editAvatar',
|
||||
})
|
||||
}
|
||||
|
||||
@ -426,9 +444,7 @@ const toManagePage = (label) => {
|
||||
return
|
||||
}
|
||||
uni.navigateTo({
|
||||
url:
|
||||
'/pages/chatSettings/groupManage/editGroupName?groupAvatar=' +
|
||||
groupAvatar.value,
|
||||
url: '/pages/chatSettings/groupManage/editGroupName',
|
||||
})
|
||||
} else if (label === t('chat.settings.groupNotice')) {
|
||||
uni.navigateTo({
|
||||
@ -455,19 +471,23 @@ const toManagePage = (label) => {
|
||||
//点击跳转到按条件搜索页
|
||||
const toSearchByConditionPage = (flag) => {
|
||||
let condition = ''
|
||||
if (flag == 0) {
|
||||
let flagIndex = 0
|
||||
if (dialogueParams.type === 1) {
|
||||
flagIndex = -1
|
||||
}
|
||||
if (flag == flagIndex) {
|
||||
uni.navigateTo({
|
||||
url:
|
||||
'/pages/chatSettings/groupManage/selectMembers?manageType=searchRecord',
|
||||
})
|
||||
} else {
|
||||
if (flag == 1) {
|
||||
if (flag == flagIndex + 1) {
|
||||
condition = 'date'
|
||||
} else if (flag == 2) {
|
||||
} else if (flag == flagIndex + 2) {
|
||||
condition = 'imgAndVideo'
|
||||
} else if (flag == 3) {
|
||||
} else if (flag == flagIndex + 3) {
|
||||
condition = 'file'
|
||||
} else if (flag == 4) {
|
||||
} else if (flag == flagIndex + 4) {
|
||||
condition = 'link'
|
||||
}
|
||||
uni.navigateTo({
|
||||
@ -519,6 +539,89 @@ const changeSwitch = (switchStatus, label) => {
|
||||
|
||||
resp.catch(() => {})
|
||||
}
|
||||
|
||||
//点击显示确认提示悬窗
|
||||
const showConfirmPrompt = (flag) => {
|
||||
let confirmContent = ''
|
||||
let subContent = ''
|
||||
let subContentColor = ''
|
||||
if (flag === 1) {
|
||||
//清空聊天记录
|
||||
confirmContent = t('ok') + t('chat.settings.clearChatRecord')
|
||||
} else if (flag === 2) {
|
||||
//解散群聊
|
||||
confirmContent = t('ok') + t('group.quit.btn')
|
||||
subContent = t('groupManage.disband.hint')
|
||||
subContentColor = '#CF3050'
|
||||
} else if (flag === 3) {
|
||||
//退出群聊
|
||||
confirmContent = t('ok') + t('group.quit.btn')
|
||||
subContent = t('groupManage.quit.hint')
|
||||
subContentColor = '#B4B4B4'
|
||||
}
|
||||
showConfirm({
|
||||
subContent: subContent,
|
||||
subContentColor: subContentColor,
|
||||
confirmColor: '#CF3050',
|
||||
content: confirmContent,
|
||||
contentText: t('ok'),
|
||||
confirmText: t('ok'),
|
||||
cancelText: t('cancel'),
|
||||
onConfirm: async () => {
|
||||
if (flag === 1) {
|
||||
dialogueStore.apiClearRecord()
|
||||
} else if (flag === 2) {
|
||||
let params = {
|
||||
group_id: dialogueParams.receiver_id, //群id
|
||||
}
|
||||
console.log(params)
|
||||
const res = await ServeDismissGroup(params)
|
||||
if (res.code === 200) {
|
||||
dialogueStore.updateGroupMembers()
|
||||
groupStore.ServeGroupDetail()
|
||||
uni.navigateBack({
|
||||
delta: 2,
|
||||
})
|
||||
}
|
||||
} else if (flag === 3) {
|
||||
ServeSecedeGroup({
|
||||
group_id: dialogueParams.receiver_id,
|
||||
}).then(({ code, message }) => {
|
||||
if (code == 200) {
|
||||
// dialogueStore.apiClearRecord()
|
||||
} else {
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
onCancel: () => {},
|
||||
})
|
||||
}
|
||||
|
||||
//普通群/项目群拉人
|
||||
const inviteMembersInGroup = async (memberList) => {
|
||||
if (memberList?.length > 0) {
|
||||
let erp_ids = ''
|
||||
memberList.forEach((item) => {
|
||||
if (!erp_ids) {
|
||||
erp_ids = item.ID
|
||||
} else {
|
||||
erp_ids += ',' + item.ID
|
||||
}
|
||||
})
|
||||
let params = {
|
||||
group_id: dialogueParams.receiver_id, //群id
|
||||
erp_ids: String(erp_ids), //erp的用户id
|
||||
}
|
||||
console.log(params)
|
||||
const res = await ServeInviteGroup(params)
|
||||
if (res.code === 200) {
|
||||
dialogueStore.updateGroupMembers()
|
||||
groupStore.ServeGroupDetail()
|
||||
useGroupTypeStore()?.resetGroupInfo()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.outer-layer {
|
||||
@ -555,16 +658,7 @@ const changeSwitch = (switchStatus, label) => {
|
||||
padding: 32rpx 40rpx 32rpx 20rpx;
|
||||
|
||||
.base-info-avatar {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
flex-shrink: 0;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
.base-info {
|
||||
width: 100%;
|
||||
|
1091
src/pages/chooseByDeps/index.vue
Normal file
@ -1,36 +1,58 @@
|
||||
<template>
|
||||
<div>
|
||||
<div @click="cellClick" :class="['chatItem', props.data.is_top === 1 ? 'isTop' : '']">
|
||||
<div
|
||||
@click="cellClick"
|
||||
:class="['chatItem', props.data.is_top === 1 ? 'isTop' : '']"
|
||||
>
|
||||
<div>
|
||||
<tm-checkbox v-if="props.isMultiSelect" :round="10" :modelValue="props.data.isCheck"
|
||||
@update:modelValue="props.data.isCheck = !props.data.isCheck" :size="42" :color="props.data.isCheck ? '#46299D' : '#BABABA'"></tm-checkbox>
|
||||
<tm-checkbox
|
||||
v-if="props.isMultiSelect"
|
||||
:round="10"
|
||||
:modelValue="props.data.isCheck"
|
||||
@update:modelValue="props.data.isCheck = !props.data.isCheck"
|
||||
:size="32"
|
||||
:color="props.data.isCheck ? '#46299D' : '#BABABA'"
|
||||
class="pr-[4rpx]"
|
||||
></tm-checkbox>
|
||||
</div>
|
||||
<div class="avatarImg">
|
||||
<tm-image :width="96" :height="96" :round="12" :src="props.data.avatar"></tm-image>
|
||||
<avatarModule
|
||||
:mode="2"
|
||||
:avatar="props?.data?.avatar"
|
||||
:groupType="props?.data?.group_type"
|
||||
:customStyle="{ width: '96rpx', height: '96rpx' }"
|
||||
></avatarModule>
|
||||
</div>
|
||||
<div class="chatInfo">
|
||||
<div class="chatInfo_1">
|
||||
<div class="flex items-center">
|
||||
<div class="text-[#000000] text-[32rpx] font-bold opacity-90">{{ props.data.name }}</div>
|
||||
<div
|
||||
class="text-[#171717] text-[32rpx] font-medium leading-[44rpx]"
|
||||
>
|
||||
<span>{{ props.data.name }}</span>
|
||||
<span>({{ props.data.group_member_num }})</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="props.index !== talkStore.talkItems.length - 1" class="divider"></div>
|
||||
</div>
|
||||
<div
|
||||
v-if="props.index !== talkStore.talkItems.length - 1"
|
||||
class="divider"
|
||||
></div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, reactive, defineProps, defineEmits } from "vue"
|
||||
import dayjs from "dayjs";
|
||||
import avatarModule from '@/components/avatar-module/index.vue'
|
||||
import { ref, reactive, defineProps, defineEmits } 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
|
||||
} = useSessionMenu()
|
||||
const { onToTopTalk } = useSessionMenu()
|
||||
const dialogueStore = useDialogueStore()
|
||||
const emit = defineEmits(['itemClick'])
|
||||
const props = defineProps({
|
||||
@ -48,25 +70,22 @@ const props = defineProps({
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
const cellClick = () => {
|
||||
emit('itemClick', props.data)
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.chatItem {
|
||||
width: 100%;
|
||||
height: 154rpx;
|
||||
padding: 30rpx 16rpx;
|
||||
padding: 24rpx 18rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&.isTop {
|
||||
background-color: #F3F3F3;
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,7 +96,7 @@ const cellClick = () => {
|
||||
|
||||
.chatInfo {
|
||||
flex: 1;
|
||||
margin-left: 20rpx;
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
|
||||
.chatInfo_1 {
|
||||
@ -97,17 +116,16 @@ const cellClick = () => {
|
||||
font-size: 28rpx;
|
||||
color: #000000;
|
||||
opacity: 40%;
|
||||
|
||||
}
|
||||
|
||||
.companyTag {
|
||||
width: 76rpx;
|
||||
height: 38rpx;
|
||||
border: 1px solid #7A58DE;
|
||||
border: 1px solid #7a58de;
|
||||
font-size: 24rpx;
|
||||
text-align: center;
|
||||
border-radius: 6rpx;
|
||||
color: #7A58DE;
|
||||
color: #7a58de;
|
||||
font-weight: bold;
|
||||
margin-left: 12rpx;
|
||||
}
|
||||
|
@ -1,74 +1,123 @@
|
||||
<template>
|
||||
<div class="outer-layer">
|
||||
<div>
|
||||
<tm-navbar :hideBack="true" hideHome :leftWidth="320">
|
||||
<template v-slot:left>
|
||||
<div @click="handleClose" class="ml-[48rpx] text-[34rpx] leading-[40rpx]">
|
||||
关闭
|
||||
<div class="choose-chat-page">
|
||||
<ZPaging ref="zPaging" :show-scrollbar="false">
|
||||
<template #top>
|
||||
<customNavbar
|
||||
:title="$t('pageTitle.select.shareChat')"
|
||||
:hideBack="true"
|
||||
>
|
||||
<template #left>
|
||||
<div
|
||||
@click="handleClose"
|
||||
class="ml-[42rpx] text-[34rpx] leading-[48rpx] font-regular"
|
||||
>
|
||||
{{ $t('button.text.close') }}
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot:default>
|
||||
<div class="text-[34rpx] font-bold ml-[-80rpx] leading-[40rpx]">
|
||||
选择一个聊天
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot:right>
|
||||
<div v-if="!isMultiSelect" @click="isMultiSelect = true" class="mr-[36rpx] text-[34rpx] leading-[40rpx]">
|
||||
多选
|
||||
<template #right>
|
||||
<div
|
||||
v-if="!isMultiSelect"
|
||||
@click="isMultiSelect = true"
|
||||
class="mr-[36rpx] text-[34rpx] leading-[48rpx] font-regular"
|
||||
>
|
||||
{{ $t('button.multiple.choice') }}
|
||||
</div>
|
||||
<div v-else class="mr-[36rpx] btnBox">
|
||||
<tm-button @click="handleFinish" color="#5B3FB1" :fontSize="34" :disabled="!selectItems.length"
|
||||
:margin="[10]" :shadow="0" size="small" :width="selectItems.length ? 162 : 116"
|
||||
:label="selectItems.length ? `完成(${selectItems.length})` : '完成'">
|
||||
</tm-button>
|
||||
<customBtn
|
||||
:btnText="
|
||||
selectItems.length
|
||||
? $t('button.text.done') + `(${selectItems.length})`
|
||||
: $t('button.text.done')
|
||||
"
|
||||
:disabled="!selectItems.length"
|
||||
@clickBtn="handleFinish"
|
||||
></customBtn>
|
||||
</div>
|
||||
</template>
|
||||
</tm-navbar>
|
||||
</div>
|
||||
<div class="root">
|
||||
</customNavbar>
|
||||
</template>
|
||||
<div class="choose-chat">
|
||||
<div class="searchRoot">
|
||||
<tm-input placeholder="请输入…" color="#F9F9FD" :round="1" prefix="tmicon-search" prefixColor="#46299D"></tm-input>
|
||||
<customInput></customInput>
|
||||
</div>
|
||||
<div class="contentRoot">
|
||||
<chatItem v-for="(item, index) in items" :key="item.index_name" :index="index" :data="item"
|
||||
:isMultiSelect="isMultiSelect" @itemClick="itemClick" />
|
||||
<chatItem
|
||||
v-for="(item, index) in items"
|
||||
:key="item.index_name"
|
||||
:index="index"
|
||||
:data="item"
|
||||
:isMultiSelect="isMultiSelect"
|
||||
@itemClick="itemClick"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<tm-modal v-model:show="showWin" :height="forwardModalHeight" :round="4" okColor="#FFFFFF" @ok="handleOk"
|
||||
@cancel="handleCancel">
|
||||
<tm-modal
|
||||
v-model:show="showWin"
|
||||
:height="forwardModalHeight"
|
||||
:round="4"
|
||||
okColor="#FFFFFF"
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<template v-slot:title>
|
||||
<div class="w-full pl-[30rpx] leading-[48rpx] mt-[30rpx] font-bold text-[32rpx]">
|
||||
<div
|
||||
class="w-full pl-[30rpx] leading-[48rpx] mt-[30rpx] font-bold text-[32rpx]"
|
||||
>
|
||||
发送给:
|
||||
</div>
|
||||
</template>
|
||||
<template v-slot:default>
|
||||
<div class="pl-[22rpx] pr-[32rpx] pt-[10rpx] flex items-center justify-start flex-wrap">
|
||||
<div v-if="selectItemsModal.length > 1" v-for="item in selectItemsModal"
|
||||
class="w-1/4 mt-[20rpx] flex flex-col items-center justify-center">
|
||||
<div
|
||||
class="pl-[22rpx] pr-[32rpx] pt-[10rpx] flex items-center justify-start flex-wrap"
|
||||
>
|
||||
<div
|
||||
v-if="selectItemsModal.length > 1"
|
||||
v-for="item in selectItemsModal"
|
||||
class="w-1/4 mt-[20rpx] flex flex-col items-center justify-center"
|
||||
>
|
||||
<div class="w-[96rpx] h-[96rpx] rounded-[60rpx] flex-center">
|
||||
<tm-image :width="96" :height="96" :round="12" :src="item.avatar"></tm-image>
|
||||
<tm-image
|
||||
:width="96"
|
||||
:height="96"
|
||||
:round="12"
|
||||
:src="item.avatar"
|
||||
></tm-image>
|
||||
</div>
|
||||
<div
|
||||
class="mt-[8rpx] text-[#666666] text-[24rpx] w-[94rpx] truncate"
|
||||
>
|
||||
{{ item.name }}
|
||||
</div>
|
||||
<div class="mt-[8rpx] text-[#666666] text-[24rpx] w-[94rpx] truncate">{{ item.name }}</div>
|
||||
</div>
|
||||
<div v-else class="w-full flex items-center justify-start">
|
||||
<div class="w-[96rpx] h-[96rpx] rounded-[60rpx] flex-center">
|
||||
<tm-image :width="96" :height="96" :round="12" :src="selectItemsModal[0].avatar"></tm-image>
|
||||
<avatarModule
|
||||
:mode="2"
|
||||
:avatar="selectItemsModal[0].avatar"
|
||||
:groupType="selectItemsModal[0].group_type"
|
||||
:customStyle="{ width: '96rpx', height: '96rpx' }"
|
||||
></avatarModule>
|
||||
<div class="ml-[16rpx] text-[#161616] text-[32rpx] font-bold">
|
||||
{{ selectItemsModal[0].name }}
|
||||
</div>
|
||||
<div class="ml-[16rpx] text-[#161616] text-[32rpx] font-bold ">{{ selectItemsModal[0].name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</tm-modal>
|
||||
</ZPaging>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, watch, computed, onMounted, onUnmounted } from 'vue';
|
||||
import { onShow, onLoad } from "@dcloudio/uni-app";
|
||||
import { useChatList } from "@/store/chatList/index.js";
|
||||
import { useAuth } from "@/store/auth";
|
||||
import customInput from '@/components/custom-input/custom-input.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 { ref, watch, computed, onMounted, onUnmounted } from 'vue'
|
||||
import { onShow, onLoad } from '@dcloudio/uni-app'
|
||||
import { useChatList } from '@/store/chatList/index.js'
|
||||
import { useAuth } from '@/store/auth'
|
||||
import { useTalkStore, useUserStore, useDialogueStore } from '@/store'
|
||||
import chatItem from './components/chatItem.vue';
|
||||
import addCircle from "@/static/image/chatList/addCircle.png";
|
||||
import chatItem from './components/chatItem.vue'
|
||||
import addCircle from '@/static/image/chatList/addCircle.png'
|
||||
import lodash from 'lodash'
|
||||
|
||||
const talkStore = useTalkStore()
|
||||
@ -87,13 +136,13 @@ const items = ref([])
|
||||
// })
|
||||
|
||||
const selectItems = computed(() => {
|
||||
return items.value.filter(item => item.isCheck)
|
||||
return items.value.filter((item) => item.isCheck)
|
||||
})
|
||||
|
||||
const selectItemsModal = ref([])
|
||||
|
||||
const itemClick = (item) => {
|
||||
console.log(item);
|
||||
console.log(item)
|
||||
if (isMultiSelect.value) {
|
||||
item.isCheck = !item.isCheck
|
||||
return false
|
||||
@ -104,7 +153,7 @@ const itemClick = (item) => {
|
||||
}
|
||||
|
||||
const handleFinish = () => {
|
||||
console.log(selectItems.value);
|
||||
console.log(selectItems.value)
|
||||
selectItemsModal.value = selectItems.value
|
||||
if (selectItemsModal.value.length > 4) {
|
||||
forwardModalHeight.value = 560
|
||||
@ -123,7 +172,6 @@ const handleClose = () => {
|
||||
const handleOk = () => {
|
||||
let msg_ids = dialogueStore.forwardMessages.map((item) => item.msg_id)
|
||||
|
||||
|
||||
let user_ids = []
|
||||
let group_ids = []
|
||||
|
||||
@ -139,23 +187,27 @@ const handleOk = () => {
|
||||
mode: dialogueStore.forwardType,
|
||||
message_ids: msg_ids,
|
||||
uids: user_ids,
|
||||
gids: group_ids
|
||||
gids: group_ids,
|
||||
})
|
||||
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
console.log('cancel');
|
||||
console.log('cancel')
|
||||
}
|
||||
|
||||
watch(() => talkStore, (newValue, oldValue) => {
|
||||
console.log(talkStore);
|
||||
|
||||
}, { deep: true, immediate: true })
|
||||
watch(
|
||||
() => talkStore,
|
||||
(newValue, oldValue) => {
|
||||
console.log(talkStore)
|
||||
},
|
||||
{ deep: true, immediate: true },
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
talkStore.loadTalkList()
|
||||
console.log(talkStore.talkItems)
|
||||
items.value = lodash.cloneDeep(talkStore.talkItems)
|
||||
})
|
||||
onUnmounted(() => {
|
||||
@ -164,24 +216,20 @@ onUnmounted(() => {
|
||||
})
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
uni-page-body,
|
||||
page {
|
||||
::v-deep .zp-paging-container-content {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.outer-layer {
|
||||
overflow-y: auto;
|
||||
.choose-chat {
|
||||
flex: 1;
|
||||
background-image: url("@/static/image/clockIn/z3280@3x.png");
|
||||
background-size: cover;
|
||||
padding: 0 32rpx 20rpx 32rpx;
|
||||
padding: 20rpx 32rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.root {
|
||||
flex: 1;
|
||||
padding: 20rpx 0;
|
||||
background-image: url('@/static/image/clockIn/z3280@3x.png');
|
||||
background-size: cover;
|
||||
background-position: bottom center;
|
||||
background-attachment: fixed;
|
||||
}
|
||||
|
||||
.searchRoot {
|
||||
@ -195,33 +243,38 @@ page {
|
||||
}
|
||||
|
||||
.btnBox {
|
||||
:deep(.button) {
|
||||
&[disabled="true"] {
|
||||
background-color: #F3F3F3 !important;
|
||||
color: #BABABA !important;
|
||||
::v-deep .custom-btn-class {
|
||||
padding: 6rpx 24rpx !important;
|
||||
width: unset !important;
|
||||
min-width: unset !important;
|
||||
height: unset !important;
|
||||
}
|
||||
::v-deep .wd-button__text {
|
||||
font-size: 34rpx !important;
|
||||
font-weight: 400 !important;
|
||||
line-height: 48rpx !important;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.noNvueBorder.round-bl-4) {
|
||||
border-top: 1rpx solid #E7E7E7 !important;
|
||||
background-color: #FFFFFF !important;
|
||||
border-top: 1rpx solid #e7e7e7 !important;
|
||||
background-color: #ffffff !important;
|
||||
|
||||
uni-text {
|
||||
color: #1A1A1A !important;
|
||||
color: #1a1a1a !important;
|
||||
font-size: 32rpx !important;
|
||||
line-height: 48rpx !important;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.noNvueBorder.round-br-4) {
|
||||
border-top: 1rpx solid #E7E7E7 !important;
|
||||
border-left: 1rpx solid #E7E7E7 !important;
|
||||
background-color: #FFFFFF !important;
|
||||
border-top: 1rpx solid #e7e7e7 !important;
|
||||
border-left: 1rpx solid #e7e7e7 !important;
|
||||
background-color: #ffffff !important;
|
||||
|
||||
uni-text {
|
||||
font-weight: bold;
|
||||
color: #46299D !important;
|
||||
color: #46299d !important;
|
||||
font-size: 32rpx !important;
|
||||
line-height: 48rpx !important;
|
||||
}
|
||||
|
@ -1,12 +1,11 @@
|
||||
<template>
|
||||
<div class="outer-layer">
|
||||
<div>
|
||||
<tm-navbar :hideBack="false" hideHome title="选择部门"> </tm-navbar>
|
||||
</div>
|
||||
<div class="root">
|
||||
<div
|
||||
class="w-full h-[1134rpx] mb-[20rpx] pl-[32rpx] pr-[32rpx] pb-[20rpx] overflow-y-auto"
|
||||
>
|
||||
<div class="choose-deps-page">
|
||||
<zPaging ref="zPaging" :show-scrollbar="false">
|
||||
<template #top>
|
||||
<customNavbar :title="$t('pageTitle.select.department')"></customNavbar>
|
||||
</template>
|
||||
<div class="choose-deps">
|
||||
<div class="w-full pl-[32rpx] pr-[32rpx] overflow-y-auto">
|
||||
<div class="pl-[16rpx] pr-[16rpx] pt-[22rpx] pb-[24rpx] bg-[#FFFFFF]">
|
||||
<tm-input
|
||||
placeholder="请输入…"
|
||||
@ -48,7 +47,7 @@
|
||||
</div>
|
||||
<div
|
||||
class="pl-[32rpx] bg-[#FFFFFF] mt-[20rpx] h-[110rpx] flex items-center"
|
||||
@click="()=>allCheck(allCheckStatus)"
|
||||
@click="() => allCheck(allCheckStatus)"
|
||||
>
|
||||
<div>
|
||||
<checkBox
|
||||
@ -66,9 +65,7 @@
|
||||
class="pl-[32rpx] bg-[#FFFFFF] mt-[20rpx] h-[110rpx] flex items-center"
|
||||
>
|
||||
<div class="w-full flex items-center justify-between">
|
||||
<div
|
||||
class="flex items-center"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<div>
|
||||
<checkBox
|
||||
:disabled="!item?.sons?.length"
|
||||
@ -80,14 +77,19 @@
|
||||
{{ item.name }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="item.sons?.length" class="flex items-center mr-[32rpx]">
|
||||
<div
|
||||
v-if="item.sons?.length"
|
||||
class="flex items-center mr-[32rpx]"
|
||||
>
|
||||
<div class="vDivider mr-[32rpx]"></div>
|
||||
<div @click="() => toNextLevel(item)" class="flex items-center">
|
||||
<div class="mr-[12rpx]">
|
||||
<tm-image
|
||||
:width="26"
|
||||
:height="26"
|
||||
:src="item.checkStatus !== 'checked' ? downDep : downDepDis"
|
||||
:src="
|
||||
item.checkStatus !== 'checked' ? downDep : downDepDis
|
||||
"
|
||||
></tm-image>
|
||||
</div>
|
||||
<div
|
||||
@ -105,18 +107,29 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template #bottom>
|
||||
<div class="h-[162rpx] pl-[32rpx] pr-[32rpx] bg-[#FFFFFF]">
|
||||
<div class="mt-[14rpx] flex justify-between">
|
||||
<div class="flex flex-col">
|
||||
<div @click="openDrawer" class="flex items-center text-[28rpx] leading-[60rpx] text-[#000000] font-bold">
|
||||
<div
|
||||
@click="openDrawer"
|
||||
class="flex items-center text-[28rpx] leading-[60rpx] text-[#000000] font-bold"
|
||||
>
|
||||
<div>已选择部门数:</div>
|
||||
<div>{{ allCheckedList.length }}</div>
|
||||
<div class="ml-[28rpx]">
|
||||
<tm-icon :fontSize="24" color="#46299D" name="tmicon-angle-up"></tm-icon>
|
||||
<tm-icon
|
||||
:fontSize="24"
|
||||
color="#46299D"
|
||||
name="tmicon-angle-up"
|
||||
></tm-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-[24rpx] leading-[24rpx] text-[#7A58DE] w-[280rpx] truncate">
|
||||
{{ allCheckedList.map(v=>v.name).toString() }}
|
||||
<div
|
||||
class="text-[24rpx] leading-[24rpx] text-[#7A58DE] w-[280rpx] truncate"
|
||||
>
|
||||
{{ allCheckedList.map((v) => v.name).toString() }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="btnBox">
|
||||
@ -131,9 +144,7 @@
|
||||
:height="76"
|
||||
size="large"
|
||||
label="确定"
|
||||
>
|
||||
</tm-button>
|
||||
</div>
|
||||
></tm-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -144,50 +155,68 @@
|
||||
:hideHeader="true"
|
||||
:round="5"
|
||||
>
|
||||
<div class="flex flex-col w-full h-full pt-[36rpx] pl-[32rpx] pr-[32rpx] leading-[60rpx]" >
|
||||
<div class="text-[32rpx] font-bold flex items-center justify-between" >
|
||||
<div
|
||||
class="flex flex-col w-full h-full pt-[36rpx] pl-[32rpx] pr-[32rpx] leading-[60rpx]"
|
||||
>
|
||||
<div
|
||||
class="text-[32rpx] font-bold flex items-center justify-between"
|
||||
>
|
||||
<div class="flex items-center ml-[10rpx]">
|
||||
<div>已选择部门数:</div>
|
||||
<div>{{ allCheckedList.length }}</div>
|
||||
</div>
|
||||
<div
|
||||
class="text-[#7A58DE] mr-[10rpx]"
|
||||
@click="()=>showWin = false"
|
||||
@click="() => (showWin = false)"
|
||||
>
|
||||
确定
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1 pb-[20rpx] overflow-y-auto pt-[30rpx]" >
|
||||
<div class="flex-1 pb-[20rpx] overflow-y-auto pt-[30rpx]">
|
||||
<div
|
||||
v-for="(item,index) in allCheckedList"
|
||||
v-for="(item, index) in allCheckedList"
|
||||
class="flex flex-col"
|
||||
>
|
||||
<div v-if="index ===0" class="divider" ></div>
|
||||
<div v-if="index === 0" class="divider"></div>
|
||||
<div
|
||||
class="flex items-center justify-between mt-[36rpx] font-bold text-[#000000] leading-[54rpx] mb-[34rpx]"
|
||||
>
|
||||
<div class="text-[28rpx] ml-[10rpx]">{{ item.name }}</div>
|
||||
<div class="diyBtn">
|
||||
<tm-button @click="()=>deleteNode(item)" :disabled="userDepIds.includes(item.ID)" :margin="[10]" :shadow="0" text size="small" :width="106" :height="50" outlined label="移除"></tm-button>
|
||||
<tm-button
|
||||
@click="() => deleteNode(item)"
|
||||
:disabled="userDepIds.includes(item.ID)"
|
||||
:margin="[10]"
|
||||
:shadow="0"
|
||||
text
|
||||
size="small"
|
||||
:width="106"
|
||||
:height="50"
|
||||
outlined
|
||||
label="移除"
|
||||
></tm-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider" ></div>
|
||||
<div class="divider"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</tm-drawer>
|
||||
</template>
|
||||
</zPaging>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, watch, computed, onMounted,nextTick } from "vue";
|
||||
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 downDep from "@/static/image/chatList/downDep.png";
|
||||
import downDepDis from "@/static/image/chatList/downDepDis.png";
|
||||
import checkBox from "@/components/checkBox/index.vue";
|
||||
import zPaging from '@/uni_modules/z-paging/components/z-paging/z-paging.vue'
|
||||
import { ref, watch, computed, onMounted, nextTick } from 'vue'
|
||||
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 downDep from '@/static/image/chatList/downDep.png'
|
||||
import downDepDis from '@/static/image/chatList/downDepDis.png'
|
||||
import checkBox from '@/components/checkBox/index.vue'
|
||||
import lodash from 'lodash'
|
||||
|
||||
const {
|
||||
@ -197,201 +226,204 @@ const {
|
||||
crumbs,
|
||||
crumbsIndex,
|
||||
depCheckedKeys,
|
||||
} = useGroupTypeStore();
|
||||
} = useGroupTypeStore()
|
||||
const userStore = useUserStore()
|
||||
|
||||
const searchVal = ref("");
|
||||
const crumbsContainer = ref(null);
|
||||
const showWin = ref(false);
|
||||
const searchVal = ref('')
|
||||
const crumbsContainer = ref(null)
|
||||
const showWin = ref(false)
|
||||
|
||||
const getAllCheckedNodes = (node, checkedNodes = []) => {
|
||||
if (node.checkStatus === 'checked') {
|
||||
checkedNodes.push(node);
|
||||
checkedNodes.push(node)
|
||||
}
|
||||
if (node.sons && Array.isArray(node.sons)) {
|
||||
node.sons.forEach(son => getAllCheckedNodes(son, checkedNodes));
|
||||
node.sons.forEach((son) => getAllCheckedNodes(son, checkedNodes))
|
||||
}
|
||||
return checkedNodes;
|
||||
};
|
||||
return checkedNodes
|
||||
}
|
||||
|
||||
const userDepIds = computed(() => {
|
||||
return userStore.deps.map(v=>v.dept_id);
|
||||
});
|
||||
return userStore.deps.map((v) => v.dept_id)
|
||||
})
|
||||
|
||||
const allCheckedList = computed(() => {
|
||||
const checkedNodes = [];
|
||||
depTreeMyList.value.forEach(node => getAllCheckedNodes(node, checkedNodes));
|
||||
console.log("checkedNodes", checkedNodes);
|
||||
const checkedNodes = []
|
||||
depTreeMyList.value.forEach((node) => getAllCheckedNodes(node, checkedNodes))
|
||||
console.log('checkedNodes', checkedNodes)
|
||||
|
||||
return checkedNodes;
|
||||
return checkedNodes
|
||||
})
|
||||
|
||||
const currentCrumbs = computed(() => {
|
||||
if (crumbs.value[crumbsIndex.value] ) {
|
||||
if (searchVal.value && searchVal.value !== "") {
|
||||
if (crumbs.value[crumbsIndex.value]) {
|
||||
if (searchVal.value && searchVal.value !== '') {
|
||||
let filterSons = crumbs.value[crumbsIndex.value].sons.filter((item) =>
|
||||
item.name.includes(searchVal.value)
|
||||
);
|
||||
item.name.includes(searchVal.value),
|
||||
)
|
||||
return {
|
||||
...crumbs.value[crumbsIndex.value],
|
||||
sons: filterSons,
|
||||
};
|
||||
}
|
||||
console.log("allCheckedList", crumbs.value[crumbsIndex.value]);
|
||||
return crumbs.value[crumbsIndex.value];
|
||||
}
|
||||
console.log('allCheckedList', crumbs.value[crumbsIndex.value])
|
||||
return crumbs.value[crumbsIndex.value]
|
||||
}
|
||||
return {}
|
||||
});
|
||||
})
|
||||
|
||||
const allCheckStatus = computed(() => {
|
||||
if (!currentCrumbs.value.sons) {
|
||||
return "noChecked";
|
||||
return 'noChecked'
|
||||
}
|
||||
const allChecked = currentCrumbs.value.sons.every((son) => son.checkStatus === "checked");
|
||||
const someChecked =currentCrumbs.value.sons.some(
|
||||
(son) =>
|
||||
son.checkStatus === "checked" || son.checkStatus === "halfChecked"
|
||||
);
|
||||
const allChecked = currentCrumbs.value.sons.every(
|
||||
(son) => son.checkStatus === 'checked',
|
||||
)
|
||||
const someChecked = currentCrumbs.value.sons.some(
|
||||
(son) => son.checkStatus === 'checked' || son.checkStatus === 'halfChecked',
|
||||
)
|
||||
if (allChecked) {
|
||||
return "checked";
|
||||
return 'checked'
|
||||
} else if (someChecked) {
|
||||
return "halfChecked";
|
||||
return 'halfChecked'
|
||||
} else {
|
||||
return "noChecked";
|
||||
return 'noChecked'
|
||||
}
|
||||
})
|
||||
|
||||
const findNodeById = (node, targetId) => {
|
||||
if (node.ID === targetId) {
|
||||
return node;
|
||||
return node
|
||||
}
|
||||
if (node.sons && Array.isArray(node.sons)) {
|
||||
for (const son of node.sons) {
|
||||
const found = findNodeById(son, targetId);
|
||||
const found = findNodeById(son, targetId)
|
||||
if (found) {
|
||||
return found;
|
||||
return found
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
return null
|
||||
}
|
||||
|
||||
const findParentNode = (node, targetId) => {
|
||||
if (!node.sons || !Array.isArray(node.sons)) return null;
|
||||
if (!node.sons || !Array.isArray(node.sons)) return null
|
||||
for (const son of node.sons) {
|
||||
if (son.ID === targetId) {
|
||||
return node;
|
||||
return node
|
||||
}
|
||||
const parent = findParentNode(son, targetId);
|
||||
const parent = findParentNode(son, targetId)
|
||||
if (parent) {
|
||||
return parent;
|
||||
return parent
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
return null
|
||||
}
|
||||
|
||||
// 检查并更新父节点的状态
|
||||
const updateParentStatus = (node) => {
|
||||
const parent = findParentNode(depTreeMyList.value[0], node.ID);
|
||||
if (!parent) return;
|
||||
if (parent.checkStatus !== "checked"){
|
||||
const parent = findParentNode(depTreeMyList.value[0], node.ID)
|
||||
if (!parent) return
|
||||
if (parent.checkStatus !== 'checked') {
|
||||
const allChecked =
|
||||
parent.sons &&
|
||||
Array.isArray(parent.sons) &&
|
||||
parent.sons.every((son) => son.checkStatus === "checked");
|
||||
parent.sons.every((son) => son.checkStatus === 'checked')
|
||||
const someChecked =
|
||||
parent.sons &&
|
||||
Array.isArray(parent.sons) &&
|
||||
parent.sons.some(
|
||||
(son) =>
|
||||
son.checkStatus === "checked" || son.checkStatus === "halfChecked"
|
||||
);
|
||||
son.checkStatus === 'checked' || son.checkStatus === 'halfChecked',
|
||||
)
|
||||
|
||||
if (allChecked) {
|
||||
parent.checkStatus = "halfChecked";
|
||||
parent.checkStatus = 'halfChecked'
|
||||
} else if (someChecked) {
|
||||
parent.checkStatus = "halfChecked";
|
||||
parent.checkStatus = 'halfChecked'
|
||||
} else {
|
||||
parent.checkStatus = "noChecked";
|
||||
parent.checkStatus = 'noChecked'
|
||||
}
|
||||
}
|
||||
|
||||
updateParentStatus(parent);
|
||||
};
|
||||
updateParentStatus(parent)
|
||||
}
|
||||
//根据子节点状态更新当前节点状态
|
||||
const updateNodeStatus = (node) => {
|
||||
if (!node.sons || !Array.isArray(node.sons)) return;
|
||||
const allChecked = node.sons.every(son => son.checkStatus === 'checked');
|
||||
const someChecked = node.sons.some(son => son.checkStatus === 'checked' || son.checkStatus === 'halfChecked');
|
||||
if (!node.sons || !Array.isArray(node.sons)) return
|
||||
const allChecked = node.sons.every((son) => son.checkStatus === 'checked')
|
||||
const someChecked = node.sons.some(
|
||||
(son) => son.checkStatus === 'checked' || son.checkStatus === 'halfChecked',
|
||||
)
|
||||
|
||||
if (allChecked) {
|
||||
node.checkStatus = 'halfChecked';
|
||||
node.checkStatus = 'halfChecked'
|
||||
} else if (someChecked) {
|
||||
node.checkStatus = 'halfChecked';
|
||||
node.checkStatus = 'halfChecked'
|
||||
} else {
|
||||
node.checkStatus = 'noChecked';
|
||||
node.checkStatus = 'noChecked'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 更新当前节点及其所有子节点的状态
|
||||
const updateCheckStatus = (node, status) => {
|
||||
node.checkStatus = status;
|
||||
node.checkStatus = status
|
||||
if (node.sons && Array.isArray(node.sons) && node.sons.length > 0) {
|
||||
node.sons.forEach((son) => updateCheckStatus(son, status));
|
||||
node.sons.forEach((son) => updateCheckStatus(son, status))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const checkItemChange = (item, val) => {
|
||||
// 更新当前节点及其子节点的状态
|
||||
updateCheckStatus(item, val);
|
||||
updateCheckStatus(item, val)
|
||||
|
||||
// 更新父节点的状态
|
||||
updateParentStatus(item);
|
||||
};
|
||||
updateParentStatus(item)
|
||||
}
|
||||
|
||||
const deleteNode = (item) => {
|
||||
const treeNode = findNodeById(depTreeMyList.value[0], item.ID);
|
||||
if (!treeNode) return;
|
||||
treeNode.checkStatus = "noChecked";
|
||||
updateNodeStatus(treeNode);
|
||||
updateParentStatus(treeNode);
|
||||
};
|
||||
const treeNode = findNodeById(depTreeMyList.value[0], item.ID)
|
||||
if (!treeNode) return
|
||||
treeNode.checkStatus = 'noChecked'
|
||||
updateNodeStatus(treeNode)
|
||||
updateParentStatus(treeNode)
|
||||
}
|
||||
|
||||
const toNextLevel = async (item) => {
|
||||
if (item.checkStatus !== "checked") {
|
||||
crumbs.value.push(item);
|
||||
crumbsIndex.value++;
|
||||
await nextTick();
|
||||
if (item.checkStatus !== 'checked') {
|
||||
crumbs.value.push(item)
|
||||
crumbsIndex.value++
|
||||
await nextTick()
|
||||
if (crumbsContainer.value) {
|
||||
crumbsContainer.value.scrollLeft = crumbsContainer.value.scrollWidth;
|
||||
crumbsContainer.value.scrollLeft = crumbsContainer.value.scrollWidth
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const handleCrumbsClick = (index) => {
|
||||
crumbsIndex.value = index;
|
||||
crumbs.value = crumbs.value.slice(0, index + 1);
|
||||
};
|
||||
crumbsIndex.value = index
|
||||
crumbs.value = crumbs.value.slice(0, index + 1)
|
||||
}
|
||||
|
||||
const allCheck = (status) => {
|
||||
let statusT = 'noChecked';
|
||||
if (status === "checked") {
|
||||
statusT = "noChecked";
|
||||
let statusT = 'noChecked'
|
||||
if (status === 'checked') {
|
||||
statusT = 'noChecked'
|
||||
} else {
|
||||
statusT = "checked";
|
||||
statusT = 'checked'
|
||||
}
|
||||
currentCrumbs.value.sons.forEach((item) => {
|
||||
const itemT = findNodeById(depTreeMyList.value[0], item.ID)
|
||||
if (!itemT) return;
|
||||
checkItemChange(itemT, statusT);
|
||||
});
|
||||
};
|
||||
if (!itemT) return
|
||||
checkItemChange(itemT, statusT)
|
||||
})
|
||||
}
|
||||
|
||||
const openDrawer = () => {
|
||||
showWin.value = true;
|
||||
if (allCheckedList.length>0) {
|
||||
showWin.value = true
|
||||
if (allCheckedList.length > 0) {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// watch(() => depTreeMyList.value, (newValue, oldValue) => {
|
||||
// console.log("depTreeMyList", newValue);
|
||||
@ -404,54 +436,48 @@ const openDrawer = () => {
|
||||
|
||||
const handleConfirm = () => {
|
||||
depCheckedKeys.value = lodash.cloneDeep(allCheckedList.value)
|
||||
console.log("depCheckedKeys", depCheckedKeys.value);
|
||||
console.log('depCheckedKeys', depCheckedKeys.value)
|
||||
|
||||
uni.navigateBack();
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
const initCheckedKeys = () => {
|
||||
depCheckedKeys.value.forEach((item) => {
|
||||
const node = findNodeById(depTreeMyList.value[0], item.ID);
|
||||
const node = findNodeById(depTreeMyList.value[0], item.ID)
|
||||
if (node) {
|
||||
node.checkStatus = "checked";
|
||||
updateParentStatus(node);
|
||||
node.checkStatus = 'checked'
|
||||
updateParentStatus(node)
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
const init = async () => {
|
||||
crumbsIndex.value = 0;
|
||||
await getDepsTreeMy();
|
||||
crumbs.value = depTreeMyList.value.length ? [depTreeMyList.value[0]] : [];
|
||||
initCheckedKeys();
|
||||
};
|
||||
crumbsIndex.value = 0
|
||||
await getDepsTreeMy()
|
||||
crumbs.value = depTreeMyList.value.length ? [depTreeMyList.value[0]] : []
|
||||
initCheckedKeys()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
init()
|
||||
})
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
uni-page-body,
|
||||
page {
|
||||
::v-deep .zp-paging-container-content {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
}
|
||||
.outer-layer {
|
||||
overflow-y: auto;
|
||||
.choose-deps-page {
|
||||
.choose-deps {
|
||||
flex: 1;
|
||||
background-image: url("@/static/image/clockIn/z3280@3x.png");
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-image: url('@/static/image/clockIn/z3280@3x.png');
|
||||
background-size: cover;
|
||||
padding-bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.root {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
padding-top: 18rpx;
|
||||
overflow: hidden;
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
}
|
||||
|
||||
.divider {
|
||||
height: 1rpx;
|
||||
background-color: #707070;
|
||||
@ -478,22 +504,22 @@ page {
|
||||
background-repeat: no-repeat;
|
||||
border-radius: 12rpx;
|
||||
&.firstPanel {
|
||||
background-image: url("@/static/image/chatList/zu6033@2x.png");
|
||||
background-image: url('@/static/image/chatList/zu6033@2x.png');
|
||||
}
|
||||
&.secondPanel {
|
||||
background-image: url("@/static/image/chatList/zu6031@2x.png");
|
||||
background-image: url('@/static/image/chatList/zu6031@2x.png');
|
||||
margin-top: 28rpx;
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
&.thirdPanel {
|
||||
background-image: url("@/static/image/chatList/zu6032@2x.png");
|
||||
background-image: url('@/static/image/chatList/zu6032@2x.png');
|
||||
}
|
||||
&.activePanel {
|
||||
box-shadow: 0 0 0 3rpx #46299d;
|
||||
}
|
||||
}
|
||||
.btnBox {
|
||||
:deep(uni-button[disabled="true"]) {
|
||||
:deep(uni-button[disabled='true']) {
|
||||
color: #bebebe !important;
|
||||
}
|
||||
}
|
||||
@ -508,12 +534,12 @@ page {
|
||||
.diyBtn {
|
||||
:deep(uni-button) {
|
||||
color: #191919 !important;
|
||||
border: 1rpx solid #D6D6D8 !important;
|
||||
background-color: #FFFFFF !important;
|
||||
&[disabled="true"] {
|
||||
color: #BEBEBE !important;
|
||||
border: 1rpx solid #E6E6E6 !important;
|
||||
background-color: #E6E6E6 !important;
|
||||
border: 1rpx solid #d6d6d8 !important;
|
||||
background-color: #ffffff !important;
|
||||
&[disabled='true'] {
|
||||
color: #bebebe !important;
|
||||
border: 1rpx solid #e6e6e6 !important;
|
||||
background-color: #e6e6e6 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,159 +1,195 @@
|
||||
<template>
|
||||
<div class="outer-layer">
|
||||
<div>
|
||||
<tm-navbar :hideBack="false" hideHome title="选择群类型">
|
||||
</tm-navbar>
|
||||
</div>
|
||||
<div class="root">
|
||||
<div class="w-full h-[1134rpx] mb-[20rpx] pl-[32rpx] pr-[32rpx] overflow-y-auto" >
|
||||
<div class=" pl-[32rpx] pr-[32rpx] pt-[44rpx] pb-[42rpx] bg-[#FFFFFF]" >
|
||||
<div class="text-[40rpx] leading-[54rpx] text-[#2F2F2F] font-bold" >
|
||||
<div class="choose-group-type-page">
|
||||
<ZPaging ref="zPaging" :show-scrollbar="false">
|
||||
<template #top>
|
||||
<customNavbar :title="$t('pageTitle.select.groupType')"></customNavbar>
|
||||
</template>
|
||||
<div class="choose-group-type">
|
||||
<div class="w-full h-[1134rpx] mb-[20rpx] pl-[32rpx] pr-[32rpx]">
|
||||
<div class="pl-[32rpx] pr-[32rpx] pt-[44rpx] pb-[42rpx] bg-[#FFFFFF]">
|
||||
<div class="text-[40rpx] leading-[54rpx] text-[#2F2F2F] font-bold">
|
||||
群类型保存后将不可修改
|
||||
</div>
|
||||
<div class="text-[28rpx] leading-[54rpx] text-[#46299D] mt-[16rpx]" >
|
||||
<div class="text-[28rpx] leading-[54rpx] text-[#46299D] mt-[16rpx]">
|
||||
请创建过程中正确选择
|
||||
</div>
|
||||
<div class="mt-[54rpx] w-full h-[872rpx]" >
|
||||
<div @click="groupActiveIndex = 0" class="groupCard firstPanel" :class="groupActiveIndex === 0?'activePanel':''">
|
||||
<div class="mt-[54rpx] w-full h-[872rpx]">
|
||||
<div
|
||||
@click="groupActiveIndex = 0"
|
||||
class="groupCard firstPanel"
|
||||
:class="groupActiveIndex === 0 ? 'activePanel' : ''"
|
||||
>
|
||||
<div class="w-full h-full pt-[64rpx] pl-[36rpx]">
|
||||
<div class="text-[36rpx] leading-[54rpx] text-[#2F2F2F] font-bold" >
|
||||
<div
|
||||
class="text-[36rpx] leading-[54rpx] text-[#2F2F2F] font-bold"
|
||||
>
|
||||
普通群
|
||||
</div>
|
||||
<div class="text-[24rpx] leading-[36rpx] text-[#939393] w-[216rpx]" >
|
||||
员工线上沟通专用群
|
||||
离职后自动退群
|
||||
<div
|
||||
class="text-[24rpx] leading-[36rpx] text-[#939393] w-[216rpx]"
|
||||
>
|
||||
员工线上沟通专用群 离职后自动退群
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div @click="groupActiveIndex = 1" class="groupCard secondPanel" :class="groupActiveIndex === 1?'activePanel':''">
|
||||
<div class="w-full h-full pr-[36rpx] flex flex-col items-end justify-center">
|
||||
<div class="text-[36rpx] leading-[54rpx] text-[#2F2F2F] font-bold" >
|
||||
<div
|
||||
@click="groupActiveIndex = 1"
|
||||
class="groupCard secondPanel"
|
||||
:class="groupActiveIndex === 1 ? 'activePanel' : ''"
|
||||
>
|
||||
<div
|
||||
class="w-full h-full pr-[36rpx] flex flex-col items-end justify-center"
|
||||
>
|
||||
<div
|
||||
class="text-[36rpx] leading-[54rpx] text-[#2F2F2F] font-bold"
|
||||
>
|
||||
部门群
|
||||
</div>
|
||||
<div class="text-[24rpx] leading-[36rpx] text-[#939393] w-[288rpx] text-end" >
|
||||
指定部门员工入职自动进群
|
||||
离职后自动退群
|
||||
<div
|
||||
class="text-[24rpx] leading-[36rpx] text-[#939393] w-[288rpx] text-end"
|
||||
>
|
||||
指定部门员工入职自动进群 离职后自动退群
|
||||
</div>
|
||||
<div
|
||||
v-if="!depCheckedKeys.length"
|
||||
@click="chooseDep"
|
||||
class="text-[24rpx] leading-[36rpx] text-[#C1B4EA] flex items-center mt-[16rpx]"
|
||||
>
|
||||
<div
|
||||
:class="
|
||||
groupActiveIndex === 1
|
||||
? 'text-[#7A58DE]'
|
||||
: 'text-[#C1B4EA]'
|
||||
"
|
||||
>
|
||||
选择部门
|
||||
</div>
|
||||
<div v-if="!depCheckedKeys.length" @click="chooseDep" class="text-[24rpx] leading-[36rpx] text-[#C1B4EA] flex items-center mt-[16rpx]" >
|
||||
<div :class="groupActiveIndex === 1?'text-[#7A58DE]':'text-[#C1B4EA]'" >选择部门</div>
|
||||
<div class="ml-[20rpx]">
|
||||
<tm-icon name="tmicon-angle-right" :font-size="18" :color="groupActiveIndex === 1?'#7A58DE':'#C1B4EA'"></tm-icon>
|
||||
<tm-icon
|
||||
name="tmicon-angle-right"
|
||||
:font-size="18"
|
||||
:color="groupActiveIndex === 1 ? '#7A58DE' : '#C1B4EA'"
|
||||
></tm-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-[24rpx] leading-[36rpx] flex flex-col justify-center items-end mt-[16rpx] mb-[22rpx]" >
|
||||
<div class="max-w-[336rpx] truncate" >
|
||||
{{ depCheckedKeys.map(v=>v.name).toString() }}
|
||||
<div
|
||||
v-else
|
||||
class="text-[24rpx] leading-[36rpx] flex flex-col justify-center items-end mt-[16rpx] mb-[22rpx]"
|
||||
>
|
||||
<div class="max-w-[336rpx] truncate">
|
||||
{{ depCheckedKeys.map((v) => v.name).toString() }}
|
||||
</div>
|
||||
<div @click="chooseDep" class="flex items-center" >
|
||||
<div class="text-[#7A58DE]" >查看全部</div>
|
||||
<div @click="chooseDep" class="flex items-center">
|
||||
<div class="text-[#7A58DE]">查看全部</div>
|
||||
<div class="ml-[20rpx]">
|
||||
<tm-icon name="tmicon-angle-right" :font-size="18" :color="groupActiveIndex === 1?'#7A58DE':'#C1B4EA'"></tm-icon>
|
||||
<tm-icon
|
||||
name="tmicon-angle-right"
|
||||
:font-size="18"
|
||||
:color="
|
||||
groupActiveIndex === 1 ? '#7A58DE' : '#C1B4EA'
|
||||
"
|
||||
></tm-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div @click="groupActiveIndex = 2" class="groupCard thirdPanel" :class="groupActiveIndex === 2?'activePanel':''">
|
||||
<div
|
||||
@click="groupActiveIndex = 2"
|
||||
class="groupCard thirdPanel"
|
||||
:class="groupActiveIndex === 2 ? 'activePanel' : ''"
|
||||
>
|
||||
<div class="w-full h-full pt-[64rpx] pl-[36rpx]">
|
||||
<div class="text-[36rpx] leading-[54rpx] text-[#2F2F2F] font-bold" >
|
||||
<div
|
||||
class="text-[36rpx] leading-[54rpx] text-[#2F2F2F] font-bold"
|
||||
>
|
||||
项目群
|
||||
</div>
|
||||
<div class="text-[24rpx] leading-[36rpx] text-[#939393] w-[216rpx]" >
|
||||
项目成员沟通专用群
|
||||
离职后自动退群
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-[162rpx] flex justify-center bg-[#FFFFFF]">
|
||||
<div class="mt-[14rpx] btnBox" >
|
||||
<tm-button
|
||||
@click="handleConfirm"
|
||||
color="#46299D"
|
||||
:disabled="confirmBtnStatus"
|
||||
disabledColor="#E6E6E6"
|
||||
:margin="[0]"
|
||||
:shadow="0"
|
||||
:width="426"
|
||||
:height="76"
|
||||
size="large"
|
||||
label="确定"
|
||||
<div
|
||||
class="text-[24rpx] leading-[36rpx] text-[#939393] w-[216rpx]"
|
||||
>
|
||||
</tm-button>
|
||||
项目成员沟通专用群 离职后自动退群
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template #bottom>
|
||||
<customBtn
|
||||
:isBottom="true"
|
||||
:btnText="$t('ok')"
|
||||
@click="handleConfirm"
|
||||
:disabled="confirmBtnStatus"
|
||||
></customBtn>
|
||||
</template>
|
||||
</ZPaging>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
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 } from "@/store";
|
||||
import { useGroupTypeStore } from "@/store/groupType";
|
||||
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 } 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'
|
||||
|
||||
const { groupActiveIndex,depCheckedKeys } = useGroupTypeStore();
|
||||
const { groupActiveIndex, depCheckedKeys } = useGroupTypeStore()
|
||||
|
||||
const confirmBtnStatus = computed(() => {
|
||||
let disabledT = false;
|
||||
let disabledT = false
|
||||
switch (groupActiveIndex.value) {
|
||||
case 0:
|
||||
break;
|
||||
break
|
||||
case 1:
|
||||
if (!depCheckedKeys.value.length) {
|
||||
disabledT = true;
|
||||
disabledT = true
|
||||
}
|
||||
break;
|
||||
break
|
||||
case 2:
|
||||
break;
|
||||
break
|
||||
default:
|
||||
break;
|
||||
break
|
||||
}
|
||||
return disabledT
|
||||
})
|
||||
|
||||
const chooseDep = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/chooseDeps/index'
|
||||
});
|
||||
};
|
||||
url: '/pages/chooseByDeps/index?chooseMode=1',
|
||||
})
|
||||
}
|
||||
|
||||
const handleConfirm = () => {
|
||||
uni.navigateBack()
|
||||
// uni.navigateTo({
|
||||
// url: '/pages/creatGroupChat/index'
|
||||
// });
|
||||
};
|
||||
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
uni-page-body,
|
||||
page {
|
||||
::v-deep .zp-paging-container-content {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
}
|
||||
.outer-layer {
|
||||
overflow-y: auto;
|
||||
|
||||
.choose-group-type {
|
||||
flex: 1;
|
||||
background-image: url("@/static/image/clockIn/z3280@3x.png");
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-image: url('@/static/image/clockIn/z3280@3x.png');
|
||||
background-size: cover;
|
||||
padding-bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-position: bottom center;
|
||||
padding: 20rpx 0;
|
||||
background-attachment: fixed;
|
||||
}
|
||||
.root {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
padding-top: 18rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
.divider{
|
||||
|
||||
.divider {
|
||||
height: 1rpx;
|
||||
background-color: #7C7C7C;
|
||||
background-color: #7c7c7c;
|
||||
opacity: 0.6;
|
||||
}
|
||||
.avatar-placeholder {
|
||||
@ -171,24 +207,23 @@ page {
|
||||
background-repeat: no-repeat;
|
||||
border-radius: 12rpx;
|
||||
&.firstPanel {
|
||||
background-image: url("@/static/image/chatList/zu6033@2x.png");
|
||||
background-image: url('@/static/image/chatList/zu6033@2x.png');
|
||||
}
|
||||
&.secondPanel {
|
||||
background-image: url("@/static/image/chatList/zu6031@2x.png");
|
||||
background-image: url('@/static/image/chatList/zu6031@2x.png');
|
||||
margin-top: 28rpx;
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
&.thirdPanel {
|
||||
background-image: url("@/static/image/chatList/zu6032@2x.png");
|
||||
background-image: url('@/static/image/chatList/zu6032@2x.png');
|
||||
}
|
||||
&.activePanel {
|
||||
box-shadow: 0 0 0 3rpx #46299D;
|
||||
box-shadow: 0 0 0 3rpx #46299d;
|
||||
}
|
||||
}
|
||||
.btnBox {
|
||||
:deep(uni-button[disabled="true"]){
|
||||
color: #BEBEBE !important;
|
||||
:deep(uni-button[disabled='true']) {
|
||||
color: #bebebe !important;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="outer-layer">
|
||||
<div>
|
||||
<tm-navbar :hideBack="false" hideHome title="选择部门"> </tm-navbar>
|
||||
<tm-navbar :hideBack="false" hideHome title="选择部门"></tm-navbar>
|
||||
</div>
|
||||
<div class="root">
|
||||
<div
|
||||
@ -48,7 +48,7 @@
|
||||
</div>
|
||||
<div
|
||||
class="pl-[32rpx] bg-[#FFFFFF] mt-[20rpx] h-[110rpx] flex items-center"
|
||||
@click="()=>allCheck(allCheckStatus)"
|
||||
@click="() => allCheck(allCheckStatus)"
|
||||
>
|
||||
<div>
|
||||
<checkBox
|
||||
@ -63,12 +63,20 @@
|
||||
<div
|
||||
v-if="currentCrumbs?.sons?.length"
|
||||
v-for="item in currentCrumbs?.sons"
|
||||
@click="
|
||||
checkItemChange(
|
||||
item,
|
||||
item.checkStatus
|
||||
? item.checkStatus === 'checked'
|
||||
? 'noChecked'
|
||||
: 'checked'
|
||||
: 'checked',
|
||||
)
|
||||
"
|
||||
class="pl-[32rpx] bg-[#FFFFFF] mt-[20rpx] h-[110rpx] flex items-center"
|
||||
>
|
||||
<div class="w-full flex items-center justify-between">
|
||||
<div
|
||||
class="flex items-center"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<div>
|
||||
<checkBox
|
||||
:disabled="!item?.sons?.length"
|
||||
@ -82,7 +90,10 @@
|
||||
</div>
|
||||
<div v-if="item.sons?.length" class="flex items-center mr-[32rpx]">
|
||||
<div class="vDivider mr-[32rpx]"></div>
|
||||
<div @click="() => toNextLevel(item)" class="flex items-center">
|
||||
<div
|
||||
@click.stop="() => toNextLevel(item)"
|
||||
class="flex items-center"
|
||||
>
|
||||
<div class="mr-[12rpx]">
|
||||
<tm-image
|
||||
:width="26"
|
||||
@ -108,15 +119,26 @@
|
||||
<div
|
||||
v-if="currentMembers.length"
|
||||
v-for="item in currentMembers"
|
||||
@click="
|
||||
checkMember(
|
||||
item,
|
||||
membersCheckedKeys.filter((v) => v.ID === item.ID).length > 0
|
||||
? 'noChecked'
|
||||
: 'checked',
|
||||
)
|
||||
"
|
||||
class="pl-[32rpx] pr-[32rpx] bg-[#FFFFFF] mt-[20rpx] h-[110rpx] flex items-center"
|
||||
>
|
||||
<div class="w-full flex items-center justify-between">
|
||||
<div
|
||||
class="flex items-center"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<div>
|
||||
<checkBox
|
||||
:modelValue="membersCheckedKeys.filter(v=>v.ID === item.ID).length>0?'checked':'noChecked'"
|
||||
:modelValue="
|
||||
membersCheckedKeys.filter((v) => v.ID === item.ID).length >
|
||||
0
|
||||
? 'checked'
|
||||
: 'noChecked'
|
||||
"
|
||||
@change="(val) => checkMember(item, val)"
|
||||
></checkBox>
|
||||
</div>
|
||||
@ -124,24 +146,30 @@
|
||||
<div class="userAvatar flex items-center justify-center">
|
||||
{{ item.nickName.slice(-2) }}
|
||||
</div>
|
||||
<div class="ml-[20rpx] flex flex-col items-center">
|
||||
<div class="text-[28rpx] font-bold" >{{ item.nickName }}</div>
|
||||
<div class="text-[20rpx] text-[#747474]" >{{ item.jobNum }}</div>
|
||||
<div class="ml-[20rpx] flex flex-col justify-center">
|
||||
<div class="text-[28rpx] font-bold">{{ item.nickName }}</div>
|
||||
<div class="text-[20rpx] text-[#747474]">
|
||||
{{ item.jobNum }}
|
||||
</div>
|
||||
</div>
|
||||
<tm-popover position="tc">
|
||||
<div class="ml-[20rpx] flex h-[68rpx] flex-wrap line-clamp-2 max-w-[342rpx]">
|
||||
<div
|
||||
class="ml-[6rpx] flex h-[68rpx] flex-wrap line-clamp-2 max-w-[342rpx]"
|
||||
>
|
||||
<div
|
||||
v-for="post in item.positions"
|
||||
class="postTag truncate mb-[4rpx] mr-[14rpx] max-w-[164rpx]"
|
||||
class="postTag truncate mb-[4rpx] ml-[14rpx] max-w-[164rpx]"
|
||||
>
|
||||
{{ post.name }}
|
||||
</div>
|
||||
</div>
|
||||
<template v-slot:label>
|
||||
<div class="max-h-[250rpx] overflow-y-auto pt-[10rpx] pl-[18rpx] pr-[18rpx] pb-[12rpx]">
|
||||
<div
|
||||
class="max-h-[250rpx] overflow-y-auto pt-[10rpx] pl-[18rpx] pr-[18rpx] pb-[12rpx]"
|
||||
>
|
||||
<div
|
||||
v-for="post in item.positions"
|
||||
class="postTag truncate mb-[10rpx] "
|
||||
class="postTag truncate mb-[10rpx]"
|
||||
>
|
||||
{{ post.name }}
|
||||
</div>
|
||||
@ -152,25 +180,33 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="h-[162rpx] pl-[32rpx] pr-[32rpx] bg-[#FFFFFF]">
|
||||
<div class="mt-[14rpx] flex justify-between">
|
||||
<div class="flex flex-col">
|
||||
<div @click="openDrawer" class="flex items-center text-[28rpx] leading-[60rpx] text-[#000000] font-bold">
|
||||
<div
|
||||
@click="openDrawer"
|
||||
class="flex items-center text-[28rpx] leading-[60rpx] text-[#000000] font-bold"
|
||||
>
|
||||
<div>已选择:</div>
|
||||
<div>{{ totalMembers }}人</div>
|
||||
<div class="ml-[28rpx]">
|
||||
<tm-icon :fontSize="24" color="#46299D" name="tmicon-angle-up"></tm-icon>
|
||||
<tm-icon
|
||||
:fontSize="24"
|
||||
color="#46299D"
|
||||
name="tmicon-angle-up"
|
||||
></tm-icon>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-[24rpx] leading-[24rpx] text-[#7A58DE] w-[280rpx] truncate ">
|
||||
<span v-for="item in allCheckedList" >
|
||||
<div
|
||||
class="text-[24rpx] leading-[24rpx] text-[#7A58DE] w-[280rpx] truncate"
|
||||
>
|
||||
<span v-for="item in allCheckedList">
|
||||
{{ item.name }}({{ item.staffNum }})
|
||||
</span>
|
||||
<span v-for="(item,index) in membersCheckedKeys" >
|
||||
<span v-for="(item, index) in membersCheckedKeys">
|
||||
{{ item.nickName }}
|
||||
{{ index!== membersCheckedKeys.length-1?',':"" }}
|
||||
{{ index !== membersCheckedKeys.length - 1 ? ',' : '' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -186,8 +222,7 @@
|
||||
:height="76"
|
||||
size="large"
|
||||
label="确定"
|
||||
>
|
||||
</tm-button>
|
||||
></tm-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -199,48 +234,63 @@
|
||||
:hideHeader="true"
|
||||
:round="5"
|
||||
>
|
||||
<div class="flex flex-col w-full h-full pt-[36rpx] pl-[32rpx] pr-[32rpx] leading-[60rpx]" >
|
||||
<div class="text-[32rpx] font-bold flex items-center justify-between" >
|
||||
<div
|
||||
class="flex flex-col w-full h-full pt-[36rpx] pl-[32rpx] pr-[32rpx] leading-[60rpx]"
|
||||
>
|
||||
<div class="text-[32rpx] font-bold flex items-center justify-between">
|
||||
<div class="flex items-center ml-[10rpx]">
|
||||
<div>已选择部门数:</div>
|
||||
<div>{{ allCheckedList.length }}</div>
|
||||
</div>
|
||||
<div
|
||||
class="text-[#7A58DE] mr-[10rpx]"
|
||||
@click="()=>showWin = false"
|
||||
@click="() => (showWin = false)"
|
||||
>
|
||||
确定
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1 pb-[20rpx] overflow-y-auto pt-[30rpx]" >
|
||||
<div
|
||||
v-for="(item,index) in allCheckedList"
|
||||
class="flex flex-col"
|
||||
>
|
||||
<div v-if="index ===0" class="divider" ></div>
|
||||
<div class="flex-1 pb-[20rpx] overflow-y-auto pt-[30rpx]">
|
||||
<div v-for="(item, index) in allCheckedList" class="flex flex-col">
|
||||
<div v-if="index === 0" class="divider"></div>
|
||||
<div
|
||||
class="flex items-center justify-between mt-[36rpx] font-bold text-[#000000] leading-[54rpx] mb-[34rpx]"
|
||||
>
|
||||
<div class="text-[28rpx] ml-[10rpx]">{{ item.name }}({{ item.staffNum }})</div>
|
||||
<div class="text-[28rpx] ml-[10rpx]">
|
||||
{{ item.name }}({{ item.staffNum }})
|
||||
</div>
|
||||
<div class="diyBtn">
|
||||
<tm-button @click="()=>deleteNode(item)" :disabled="userDepIds.includes(item.ID)" :margin="[10]" :shadow="0" text size="small" :width="106" :height="50" outlined label="移除"></tm-button>
|
||||
<tm-button
|
||||
@click="() => deleteNode(item)"
|
||||
:disabled="userDepIds.includes(item.ID)"
|
||||
:margin="[10]"
|
||||
:shadow="0"
|
||||
text
|
||||
size="small"
|
||||
:width="106"
|
||||
:height="50"
|
||||
outlined
|
||||
label="移除"
|
||||
></tm-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider" ></div>
|
||||
<div class="divider"></div>
|
||||
</div>
|
||||
<div
|
||||
v-for="(item,index) in membersCheckedKeys"
|
||||
v-for="(item, index) in membersCheckedKeys"
|
||||
class="flex flex-col"
|
||||
>
|
||||
<div v-if="index ===0 && allCheckedList.length==0" class="divider" ></div>
|
||||
<div
|
||||
v-if="index === 0 && allCheckedList.length == 0"
|
||||
class="divider"
|
||||
></div>
|
||||
<div
|
||||
class="flex items-center justify-between mt-[36rpx] font-bold text-[#000000] leading-[54rpx] mb-[34rpx]"
|
||||
>
|
||||
<div class="text-[28rpx] ml-[10rpx] flex">
|
||||
<div>
|
||||
{{ item.nickName }}({{ item.jobNum }})
|
||||
</div>
|
||||
<div class="ml-[20rpx] flex h-[68rpx] flex-wrap line-clamp-2 max-w-[342rpx]">
|
||||
<div>{{ item.nickName }}({{ item.jobNum }})</div>
|
||||
<div
|
||||
class="ml-[20rpx] flex h-[68rpx] flex-wrap line-clamp-2 max-w-[342rpx]"
|
||||
>
|
||||
<div
|
||||
v-for="post in item.positions"
|
||||
class="postTag truncate mb-[4rpx] mr-[14rpx] max-w-[164rpx]"
|
||||
@ -250,10 +300,20 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="diyBtn">
|
||||
<tm-button @click="()=>deleteMember(item)" :margin="[10]" :shadow="0" text size="small" :width="106" :height="50" outlined label="移除"></tm-button>
|
||||
<tm-button
|
||||
@click="() => deleteMember(item)"
|
||||
:margin="[10]"
|
||||
:shadow="0"
|
||||
text
|
||||
size="small"
|
||||
:width="106"
|
||||
:height="50"
|
||||
outlined
|
||||
label="移除"
|
||||
></tm-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider" ></div>
|
||||
<div class="divider"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -261,15 +321,15 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, watch, computed, onMounted,nextTick } from "vue";
|
||||
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 downDep from "@/static/image/chatList/downDep.png";
|
||||
import downDepDis from "@/static/image/chatList/downDepDis.png";
|
||||
import checkBox from "@/components/checkBox/index.vue";
|
||||
import { ref, watch, computed, onMounted, nextTick } from 'vue'
|
||||
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 downDep from '@/static/image/chatList/downDep.png'
|
||||
import downDepDis from '@/static/image/chatList/downDepDis.png'
|
||||
import checkBox from '@/components/checkBox/index.vue'
|
||||
import lodash from 'lodash'
|
||||
|
||||
const {
|
||||
@ -282,76 +342,78 @@ const {
|
||||
getDepMembers,
|
||||
membersCheckedKeys,
|
||||
allChooseMembers,
|
||||
} = useGroupTypeStore();
|
||||
} = useGroupTypeStore()
|
||||
const userStore = useUserStore()
|
||||
|
||||
const searchVal = ref("");
|
||||
const crumbsContainer = ref(null);
|
||||
const showWin = ref(false);
|
||||
const searchVal = ref('')
|
||||
const crumbsContainer = ref(null)
|
||||
const showWin = ref(false)
|
||||
const currentMembers = ref([])
|
||||
|
||||
const getAllCheckedNodes = (node, checkedNodes = []) => {
|
||||
if (node.checkStatus === 'checked') {
|
||||
checkedNodes.push(node);
|
||||
checkedNodes.push(node)
|
||||
}
|
||||
if (node.sons && Array.isArray(node.sons)) {
|
||||
node.sons.forEach(son => getAllCheckedNodes(son, checkedNodes));
|
||||
node.sons.forEach((son) => getAllCheckedNodes(son, checkedNodes))
|
||||
}
|
||||
return checkedNodes;
|
||||
};
|
||||
return checkedNodes
|
||||
}
|
||||
|
||||
const userDepIds = computed(() => {
|
||||
return userStore.deps.map(v=>v.dept_id);
|
||||
});
|
||||
return userStore.deps.map((v) => v.dept_id)
|
||||
})
|
||||
|
||||
const calculateTotalStaffNum = (node) => {
|
||||
let total = node.staffNum || 0;
|
||||
let total = node.staffNum || 0
|
||||
if (node.sons && Array.isArray(node.sons)) {
|
||||
node.sons.forEach(son => {
|
||||
total += calculateTotalStaffNum(son);
|
||||
});
|
||||
node.sons.forEach((son) => {
|
||||
total += calculateTotalStaffNum(son)
|
||||
})
|
||||
}
|
||||
return total;
|
||||
};
|
||||
return total
|
||||
}
|
||||
|
||||
const allCheckedList = computed(() => {
|
||||
const checkedNodes = [];
|
||||
depTreeMyList.value.forEach(node => getAllCheckedNodes(node, checkedNodes));
|
||||
console.log("checkedNodes", checkedNodes);
|
||||
const checkedNodes = []
|
||||
depTreeMyList.value.forEach((node) => getAllCheckedNodes(node, checkedNodes))
|
||||
console.log('checkedNodes', checkedNodes)
|
||||
|
||||
return checkedNodes;
|
||||
return checkedNodes
|
||||
})
|
||||
|
||||
const currentCrumbs = computed(() => {
|
||||
if (crumbs.value[crumbsIndex.value] ) {
|
||||
if (searchVal.value && searchVal.value !== "") {
|
||||
if (crumbs.value[crumbsIndex.value]) {
|
||||
if (searchVal.value && searchVal.value !== '') {
|
||||
let filterSons = crumbs.value[crumbsIndex.value].sons.filter((item) =>
|
||||
item.name.includes(searchVal.value)
|
||||
);
|
||||
item.name.includes(searchVal.value),
|
||||
)
|
||||
return {
|
||||
...crumbs.value[crumbsIndex.value],
|
||||
sons: filterSons,
|
||||
};
|
||||
}
|
||||
console.log("allCheckedList", crumbs.value[crumbsIndex.value]);
|
||||
return crumbs.value[crumbsIndex.value];
|
||||
}
|
||||
console.log('allCheckedList', crumbs.value[crumbsIndex.value])
|
||||
return crumbs.value[crumbsIndex.value]
|
||||
}
|
||||
return {}
|
||||
});
|
||||
})
|
||||
|
||||
const getCurrentMembers = async (depItem) => {
|
||||
const res = await getDepMembers({
|
||||
departmentId:depItem.ID,
|
||||
status:'notactive'
|
||||
departmentId: depItem.ID,
|
||||
status: 'notactive',
|
||||
})
|
||||
if (res.status === 0) {
|
||||
currentMembers.value = res.data.data.length?res.data.data.map(v=>{
|
||||
currentMembers.value = res.data.data.length
|
||||
? res.data.data.map((v) => {
|
||||
return {
|
||||
...v,
|
||||
isMember:true,
|
||||
isMember: true,
|
||||
}
|
||||
}):[]
|
||||
}else{
|
||||
})
|
||||
: []
|
||||
} else {
|
||||
currentMembers.value = []
|
||||
}
|
||||
}
|
||||
@ -359,168 +421,178 @@ const getCurrentMembers = async (depItem) => {
|
||||
const checkMember = (item, val) => {
|
||||
if (val === 'checked') {
|
||||
membersCheckedKeys.value.push(item)
|
||||
}else {
|
||||
membersCheckedKeys.value = membersCheckedKeys.value.filter(v=>v.ID !==item.ID)
|
||||
} else {
|
||||
membersCheckedKeys.value = membersCheckedKeys.value.filter(
|
||||
(v) => v.ID !== item.ID,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => currentCrumbs.value, (newValue, oldValue) => {
|
||||
watch(
|
||||
() => currentCrumbs.value,
|
||||
(newValue, oldValue) => {
|
||||
getCurrentMembers(newValue)
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
const allCheckStatus = computed(() => {
|
||||
if (!currentCrumbs.value.sons) {
|
||||
return "noChecked";
|
||||
return 'noChecked'
|
||||
}
|
||||
const allChecked = currentCrumbs.value.sons.every((son) => son.checkStatus === "checked");
|
||||
const someChecked =currentCrumbs.value.sons.some(
|
||||
(son) =>
|
||||
son.checkStatus === "checked" || son.checkStatus === "halfChecked"
|
||||
);
|
||||
const allChecked = currentCrumbs.value.sons.every(
|
||||
(son) => son.checkStatus === 'checked',
|
||||
)
|
||||
const someChecked = currentCrumbs.value.sons.some(
|
||||
(son) => son.checkStatus === 'checked' || son.checkStatus === 'halfChecked',
|
||||
)
|
||||
if (allChecked) {
|
||||
return "checked";
|
||||
return 'checked'
|
||||
} else if (someChecked) {
|
||||
return "halfChecked";
|
||||
return 'halfChecked'
|
||||
} else {
|
||||
return "noChecked";
|
||||
return 'noChecked'
|
||||
}
|
||||
})
|
||||
|
||||
const findNodeById = (node, targetId) => {
|
||||
if (node.ID === targetId) {
|
||||
return node;
|
||||
return node
|
||||
}
|
||||
if (node.sons && Array.isArray(node.sons)) {
|
||||
for (const son of node.sons) {
|
||||
const found = findNodeById(son, targetId);
|
||||
const found = findNodeById(son, targetId)
|
||||
if (found) {
|
||||
return found;
|
||||
return found
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
return null
|
||||
}
|
||||
|
||||
const findParentNode = (node, targetId) => {
|
||||
if (!node.sons || !Array.isArray(node.sons)) return null;
|
||||
if (!node.sons || !Array.isArray(node.sons)) return null
|
||||
for (const son of node.sons) {
|
||||
if (son.ID === targetId) {
|
||||
return node;
|
||||
return node
|
||||
}
|
||||
const parent = findParentNode(son, targetId);
|
||||
const parent = findParentNode(son, targetId)
|
||||
if (parent) {
|
||||
return parent;
|
||||
return parent
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
return null
|
||||
}
|
||||
|
||||
// 检查并更新父节点的状态
|
||||
const updateParentStatus = (node) => {
|
||||
const parent = findParentNode(depTreeMyList.value[0], node.ID);
|
||||
if (!parent) return;
|
||||
if (parent.checkStatus !== "checked"){
|
||||
const parent = findParentNode(depTreeMyList.value[0], node.ID)
|
||||
if (!parent) return
|
||||
if (parent.checkStatus !== 'checked') {
|
||||
const allChecked =
|
||||
parent.sons &&
|
||||
Array.isArray(parent.sons) &&
|
||||
parent.sons.every((son) => son.checkStatus === "checked");
|
||||
parent.sons.every((son) => son.checkStatus === 'checked')
|
||||
const someChecked =
|
||||
parent.sons &&
|
||||
Array.isArray(parent.sons) &&
|
||||
parent.sons.some(
|
||||
(son) =>
|
||||
son.checkStatus === "checked" || son.checkStatus === "halfChecked"
|
||||
);
|
||||
son.checkStatus === 'checked' || son.checkStatus === 'halfChecked',
|
||||
)
|
||||
|
||||
if (allChecked) {
|
||||
parent.checkStatus = "halfChecked";
|
||||
parent.checkStatus = 'halfChecked'
|
||||
} else if (someChecked) {
|
||||
parent.checkStatus = "halfChecked";
|
||||
parent.checkStatus = 'halfChecked'
|
||||
} else {
|
||||
parent.checkStatus = "noChecked";
|
||||
parent.checkStatus = 'noChecked'
|
||||
}
|
||||
}
|
||||
|
||||
updateParentStatus(parent);
|
||||
};
|
||||
updateParentStatus(parent)
|
||||
}
|
||||
//根据子节点状态更新当前节点状态
|
||||
const updateNodeStatus = (node) => {
|
||||
if (!node.sons || !Array.isArray(node.sons)) return;
|
||||
const allChecked = node.sons.every(son => son.checkStatus === 'checked');
|
||||
const someChecked = node.sons.some(son => son.checkStatus === 'checked' || son.checkStatus === 'halfChecked');
|
||||
if (!node.sons || !Array.isArray(node.sons)) return
|
||||
const allChecked = node.sons.every((son) => son.checkStatus === 'checked')
|
||||
const someChecked = node.sons.some(
|
||||
(son) => son.checkStatus === 'checked' || son.checkStatus === 'halfChecked',
|
||||
)
|
||||
|
||||
if (allChecked) {
|
||||
node.checkStatus = 'halfChecked';
|
||||
node.checkStatus = 'halfChecked'
|
||||
} else if (someChecked) {
|
||||
node.checkStatus = 'halfChecked';
|
||||
node.checkStatus = 'halfChecked'
|
||||
} else {
|
||||
node.checkStatus = 'noChecked';
|
||||
node.checkStatus = 'noChecked'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// 更新当前节点及其所有子节点的状态
|
||||
const updateCheckStatus = (node, status) => {
|
||||
node.checkStatus = status;
|
||||
node.checkStatus = status
|
||||
if (node.sons && Array.isArray(node.sons) && node.sons.length > 0) {
|
||||
node.sons.forEach((son) => updateCheckStatus(son, status));
|
||||
node.sons.forEach((son) => updateCheckStatus(son, status))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const checkItemChange = (item, val) => {
|
||||
// 更新当前节点及其子节点的状态
|
||||
updateCheckStatus(item, val);
|
||||
updateCheckStatus(item, val)
|
||||
|
||||
// 更新父节点的状态
|
||||
updateParentStatus(item);
|
||||
};
|
||||
updateParentStatus(item)
|
||||
}
|
||||
|
||||
const deleteNode = (item) => {
|
||||
const treeNode = findNodeById(depTreeMyList.value[0], item.ID);
|
||||
if (!treeNode) return;
|
||||
treeNode.checkStatus = "noChecked";
|
||||
updateNodeStatus(treeNode);
|
||||
updateParentStatus(treeNode);
|
||||
};
|
||||
const treeNode = findNodeById(depTreeMyList.value[0], item.ID)
|
||||
if (!treeNode) return
|
||||
treeNode.checkStatus = 'noChecked'
|
||||
updateNodeStatus(treeNode)
|
||||
updateParentStatus(treeNode)
|
||||
}
|
||||
|
||||
const deleteMember = (item) => {
|
||||
membersCheckedKeys.value = membersCheckedKeys.value.filter(v=>v.ID !== item.ID)
|
||||
membersCheckedKeys.value = membersCheckedKeys.value.filter(
|
||||
(v) => v.ID !== item.ID,
|
||||
)
|
||||
}
|
||||
|
||||
const toNextLevel = async (item) => {
|
||||
if (item.checkStatus !== "checked") {
|
||||
crumbs.value.push(item);
|
||||
crumbsIndex.value++;
|
||||
await nextTick();
|
||||
if (item.checkStatus !== 'checked') {
|
||||
crumbs.value.push(item)
|
||||
crumbsIndex.value++
|
||||
await nextTick()
|
||||
if (crumbsContainer.value) {
|
||||
crumbsContainer.value.scrollLeft = crumbsContainer.value.scrollWidth;
|
||||
crumbsContainer.value.scrollLeft = crumbsContainer.value.scrollWidth
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const handleCrumbsClick = (index) => {
|
||||
crumbsIndex.value = index;
|
||||
crumbs.value = crumbs.value.slice(0, index + 1);
|
||||
};
|
||||
crumbsIndex.value = index
|
||||
crumbs.value = crumbs.value.slice(0, index + 1)
|
||||
}
|
||||
|
||||
const allCheck = (status) => {
|
||||
let statusT = 'noChecked';
|
||||
if (status === "checked") {
|
||||
statusT = "noChecked";
|
||||
let statusT = 'noChecked'
|
||||
if (status === 'checked') {
|
||||
statusT = 'noChecked'
|
||||
} else {
|
||||
statusT = "checked";
|
||||
statusT = 'checked'
|
||||
}
|
||||
currentCrumbs.value.sons.forEach((item) => {
|
||||
const itemT = findNodeById(depTreeMyList.value[0], item.ID)
|
||||
if (!itemT) return;
|
||||
checkItemChange(itemT, statusT);
|
||||
});
|
||||
};
|
||||
if (!itemT) return
|
||||
checkItemChange(itemT, statusT)
|
||||
})
|
||||
}
|
||||
|
||||
const openDrawer = () => {
|
||||
showWin.value = true;
|
||||
if (allCheckedList.length>0) {
|
||||
showWin.value = true
|
||||
if (allCheckedList.length > 0) {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// watch(() => depTreeMyList.value, (newValue, oldValue) => {
|
||||
// console.log("depTreeMyList", newValue);
|
||||
@ -531,59 +603,62 @@ const openDrawer = () => {
|
||||
// console.log("searchVal", newValue);
|
||||
// });
|
||||
|
||||
|
||||
const getDepTotalMembers = (item) => {
|
||||
const rootNode = depTreeMyList.value[0];
|
||||
const targetNode = findNodeById(rootNode, item.ID);
|
||||
const rootNode = depTreeMyList.value[0]
|
||||
const targetNode = findNodeById(rootNode, item.ID)
|
||||
if (targetNode) {
|
||||
return calculateTotalStaffNum(targetNode);
|
||||
return calculateTotalStaffNum(targetNode)
|
||||
}
|
||||
return 0;
|
||||
return 0
|
||||
}
|
||||
|
||||
const totalMembers = computed(() => {
|
||||
const depMembers = allCheckedList.value.reduce((sum, item) => sum + item.staffNum, 0);
|
||||
const memberCount = membersCheckedKeys.value.length;
|
||||
return depMembers + memberCount;
|
||||
const depMembers = allCheckedList.value.reduce(
|
||||
(sum, item) => sum + item.staffNum,
|
||||
0,
|
||||
)
|
||||
const memberCount = membersCheckedKeys.value.length
|
||||
return depMembers + memberCount
|
||||
})
|
||||
|
||||
const handleConfirm = async () => {
|
||||
uni.showLoading()
|
||||
const listT = membersCheckedKeys.value.map(v=>v)
|
||||
const listT = membersCheckedKeys.value.map((v) => v)
|
||||
const res = await getDepMembers({
|
||||
departmentIds:allCheckedList.value.map(v=>v.ID),
|
||||
status:'notactive',
|
||||
departmentIds: allCheckedList.value.map((v) => v.ID),
|
||||
status: 'notactive',
|
||||
})
|
||||
if (res.status == 0 && res.data?.data?.length) {
|
||||
res.data?.data.forEach(v=>{
|
||||
res.data?.data.forEach((v) => {
|
||||
listT.push(v)
|
||||
})
|
||||
}
|
||||
allChooseMembers.value = listT
|
||||
depCheckedKeys.value = lodash.cloneDeep(allCheckedList.value)
|
||||
uni.hideLoading()
|
||||
uni.navigateBack();
|
||||
uni.navigateBack()
|
||||
}
|
||||
|
||||
const initCheckedKeys = () => {
|
||||
depCheckedKeys.value.forEach((item) => {
|
||||
const node = findNodeById(depTreeMyList.value[0], item.ID);
|
||||
const node = findNodeById(depTreeMyList.value[0], item.ID)
|
||||
if (node) {
|
||||
node.checkStatus = "checked";
|
||||
updateParentStatus(node);
|
||||
node.checkStatus = 'checked'
|
||||
updateParentStatus(node)
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
const init = async () => {
|
||||
crumbsIndex.value = 0;
|
||||
await getDepsTreeMy();
|
||||
crumbs.value = depTreeMyList.value.length ? [depTreeMyList.value[0]] : [];
|
||||
initCheckedKeys();
|
||||
};
|
||||
crumbsIndex.value = 0
|
||||
await getDepsTreeMy()
|
||||
crumbs.value = depTreeMyList.value.length ? [depTreeMyList.value[0]] : []
|
||||
initCheckedKeys()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
init()
|
||||
})
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
uni-page-body,
|
||||
@ -593,7 +668,7 @@ page {
|
||||
.outer-layer {
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
background-image: url("@/static/image/clockIn/z3280@3x.png");
|
||||
background-image: url('@/static/image/clockIn/z3280@3x.png');
|
||||
background-size: cover;
|
||||
padding-bottom: 0;
|
||||
display: flex;
|
||||
@ -633,22 +708,22 @@ page {
|
||||
background-repeat: no-repeat;
|
||||
border-radius: 12rpx;
|
||||
&.firstPanel {
|
||||
background-image: url("@/static/image/chatList/zu6033@2x.png");
|
||||
background-image: url('@/static/image/chatList/zu6033@2x.png');
|
||||
}
|
||||
&.secondPanel {
|
||||
background-image: url("@/static/image/chatList/zu6031@2x.png");
|
||||
background-image: url('@/static/image/chatList/zu6031@2x.png');
|
||||
margin-top: 28rpx;
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
&.thirdPanel {
|
||||
background-image: url("@/static/image/chatList/zu6032@2x.png");
|
||||
background-image: url('@/static/image/chatList/zu6032@2x.png');
|
||||
}
|
||||
&.activePanel {
|
||||
box-shadow: 0 0 0 3rpx #46299d;
|
||||
}
|
||||
}
|
||||
.btnBox {
|
||||
:deep(uni-button[disabled="true"]) {
|
||||
:deep(uni-button[disabled='true']) {
|
||||
color: #bebebe !important;
|
||||
}
|
||||
}
|
||||
@ -663,17 +738,17 @@ page {
|
||||
.diyBtn {
|
||||
:deep(uni-button) {
|
||||
color: #191919 !important;
|
||||
border: 1rpx solid #D6D6D8 !important;
|
||||
background-color: #FFFFFF !important;
|
||||
&[disabled="true"] {
|
||||
color: #BEBEBE !important;
|
||||
border: 1rpx solid #E6E6E6 !important;
|
||||
background-color: #E6E6E6 !important;
|
||||
border: 1rpx solid #d6d6d8 !important;
|
||||
background-color: #ffffff !important;
|
||||
&[disabled='true'] {
|
||||
color: #bebebe !important;
|
||||
border: 1rpx solid #e6e6e6 !important;
|
||||
background-color: #e6e6e6 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.userAvatar{
|
||||
background: linear-gradient(#674BBC, #46299D);
|
||||
.userAvatar {
|
||||
background: linear-gradient(#674bbc, #46299d);
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
border-radius: 36rpx;
|
||||
@ -681,12 +756,12 @@ page {
|
||||
font-size: 24rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
.postTag{
|
||||
background-color: #EEE9F8;
|
||||
.postTag {
|
||||
background-color: #eee9f8;
|
||||
height: 32rpx;
|
||||
line-height: 32rpx;
|
||||
font-size: 20rpx;
|
||||
padding: 0 12rpx 0 12rpx;
|
||||
color: #46299D;
|
||||
color: #46299d;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,29 +1,26 @@
|
||||
<template>
|
||||
<div class="outer-layer">
|
||||
<div>
|
||||
<tm-navbar :hideBack="false" hideHome title="发起群聊" :leftWidth="320">
|
||||
</tm-navbar>
|
||||
</div>
|
||||
<div class="root">
|
||||
<div class="w-full pl-[32rpx] pr-[32rpx] mb-[20rpx]" >
|
||||
<div class="w-full mt-[60rpx] flex justify-center" >
|
||||
<div v-if="groupActiveIndex === -1" class="avatar-placeholder">
|
||||
|
||||
</div>
|
||||
<div class="mb-[40rpx]" v-else>
|
||||
<tm-image
|
||||
:width="192"
|
||||
:height="192"
|
||||
:round="12"
|
||||
:src="avatarImg"
|
||||
></tm-image>
|
||||
<div class="create-group-chat-page">
|
||||
<zPaging ref="zPaging" :show-scrollbar="false">
|
||||
<template #top>
|
||||
<customNavbar :title="$t('pageTitle.create.group')"></customNavbar>
|
||||
</template>
|
||||
<div class="create-group-chat flex flex-col">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group flex items-center justify-between" >
|
||||
<div class="input-group flex items-center justify-between">
|
||||
<div class="input-item">
|
||||
群名称
|
||||
</div>
|
||||
<div class="input-box" >
|
||||
<div class="input-box">
|
||||
<tm-input
|
||||
v-model="groupName"
|
||||
:followTheme="false"
|
||||
@ -35,274 +32,350 @@
|
||||
:height="40"
|
||||
:transprent="true"
|
||||
placeholder="请输入群名称(1~20个字)"
|
||||
:padding="[0,0]"
|
||||
:padding="[0, 0]"
|
||||
align="right"
|
||||
></tm-input>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="input-group w-full flex flex-col mt-[20rpx] leading-[40rpx]"
|
||||
>
|
||||
</tm-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group w-full flex flex-col mt-[20rpx] leading-[40rpx]" >
|
||||
<div class="flex items-center justify-between" >
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="input-item">
|
||||
群类型
|
||||
</div>
|
||||
<div @click="chooseGroupType" class="left-box" >
|
||||
<div class="text-[#B4B4B4] text-[28rpx] font-bold" >
|
||||
<span v-if="groupActiveIndex ===-1">请选择群类型</span>
|
||||
<span v-else-if="groupActiveIndex ===0">普通群</span>
|
||||
<span v-else-if="groupActiveIndex ===1">部门群</span>
|
||||
<span v-else-if="groupActiveIndex ===2">项目群</span>
|
||||
<div @click="chooseGroupType" class="left-box">
|
||||
<div class="text-[#B4B4B4] text-[28rpx] font-bold">
|
||||
<span v-if="groupActiveIndex === -1">请选择群类型</span>
|
||||
<span v-else-if="groupActiveIndex === 0">普通群</span>
|
||||
<span v-else-if="groupActiveIndex === 1">部门群</span>
|
||||
<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>
|
||||
<div class="ml-[32rpx]">
|
||||
<tm-icon
|
||||
:font-size="22"
|
||||
color="#747474"
|
||||
name="tmicon-angle-right"
|
||||
></tm-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="depCheckedKeys.length" class="mt-[32rpx]" >
|
||||
<div
|
||||
v-for="(item,index) in depCheckedKeys"
|
||||
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&&index>4 ? 'hidden':''
|
||||
index !== 0 ? 'mt-[10rpx]' : '',
|
||||
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 = !depsNoExpanded" class="w-[100rpx]">
|
||||
{{ depsNoExpanded? '展开':'收起' }}
|
||||
<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" class="input-group w-full flex flex-col mt-[20rpx] leading-[40rpx]" >
|
||||
<div class="flex items-center justify-between" >
|
||||
<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 v-if="!groupAdmins.length" class="text-[#B4B4B4] text-[28rpx] font-bold mr-[32rpx]">全部({{ 0 }})</div>
|
||||
<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 @click="chooseMembers" class="left-box">
|
||||
<div class="ml-[32rpx] flex items-center">
|
||||
<div
|
||||
v-for="(item,index) in groupAdmins"
|
||||
class="text-[#747474] text-[28rpx] leading-[40rpx] font-bold"
|
||||
:class="[
|
||||
index !==0 ? 'mt-[10rpx]':'',
|
||||
depsNoExpanded&&index>4 ? 'hidden':''
|
||||
]"
|
||||
v-if="!groupAdmins.length"
|
||||
class="text-[#B4B4B4] text-[28rpx] font-bold mr-[32rpx]"
|
||||
>
|
||||
{{ item.name }}
|
||||
全部({{ allChooseMembers?.length || 0 }})
|
||||
</div>
|
||||
<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? '展开':'收起' }}
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<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
|
||||
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>
|
||||
<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 @click="chooseGroupAdmin" class="left-box">
|
||||
<div class="ml-[32rpx] flex items-center">
|
||||
<div
|
||||
v-for="(item,index) in groupAdmins"
|
||||
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>
|
||||
</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"
|
||||
:class="[
|
||||
index !==0 ? 'mt-[10rpx]':'',
|
||||
depsNoExpanded&&index>4 ? 'hidden':''
|
||||
index !== 0 ? 'mt-[10rpx]' : '',
|
||||
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 = !depsNoExpanded" class="w-[100rpx]">
|
||||
{{ depsNoExpanded? '展开':'收起' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="h-[162rpx] flex justify-center bg-[#FFFFFF]">
|
||||
<div class="mt-[14rpx] btnBox" >
|
||||
<tm-button
|
||||
@click="handleConfirm"
|
||||
color="#46299D"
|
||||
:disabled="confirmBtnStatus"
|
||||
disabledColor="#E6E6E6"
|
||||
:margin="[0]"
|
||||
:shadow="0"
|
||||
:width="426"
|
||||
:height="76"
|
||||
size="large"
|
||||
label="发起群聊"
|
||||
<div
|
||||
class="text-[#46299D] text-[28rpx] mt-[20rpx] font-bold flex justify-center"
|
||||
>
|
||||
</tm-button>
|
||||
<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"
|
||||
:disabled="confirmBtnStatus"
|
||||
></customBtn>
|
||||
</template>
|
||||
</zPaging>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
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 } from "@/store";
|
||||
import addCircle from "@/static/image/chatList/addCircle.png";
|
||||
import cahtPopover from "@/static/image/chatList/cahtPopover.png";
|
||||
import zu4992 from "@/static/image/chatList/zu4992@2x.png";
|
||||
import zu4991 from "@/static/image/chatList/zu4991@2x.png";
|
||||
import zu4989 from "@/static/image/chatList/zu4989@2x.png";
|
||||
import { useGroupTypeStore } from "@/store/groupType";
|
||||
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'
|
||||
|
||||
const { groupName,groupActiveIndex,depCheckedKeys,groupAdmins,createDepGroup,resetGroupInfo } = useGroupTypeStore();
|
||||
const talkStore = useTalkStore();
|
||||
const userStore = useUserStore();
|
||||
const { userInfo } = useAuth();
|
||||
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 groupChatType = ref('');
|
||||
const depsNoExpanded = ref(true);
|
||||
const {
|
||||
groupName,
|
||||
groupActiveIndex,
|
||||
depCheckedKeys,
|
||||
groupAdmins,
|
||||
createDepGroup,
|
||||
resetGroupInfo,
|
||||
allChooseMembers,
|
||||
} = useGroupTypeStore()
|
||||
const talkStore = useTalkStore()
|
||||
const userStore = useUserStore()
|
||||
const groupStore = useGroupStore()
|
||||
const { userInfo } = useAuth()
|
||||
|
||||
const avatarImg = computed(() => {
|
||||
let srcT = "";
|
||||
switch (groupActiveIndex.value) {
|
||||
case 0:
|
||||
srcT = zu4992;
|
||||
break;
|
||||
case 1:
|
||||
srcT = zu4989;
|
||||
break;
|
||||
case 2:
|
||||
srcT = zu4991;
|
||||
break;
|
||||
default:
|
||||
srcT = zu4992;
|
||||
}
|
||||
return srcT;
|
||||
const groupChatType = ref('')
|
||||
const depsNoExpanded = ref(true)
|
||||
|
||||
onLoad(()=> {
|
||||
groupStore.$reset()
|
||||
})
|
||||
|
||||
//群类型
|
||||
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'
|
||||
url: '/pages/chooseGroupType/index',
|
||||
})
|
||||
}
|
||||
|
||||
const chooseGroupAdmin = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/chooseGroupAdmin/index'
|
||||
url:
|
||||
'/pages/chatSettings/groupManage/selectMembers?manageType=admin&isCreateDepGroup=1',
|
||||
})
|
||||
}
|
||||
|
||||
const chooseMembers = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/chooseMembers/index'
|
||||
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) {
|
||||
|
||||
}else if(groupActiveIndex.value === 1){
|
||||
//普通群
|
||||
let params = {
|
||||
avatar: '',
|
||||
name: groupName.value,
|
||||
erp_ids: erp_ids,
|
||||
type: 1,
|
||||
profile: '',
|
||||
}
|
||||
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 {
|
||||
|
||||
} 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(() => {
|
||||
let disabledT = false;
|
||||
if (groupName.value === "" || !groupActiveIndex.value) {
|
||||
return true;
|
||||
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;
|
||||
break
|
||||
case 1:
|
||||
if (!depCheckedKeys.value.length) {
|
||||
disabledT = true;
|
||||
disabledT = true
|
||||
}
|
||||
if (!groupAdmins.value.length) {
|
||||
disabledT = true;
|
||||
disabledT = true
|
||||
}
|
||||
break;
|
||||
break
|
||||
case 2:
|
||||
break;
|
||||
break
|
||||
default:
|
||||
break;
|
||||
break
|
||||
}
|
||||
return disabledT
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
depsNoExpanded.value = true;
|
||||
depsNoExpanded.value = true
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
uni-page-body,
|
||||
page {
|
||||
::v-deep .zp-paging-container-content {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
}
|
||||
.outer-layer {
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
background-image: url("@/static/image/clockIn/z3280@3x.png");
|
||||
|
||||
.create-group-chat {
|
||||
background-image: url('@/static/image/clockIn/z3280@3x.png');
|
||||
background-size: cover;
|
||||
padding-bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.root {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.divider{
|
||||
height: 1rpx;
|
||||
background-color: #7C7C7C;
|
||||
opacity: 0.6;
|
||||
}
|
||||
.avatar-placeholder {
|
||||
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%;
|
||||
margin-bottom: 40rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
.divider {
|
||||
height: 1rpx;
|
||||
background-color: #7c7c7c;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
background-color: #fff;
|
||||
padding-top: 38rpx;
|
||||
padding-bottom: 32rpx;
|
||||
padding-left: 32rpx;
|
||||
padding-right: 40rpx;
|
||||
// display: flex;
|
||||
// align-items: center;
|
||||
// justify-content: space-between;
|
||||
padding: 38rpx 40rpx 32rpx 32rpx;
|
||||
}
|
||||
.input-item {
|
||||
line-height: 40rpx;
|
||||
@ -320,9 +393,4 @@ page {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.btnBox {
|
||||
:deep(uni-button[disabled="true"]) {
|
||||
color: #bebebe !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -3,13 +3,7 @@
|
||||
<div class="root">
|
||||
<ZPaging ref="zPaging" :show-scrollbar="false">
|
||||
<template #top>
|
||||
<tm-navbar
|
||||
class="tmNavBar"
|
||||
:hideBack="false"
|
||||
hideHome
|
||||
title=""
|
||||
:leftWidth="220"
|
||||
></tm-navbar>
|
||||
<customNavbar class="tmNavBar"></customNavbar>
|
||||
</template>
|
||||
<div class="user-detail-info">
|
||||
<div class="user-info-head user-info-card">
|
||||
@ -66,6 +60,7 @@
|
||||
:isBottom="true"
|
||||
:btnText="$t('user.detail.sendMsg')"
|
||||
:subBtnText="$t('user.detail.ringBell')"
|
||||
@clickBtn="toTalkUser"
|
||||
></customBtn>
|
||||
</template>
|
||||
</ZPaging>
|
||||
@ -78,6 +73,9 @@ 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 } from '@/store'
|
||||
const talkStore = useTalkStore()
|
||||
|
||||
import { getUserInfoByClickAvatar } from '@/api/user/index'
|
||||
|
||||
import { useI18n } from 'vue-i18n'
|
||||
@ -92,7 +90,7 @@ const state = reactive({
|
||||
onLoad((options) => {
|
||||
console.log(options)
|
||||
if (options.erpUserId) {
|
||||
state.erpUserId = options.erpUserId
|
||||
state.erpUserId = Number(options.erpUserId)
|
||||
getUserInfo()
|
||||
}
|
||||
})
|
||||
@ -100,7 +98,7 @@ onLoad((options) => {
|
||||
//获取用户信息
|
||||
const getUserInfo = () => {
|
||||
let params = {
|
||||
erp_user_id: Number(state.erpUserId),
|
||||
erp_user_id: state.erpUserId,
|
||||
}
|
||||
console.log(params)
|
||||
const resp = getUserInfoByClickAvatar(params)
|
||||
@ -172,6 +170,11 @@ const getUserInfo = () => {
|
||||
|
||||
resp.catch(() => {})
|
||||
}
|
||||
|
||||
//点击对用户发起单聊
|
||||
const toTalkUser = () => {
|
||||
talkStore.toTalk(1, state.userInfo.sys_id, state.erpUserId)
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.outer-layer {
|
||||
|
@ -1,25 +1,41 @@
|
||||
<template>
|
||||
<div class="outer-layer">
|
||||
<div>
|
||||
<tm-navbar :hideBack="false" hideHome title="" :leftWidth="220">
|
||||
<div class="flex flex-col items-center justify-center">
|
||||
<div class="text-[34rpx] font-bold">{{ talkParams.username }}</div>
|
||||
<div v-if="true" class="text-[24rpx] text-[#999999]">公司群</div>
|
||||
</div>
|
||||
<template v-slot:right>
|
||||
<div class="mr-[36rpx]">
|
||||
<tm-icon color="rgb(51, 51, 51)" :font-size="36" name="tmicon-gengduo" @click="toChatSettingsPage"></tm-icon>
|
||||
<div class="dialog-page">
|
||||
<ZPaging
|
||||
use-chat-record-mode
|
||||
:refresher-enabled="false"
|
||||
:show-scrollbar="false"
|
||||
:loading-more-enabled="false"
|
||||
:hide-empty-view="true"
|
||||
height="100%"
|
||||
ref="zpagingRef"
|
||||
:use-virtual-list="true"
|
||||
:preload-page="1"
|
||||
cell-height-mode="dynamic"
|
||||
virtual-scroll-fps="80"
|
||||
:loading-more-custom-style="{ display: 'none', height: '0' }"
|
||||
@virtualListChange="virtualListChange"
|
||||
@scrolltolower="onRefreshLoad"
|
||||
>
|
||||
<template #top>
|
||||
<customNavbar :title="talkParams.username">
|
||||
<template
|
||||
#subTitle
|
||||
v-if="talkStore?.findItem(talkParams.index_name)?.group_type === 4"
|
||||
>
|
||||
<div class="text-[24rpx] text-[#999999]">公司群</div>
|
||||
</template>
|
||||
<template #right>
|
||||
<div class="mr-[36rpx] toChatSetting_btn">
|
||||
<tm-icon
|
||||
color="rgb(51, 51, 51)"
|
||||
:font-size="36"
|
||||
name="tmicon-gengduo"
|
||||
@click="toChatSettingsPage"
|
||||
></tm-icon>
|
||||
</div>
|
||||
</template>
|
||||
</tm-navbar>
|
||||
</div>
|
||||
<div class="root">
|
||||
<div class="dialogBox">
|
||||
<ZPaging :fixed="false" use-chat-record-mode :use-page-scroll="false" :refresher-enabled="false"
|
||||
:show-scrollbar="false" :loading-more-enabled="false" :hide-empty-view="true" height="100%" ref="zpagingRef"
|
||||
:use-virtual-list="true" :preload-page="1" cell-height-mode="dynamic" virtual-scroll-fps="80"
|
||||
:loading-more-custom-style="{ display: 'none', height: '0' }" @virtualListChange="virtualListChange"
|
||||
@scrolltolower="onRefreshLoad">
|
||||
</customNavbar>
|
||||
</template>
|
||||
<!-- <template #top>
|
||||
<div class="load-toolbar pointer">
|
||||
<span v-if="loadConfig.status == 0"> 正在加载数据中 ... </span>
|
||||
@ -29,55 +45,102 @@
|
||||
</template> -->
|
||||
|
||||
<!-- 数据加载状态栏 -->
|
||||
<div class="message-item" v-for="item in virtualList" :id="`zp-id-${item.zp_index}`" :key="item.zp_index"
|
||||
style="transform: scaleY(-1);">
|
||||
<div class="dialog-list">
|
||||
<div
|
||||
class="message-item"
|
||||
v-for="item in virtualList"
|
||||
:id="`zp-id-${item.zp_index}`"
|
||||
:key="item.zp_index"
|
||||
style="transform: scaleY(-1);"
|
||||
>
|
||||
<!-- 系统消息 -->
|
||||
<div v-if="item.msg_type >= 1000" class="message-box">
|
||||
<component :is="MessageComponents[item.msg_type] || 'unknown-message'" :extra="item.extra" :data="item" />
|
||||
<component
|
||||
:is="MessageComponents[item.msg_type] || 'unknown-message'"
|
||||
:extra="item.extra"
|
||||
:data="item"
|
||||
/>
|
||||
</div>
|
||||
<!-- 撤回消息 -->
|
||||
<div v-else-if="item.is_revoke == 1" class="message-box">
|
||||
<revoke-message :login_uid="userStore.uid" :user_id="item.user_id" :nickname="item.nickname"
|
||||
:talk_type="item.talk_type" :datetime="item.created_at" />
|
||||
<revoke-message
|
||||
:login_uid="userStore.uid"
|
||||
:user_id="item.user_id"
|
||||
:nickname="item.nickname"
|
||||
:talk_type="item.talk_type"
|
||||
:datetime="item.created_at"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-else class="message-box record-box" :class="{
|
||||
<div
|
||||
v-else
|
||||
class="message-box record-box"
|
||||
:class="{
|
||||
'direction-rt': item.float == 'right',
|
||||
'multi-select': dialogueStore.isOpenMultiSelect,
|
||||
'multi-select-check': item.isCheck
|
||||
}">
|
||||
'multi-select-check': item.isCheck,
|
||||
}"
|
||||
>
|
||||
<!-- 多选按钮 -->
|
||||
<aside v-if="dialogueStore.isOpenMultiSelect" class="checkbox-column">
|
||||
<aside
|
||||
v-if="dialogueStore.isOpenMultiSelect"
|
||||
class="checkbox-column"
|
||||
>
|
||||
<!-- <n-checkbox size="small" :checked="item.isCheck" @update:checked="item.isCheck = !item.isCheck" /> -->
|
||||
<tm-checkbox :round="10" :defaultChecked="item.isCheck"
|
||||
@update:modelValue="item.isCheck = !item.isCheck" :size="42" color="#46299D"></tm-checkbox>
|
||||
<tm-checkbox
|
||||
:round="10"
|
||||
:defaultChecked="item.isCheck"
|
||||
@update:modelValue="item.isCheck = !item.isCheck"
|
||||
:size="42"
|
||||
color="#46299D"
|
||||
></tm-checkbox>
|
||||
</aside>
|
||||
|
||||
<!-- 头像信息 -->
|
||||
<aside class="avatar-column" @click="toUserDetailPage(item)">
|
||||
<im-avatar class="pointer" :src="item.avatar" :size="80" :username="item.nickname"
|
||||
@click="showUserInfoModal(item.user_id)" />
|
||||
<im-avatar
|
||||
class="pointer"
|
||||
:src="item.avatar"
|
||||
:size="80"
|
||||
:username="item.nickname"
|
||||
@click="showUserInfoModal(item.user_id)"
|
||||
/>
|
||||
</aside>
|
||||
|
||||
<!-- 主体信息 -->
|
||||
<main class="main-column">
|
||||
<div class="talk-title">
|
||||
<span class="nickname pointer" v-show="talkParams.type == 2 && item.float == 'left'"
|
||||
@click="onClickNickname(item)">
|
||||
<span class="at">@</span>{{ item.nickname }}
|
||||
<span
|
||||
class="nickname pointer"
|
||||
v-show="talkParams.type == 2 && item.float == 'left'"
|
||||
@click="onClickNickname(item)"
|
||||
>
|
||||
<span class="at">@</span>
|
||||
{{ item.nickname }}
|
||||
</span>
|
||||
<span>
|
||||
{{ parseTime(item.created_at, '{m}/{d} {h}:{i}') }}
|
||||
</span>
|
||||
<span>{{ parseTime(item.created_at, '{m}/{d} {h}:{i}') }}</span>
|
||||
</div>
|
||||
|
||||
<div class="talk-content" :class="{ pointer: dialogueStore.isOpenMultiSelect }">
|
||||
<div
|
||||
class="talk-content"
|
||||
:class="{ pointer: dialogueStore.isOpenMultiSelect }"
|
||||
>
|
||||
<deepBubble
|
||||
@clickMenu="(menuType) => onContextMenu(menuType, item)"
|
||||
:isShowCopy="isShowCopy(item)"
|
||||
:isShowWithdraw="isRevoke(talkParams.uid,item)"
|
||||
:isShowWithdraw="isRevoke(talkParams.uid, item)"
|
||||
>
|
||||
<component class="component-content" :key="item.zp_index"
|
||||
:is="MessageComponents[item.msg_type] || 'unknown-message'" :extra="item.extra" :data="item"
|
||||
:max-width="true" :source="'panel'" />
|
||||
<component
|
||||
class="component-content"
|
||||
:key="item.zp_index"
|
||||
:is="MessageComponents[item.msg_type] || 'unknown-message'"
|
||||
:extra="item.extra"
|
||||
:data="item"
|
||||
:max-width="true"
|
||||
:source="'panel'"
|
||||
/>
|
||||
</deepBubble>
|
||||
<!-- <div class="talk-tools">
|
||||
<template v-if="talkParams.type == 1 && item.float == 'right'">
|
||||
@ -91,8 +154,11 @@
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<div v-if="item.extra.reply" class="talk-reply pointer"
|
||||
@click="onJumpMessage(item.extra?.reply?.msg_id)">
|
||||
<div
|
||||
v-if="item.extra.reply"
|
||||
class="talk-reply pointer"
|
||||
@click="onJumpMessage(item.extra?.reply?.msg_id)"
|
||||
>
|
||||
<!-- <n-icon :component="ToTop" size="14" class="icon-top" /> -->
|
||||
<span class="ellipsis">
|
||||
回复 {{ item.extra?.reply?.nickname }}:
|
||||
@ -101,60 +167,104 @@
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="load-toolbar pointer" style="transform: scaleY(-1);">
|
||||
<span v-if="loadConfig.status == 0"> 正在加载数据中 ... </span>
|
||||
<span v-else-if="loadConfig.status == 1" @click="onRefreshLoad"> 查看更多消息 ... </span>
|
||||
<span v-else class="no-more"> 没有更多消息了 </span>
|
||||
</div>
|
||||
|
||||
</ZPaging>
|
||||
<span v-if="loadConfig.status == 0">正在加载数据中 ...</span>
|
||||
<span v-else-if="loadConfig.status == 1" @click="onRefreshLoad">
|
||||
查看更多消息 ...
|
||||
</span>
|
||||
<span v-else class="no-more">没有更多消息了</span>
|
||||
</div>
|
||||
</div>
|
||||
<template #bottom>
|
||||
<div class="footBox">
|
||||
<div v-if="!dialogueStore.isOpenMultiSelect">
|
||||
<div class="mt-[16rpx] ml-[32rpx] mr-[32rpx] flex items-center justify-between">
|
||||
<div
|
||||
class="pt-[16rpx] ml-[32rpx] mr-[32rpx] flex items-center justify-between"
|
||||
>
|
||||
<div class="flex-1 quillBox">
|
||||
<QuillEditor ref="editor" id="editor" :options="editorOption" @editorChange="onEditorChange"
|
||||
style="height: 100%; border: none" />
|
||||
<QuillEditor
|
||||
ref="editor"
|
||||
id="editor"
|
||||
:options="editorOption"
|
||||
@editorChange="onEditorChange"
|
||||
style="height: 100%; border: none;"
|
||||
/>
|
||||
<!-- <tm-input type=textarea autoHeight focusColor="#F9F9F9" color="#F9F9F9" :inputPadding="[12]"
|
||||
placeholder=""></tm-input> -->
|
||||
</div>
|
||||
<tm-image :margin="[10, 0]" @click="handleEmojiPanel" :width="52" :height="52" :round="12"
|
||||
:src="state.isOpenEmojiPanel ? keyboard : smile"></tm-image>
|
||||
<tm-image @click="handleFilePanel" :margin="[10, 0]" :width="52" :height="52" :round="12"
|
||||
:src="addCircleGray"></tm-image>
|
||||
<tm-button @click="onSendMessageClick" :margin="[0, 0]" :padding="[0, 30]" color="#46299D" :fontSize="28"
|
||||
size="mini" :shadow="0" label="发送"></tm-button>
|
||||
<tm-image
|
||||
:margin="[10, 0]"
|
||||
@click="handleEmojiPanel"
|
||||
:width="52"
|
||||
:height="52"
|
||||
:round="12"
|
||||
:src="state.isOpenEmojiPanel ? keyboard : smile"
|
||||
></tm-image>
|
||||
<tm-image
|
||||
@click="handleFilePanel"
|
||||
:margin="[10, 0]"
|
||||
:width="52"
|
||||
:height="52"
|
||||
:round="12"
|
||||
:src="addCircleGray"
|
||||
></tm-image>
|
||||
<tm-button
|
||||
@click="onSendMessageClick"
|
||||
:margin="[0, 0]"
|
||||
:padding="[0, 30]"
|
||||
color="#46299D"
|
||||
:fontSize="28"
|
||||
size="mini"
|
||||
:shadow="0"
|
||||
label="发送"
|
||||
></tm-button>
|
||||
</div>
|
||||
<div v-if="state.isOpenEmojiPanel" class="mt-[50rpx]">
|
||||
<emojiPanel @on-select="onEmoticonEvent" />
|
||||
</div>
|
||||
<div v-if="state.isOpenFilePanel" class="mt-[16rpx]">
|
||||
<filePanel @selectImg="handleSelectImg" :talkParams="talkParams" />
|
||||
|
||||
<filePanel
|
||||
@selectImg="handleSelectImg"
|
||||
:talkParams="talkParams"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="h-[232rpx]">
|
||||
<div class="flex items-center justify-center mt-[12rpx] text-[24rpx] text-[#747474] leading-[44rpx]">
|
||||
<div
|
||||
class="flex items-center justify-center mt-[12rpx] text-[24rpx] text-[#747474] leading-[44rpx]"
|
||||
>
|
||||
<div class="mr-[8rpx]">已选中:</div>
|
||||
<div>{{ selectedMessage.length }}条消息</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-around pl-[128rpx] pr-[128rpx] mt-[18rpx] text-[20rpx] text-[#737373] ">
|
||||
<div @click="handleMergeForward" class="flex flex-col items-center justify-center">
|
||||
<div
|
||||
class="flex items-center justify-around pl-[128rpx] pr-[128rpx] mt-[18rpx] text-[20rpx] text-[#737373]"
|
||||
>
|
||||
<div
|
||||
@click="handleMergeForward"
|
||||
class="flex flex-col items-center justify-center"
|
||||
>
|
||||
<tm-image :width="68" :height="68" :src="zu6050"></tm-image>
|
||||
<div class="mt-[6rpx]">合并转发</div>
|
||||
</div>
|
||||
<div @click="handleSingleForward" class="flex flex-col items-center justify-center">
|
||||
<div
|
||||
@click="handleSingleForward"
|
||||
class="flex flex-col items-center justify-center"
|
||||
>
|
||||
<tm-image :width="68" :height="68" :src="zu6051"></tm-image>
|
||||
<div class="mt-[6rpx]">逐条转发</div>
|
||||
</div>
|
||||
<div @click="handleWechatForward" class="flex flex-col items-center justify-center">
|
||||
<div
|
||||
@click="handleWechatForward"
|
||||
class="flex flex-col items-center justify-center"
|
||||
>
|
||||
<tm-image :width="68" :height="68" :src="zu6052"></tm-image>
|
||||
<div class="mt-[6rpx]">微信</div>
|
||||
</div>
|
||||
<div @click="handleDelete" class="flex flex-col items-center justify-center">
|
||||
<div
|
||||
@click="handleDelete"
|
||||
class="flex flex-col items-center justify-center"
|
||||
>
|
||||
<tm-image :width="68" :height="68" :src="zu6053"></tm-image>
|
||||
<div class="mt-[6rpx]">删除</div>
|
||||
</div>
|
||||
@ -170,29 +280,47 @@
|
||||
:height="416"
|
||||
:round="6"
|
||||
>
|
||||
<div class="w-full h-full flex flex-col items-center" >
|
||||
<div class="mt-[46rpx] mb-[44rpx] leading-[48rpx] text-[#747474] text-[24rpx]" >
|
||||
<div class="w-full h-full flex flex-col items-center">
|
||||
<div
|
||||
class="mt-[46rpx] mb-[44rpx] leading-[48rpx] text-[#747474] text-[24rpx]"
|
||||
>
|
||||
撤回该条消息?
|
||||
</div>
|
||||
<div class="divider" ></div>
|
||||
<div @click="withdrawerConfirm" class="mt-[32rpx] mb-[32rpx] text-[32rpx] text-[#CF3050] leading-[48rpx]">
|
||||
<div class="divider"></div>
|
||||
<div
|
||||
@click="withdrawerConfirm"
|
||||
class="mt-[32rpx] mb-[32rpx] text-[32rpx] text-[#CF3050] leading-[48rpx]"
|
||||
>
|
||||
撤回
|
||||
</div>
|
||||
<div class="divider" ></div>
|
||||
<div @click="state.showWin = false" class="mt-[32rpx] mb-[32rpx] text-[32rpx] text-[#000000] leading-[48rpx]">
|
||||
<div class="divider"></div>
|
||||
<div
|
||||
@click="state.showWin = false"
|
||||
class="mt-[32rpx] mb-[32rpx] text-[32rpx] text-[#000000] leading-[48rpx]"
|
||||
>
|
||||
取消
|
||||
</div>
|
||||
</div>
|
||||
</tm-drawer>
|
||||
</div>
|
||||
</template>
|
||||
</ZPaging>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, reactive, watch, computed, onMounted, onUnmounted, nextTick } from 'vue';
|
||||
import {
|
||||
ref,
|
||||
reactive,
|
||||
watch,
|
||||
computed,
|
||||
onMounted,
|
||||
onUnmounted,
|
||||
nextTick,
|
||||
} from 'vue'
|
||||
import { QuillEditor, Quill } from '@vueup/vue-quill'
|
||||
import EmojiBlot from './formats/emoji'
|
||||
import { useChatList } from "@/store/chatList/index.js";
|
||||
import { useAuth } from "@/store/auth";
|
||||
import { useChatList } from '@/store/chatList/index.js'
|
||||
import { useAuth } from '@/store/auth'
|
||||
import {
|
||||
useUserStore,
|
||||
useDialogueStore,
|
||||
@ -200,41 +328,45 @@ import {
|
||||
useEditorDraftStore,
|
||||
useTalkStore,
|
||||
useSettingsStore,
|
||||
useDialogueListStore
|
||||
useDialogueListStore,
|
||||
} from '@/store'
|
||||
import addCircleGray from "@/static/image/chatList/addCircleGray.png";
|
||||
import addCircleGray from '@/static/image/chatList/addCircleGray.png'
|
||||
import { MessageComponents, ForwardableMessageType } from '@/constant/message'
|
||||
import { formatTime, parseTime } from '@/utils/datetime'
|
||||
import { deltaToMessage, deltaToString, isEmptyDelta } from './util'
|
||||
import smile from "@/static/image/chatList/smile@2x.png";
|
||||
import keyboard from "@/static/image/chatList/keyboard@2x.png";
|
||||
import smile from '@/static/image/chatList/smile@2x.png'
|
||||
import keyboard from '@/static/image/chatList/keyboard@2x.png'
|
||||
import { useInject, useTalkRecord } from '@/hooks'
|
||||
import { emitCall } from '@/utils/common'
|
||||
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 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 emojiPanel from './components/emojiPanel.vue'
|
||||
import filePanel from './components/filePanel.vue'
|
||||
import lodash from "lodash";
|
||||
import lodash from 'lodash'
|
||||
import { ServePublishMessage } from '@/api/chat'
|
||||
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"
|
||||
import zu6050 from "@/static/image/chatList/zu6050@2x.png"
|
||||
import zu6051 from "@/static/image/chatList/zu6051@2x.png"
|
||||
import zu6052 from "@/static/image/chatList/zu6052@2x.png"
|
||||
import zu6053 from "@/static/image/chatList/zu6053@2x.png"
|
||||
import deepBubble from "@/components/deep-bubble/deep-bubble.vue"
|
||||
import {isRevoke } from './menu'
|
||||
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'
|
||||
import zu6050 from '@/static/image/chatList/zu6050@2x.png'
|
||||
import zu6051 from '@/static/image/chatList/zu6051@2x.png'
|
||||
import zu6052 from '@/static/image/chatList/zu6052@2x.png'
|
||||
import zu6053 from '@/static/image/chatList/zu6053@2x.png'
|
||||
import deepBubble from '@/components/deep-bubble/deep-bubble.vue'
|
||||
import { isRevoke } from './menu'
|
||||
import useConfirm from '@/components/x-confirm/useConfirm.js'
|
||||
import { onLoad as uniOnload } from '@dcloudio/uni-app'
|
||||
|
||||
Quill.register('formats/emoji', EmojiBlot)
|
||||
|
||||
const { getDialogueList, updateZpagingRef, virtualList } = useDialogueListStore()
|
||||
const {
|
||||
getDialogueList,
|
||||
updateZpagingRef,
|
||||
virtualList,
|
||||
} = useDialogueListStore()
|
||||
const talkStore = useTalkStore()
|
||||
const { showConfirm } = useConfirm();
|
||||
const { showConfirm } = useConfirm()
|
||||
const settingsStore = useSettingsStore()
|
||||
const userStore = useUserStore()
|
||||
const dialogueStore = useDialogueStore()
|
||||
@ -251,7 +383,7 @@ const talkParams = reactive({
|
||||
username: computed(() => dialogueStore.talk.username),
|
||||
online: computed(() => dialogueStore.online),
|
||||
keyboard: computed(() => dialogueStore.keyboard),
|
||||
num: computed(() => dialogueStore.members.length)
|
||||
num: computed(() => dialogueStore.members.length),
|
||||
})
|
||||
|
||||
const state = ref({
|
||||
@ -259,7 +391,7 @@ const state = ref({
|
||||
isOpenFilePanel: false,
|
||||
showWin: false,
|
||||
onfocusItem: null,
|
||||
sessionId: ''
|
||||
sessionId: '',
|
||||
})
|
||||
|
||||
uniOnload((options) => {
|
||||
@ -269,11 +401,11 @@ uniOnload((options) => {
|
||||
})
|
||||
|
||||
const handleEmojiPanel = () => {
|
||||
state.value.isOpenFilePanel = false;
|
||||
state.value.isOpenFilePanel = false
|
||||
state.value.isOpenEmojiPanel = !state.value.isOpenEmojiPanel
|
||||
}
|
||||
const handleFilePanel = () => {
|
||||
state.value.isOpenEmojiPanel = false;
|
||||
state.value.isOpenEmojiPanel = false
|
||||
state.value.isOpenFilePanel = !state.value.isOpenFilePanel
|
||||
}
|
||||
|
||||
@ -282,8 +414,8 @@ const onSendMessage = (data = {}) => {
|
||||
...data,
|
||||
receiver: {
|
||||
receiver_id: talkParams.receiver_id,
|
||||
talk_type: talkParams.type
|
||||
}
|
||||
talk_type: talkParams.type,
|
||||
},
|
||||
}
|
||||
|
||||
ServePublishMessage(message)
|
||||
@ -313,10 +445,11 @@ const onSendMessageClick = () => {
|
||||
return message.info('发送内容超长,请分条发送')
|
||||
}
|
||||
onSendTextEvent({
|
||||
data, callBack: (ok) => {
|
||||
data,
|
||||
callBack: (ok) => {
|
||||
if (!ok) return
|
||||
getQuill().setContents([], Quill.sources.USER)
|
||||
}
|
||||
},
|
||||
})
|
||||
break
|
||||
}
|
||||
@ -330,7 +463,7 @@ const onSendTextEvent = lodash.throttle((value) => {
|
||||
type: 'text',
|
||||
content: data.items[0].content,
|
||||
quote_id: data.quoteId,
|
||||
mentions: data.mentionUids
|
||||
mentions: data.mentionUids,
|
||||
}
|
||||
|
||||
onSendMessage(message)
|
||||
@ -340,7 +473,7 @@ const onSendTextEvent = lodash.throttle((value) => {
|
||||
const onInputEvent = ({ data }) => {
|
||||
talkStore.updateItem({
|
||||
index_name: indexName.value,
|
||||
draft_text: data
|
||||
draft_text: data,
|
||||
})
|
||||
|
||||
// 判断对方是否在线和是否需要推送
|
||||
@ -365,7 +498,13 @@ const evnets = {
|
||||
},
|
||||
}
|
||||
|
||||
const { loadConfig, records, onLoad, onRefreshLoad, onJumpMessage } = useTalkRecord(talkParams.uid)
|
||||
const {
|
||||
loadConfig,
|
||||
records,
|
||||
onLoad,
|
||||
onRefreshLoad,
|
||||
onJumpMessage,
|
||||
} = useTalkRecord(talkParams.uid)
|
||||
|
||||
const getQuill = () => {
|
||||
return editor.value?.getQuill()
|
||||
@ -384,11 +523,10 @@ const isShowCopy = (item) => {
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const selectedMessage = computed(() => {
|
||||
return virtualList.value.filter(item => item.isCheck)
|
||||
return virtualList.value.filter((item) => item.isCheck)
|
||||
})
|
||||
|
||||
// 编辑器事件
|
||||
@ -421,7 +559,7 @@ const onEmoticonEvent = (data) => {
|
||||
|
||||
quill.setSelection(index + 1, 0, 'user')
|
||||
} else {
|
||||
let fn = emitCall('emoticon_event', data.value, () => { })
|
||||
let fn = emitCall('emoticon_event', data.value, () => {})
|
||||
emit('editor-event', fn)
|
||||
}
|
||||
}
|
||||
@ -434,7 +572,7 @@ const onEditorChange = () => {
|
||||
if (!isEmptyDelta(delta)) {
|
||||
editorDraftStore.items[indexName.value || ''] = JSON.stringify({
|
||||
text: text,
|
||||
ops: delta.ops
|
||||
ops: delta.ops,
|
||||
})
|
||||
} else {
|
||||
// 删除 editorDraftStore.items 下的元素
|
||||
@ -452,7 +590,7 @@ const onClipboardMatcher = (node, Delta) => {
|
||||
if (op.insert && typeof op.insert === 'string') {
|
||||
ops.push({
|
||||
insert: op.insert, // 文字内容
|
||||
attributes: {} //文字样式(包括背景色和文字颜色等)
|
||||
attributes: {}, //文字样式(包括背景色和文字颜色等)
|
||||
})
|
||||
} else {
|
||||
ops.push(op)
|
||||
@ -469,16 +607,16 @@ const editorOption = {
|
||||
toolbar: false,
|
||||
clipboard: {
|
||||
// 粘贴版,处理粘贴时候的自带样式
|
||||
matchers: [[Node.ELEMENT_NODE, onClipboardMatcher]]
|
||||
matchers: [[Node.ELEMENT_NODE, onClipboardMatcher]],
|
||||
},
|
||||
|
||||
keyboard: {
|
||||
bindings: {
|
||||
enter: {
|
||||
key: 13,
|
||||
handler: onSendMessageClick
|
||||
}
|
||||
}
|
||||
handler: onSendMessageClick,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// imageUploader: {
|
||||
@ -527,49 +665,49 @@ const virtualListChange = (vList) => {
|
||||
}
|
||||
|
||||
const onContextMenu = (menuType, item) => {
|
||||
console.log(menuType, item, 'item');
|
||||
console.log(menuType, item, 'item')
|
||||
switch (menuType) {
|
||||
case 'actionCopy':
|
||||
actionCopy(item)
|
||||
break;
|
||||
break
|
||||
case 'multipleChoose':
|
||||
multipleChoose(item)
|
||||
break;
|
||||
break
|
||||
case 'actionCite':
|
||||
actionCite(item)
|
||||
break;
|
||||
break
|
||||
case 'actionWithdraw':
|
||||
actionWithdraw(item)
|
||||
break;
|
||||
break
|
||||
case 'actionDelete':
|
||||
actionDelete(item)
|
||||
break;
|
||||
break
|
||||
|
||||
default:
|
||||
break;
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const actionCopy = (item) => {
|
||||
console.log('复制');
|
||||
console.log('复制')
|
||||
let content = ''
|
||||
switch (item.msg_type) {
|
||||
case 1:
|
||||
content = item.extra.content
|
||||
break;
|
||||
break
|
||||
case 3:
|
||||
content = item.extra.url
|
||||
break;
|
||||
break
|
||||
case 5:
|
||||
content = item.extra.url
|
||||
break;
|
||||
break
|
||||
|
||||
default:
|
||||
break;
|
||||
break
|
||||
}
|
||||
uni.setClipboardData({
|
||||
data: content,
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
const multipleChoose = (item) => {
|
||||
@ -578,23 +716,23 @@ const multipleChoose = (item) => {
|
||||
}
|
||||
|
||||
const actionCite = (item) => {
|
||||
console.log('引用');
|
||||
console.log('引用')
|
||||
}
|
||||
|
||||
const actionWithdraw = (item) => {
|
||||
console.log('撤回');
|
||||
console.log('撤回')
|
||||
state.value.onfocusItem = item
|
||||
state.value.showWin = true;
|
||||
state.value.showWin = true
|
||||
}
|
||||
|
||||
const withdrawerConfirm = () => {
|
||||
dialogueStore.ApiRevokeRecord(state.value.onfocusItem.msg_id)
|
||||
state.value.onfocusItem = null
|
||||
state.value.showWin = false;
|
||||
state.value.showWin = false
|
||||
}
|
||||
|
||||
const actionDelete = (item) => {
|
||||
console.log('删除');
|
||||
console.log('删除')
|
||||
item.isCheck = true
|
||||
handleDelete()
|
||||
}
|
||||
@ -603,14 +741,14 @@ const handleMergeForward = () => {
|
||||
if (selectedMessage.value.length == 0) {
|
||||
return message.warning('未选择消息')
|
||||
}
|
||||
console.log('合并转发');
|
||||
console.log('合并转发')
|
||||
dialogueStore.setForwardType(2)
|
||||
dialogueStore.setForwardMessages(selectedMessage.value)
|
||||
uni.navigateTo({
|
||||
url: '/pages/chooseChat/index',
|
||||
success: function (res) {
|
||||
clearMultiSelect()
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@ -618,14 +756,14 @@ const handleSingleForward = () => {
|
||||
if (selectedMessage.value.length == 0) {
|
||||
return message.warning('未选择消息')
|
||||
}
|
||||
console.log('逐条转发');
|
||||
console.log('逐条转发')
|
||||
dialogueStore.setForwardType(1)
|
||||
dialogueStore.setForwardMessages(selectedMessage.value)
|
||||
uni.navigateTo({
|
||||
url: '/pages/chooseChat/index',
|
||||
success: function (res) {
|
||||
clearMultiSelect()
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@ -633,40 +771,42 @@ const handleWechatForward = () => {
|
||||
if (selectedMessage.value.length == 0) {
|
||||
return message.warning('未选择消息')
|
||||
}
|
||||
console.log('微信转发');
|
||||
console.log('微信转发')
|
||||
}
|
||||
|
||||
const handleDelete = () => {
|
||||
if (selectedMessage.value.length == 0) {
|
||||
return message.warning('未选择消息')
|
||||
}
|
||||
console.log('删除');
|
||||
console.log('删除')
|
||||
showConfirm({
|
||||
content: '确定删除聊天记录',
|
||||
confirmText:'删除',
|
||||
confirmColor:'#CF3050',
|
||||
confirmText: '删除',
|
||||
confirmColor: '#CF3050',
|
||||
onConfirm: async () => {
|
||||
const msgIds = selectedMessage.value.map(item => item.msg_id)
|
||||
virtualList.value = virtualList.value.filter(item => !msgIds.includes(item.msg_id))
|
||||
const msgIds = selectedMessage.value.map((item) => item.msg_id)
|
||||
virtualList.value = virtualList.value.filter(
|
||||
(item) => !msgIds.includes(item.msg_id),
|
||||
)
|
||||
dialogueStore.ApiDeleteRecord(msgIds)
|
||||
clearMultiSelect()
|
||||
},
|
||||
onCancel: () => {
|
||||
}
|
||||
onCancel: () => {},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
watch(() => zpagingRef.value, (newValue, oldValue) => {
|
||||
watch(
|
||||
() => zpagingRef.value,
|
||||
(newValue, oldValue) => {
|
||||
if (newValue) {
|
||||
updateZpagingRef(newValue)
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
const clearMultiSelect = () => {
|
||||
dialogueStore.setMultiSelect(false)
|
||||
virtualList.value.forEach(item => {
|
||||
virtualList.value.forEach((item) => {
|
||||
item.isCheck = false
|
||||
})
|
||||
}
|
||||
@ -689,14 +829,19 @@ const initData = async () => {
|
||||
//点击跳转到聊天设置页面
|
||||
const toChatSettingsPage = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/chatSettings/index?groupId=' + talkParams?.receiver_id + '&sessionId=' + state.sessionId
|
||||
url:
|
||||
'/pages/chatSettings/index?groupId=' +
|
||||
talkParams?.receiver_id +
|
||||
'&sessionId=' +
|
||||
state.sessionId,
|
||||
})
|
||||
}
|
||||
|
||||
//点击跳转到用户详情页面
|
||||
const toUserDetailPage = (userItem) => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/dialog/dialogDetail/userDetail?erpUserId=' + userItem.erp_user_id,
|
||||
url:
|
||||
'/pages/dialog/dialogDetail/userDetail?erpUserId=' + userItem.erp_user_id,
|
||||
})
|
||||
}
|
||||
|
||||
@ -710,24 +855,23 @@ onUnmounted(() => {
|
||||
})
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
uni-page-body,
|
||||
page {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.outer-layer {
|
||||
.dialog-page {
|
||||
flex: 1;
|
||||
background-image: url("@/static/image/clockIn/z3280@3x.png");
|
||||
background-image: url('@/static/image/clockIn/z3280@3x.png');
|
||||
background-size: cover;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
background-position: bottom center;
|
||||
background-attachment: fixed;
|
||||
width: 100%;
|
||||
|
||||
.root {
|
||||
flex: 1;
|
||||
.dialog-list {
|
||||
padding: 20rpx 32rpx;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.toChatSetting_btn {
|
||||
::v-deep .tmicon-gengduo {
|
||||
line-height: unset !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.searchRoot {
|
||||
@ -745,22 +889,6 @@ page {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.dialogBox {
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
|
||||
// 添加以下样式来隐藏滚动条
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
-ms-overflow-style: none;
|
||||
/* IE and Edge */
|
||||
scrollbar-width: none;
|
||||
/* Firefox */
|
||||
}
|
||||
|
||||
.load-toolbar {
|
||||
height: 50rpx;
|
||||
color: #409eff;
|
||||
@ -825,7 +953,7 @@ page {
|
||||
margin-bottom: 6rpx;
|
||||
font-size: 24rpx;
|
||||
user-select: none;
|
||||
color: #BABABA;
|
||||
color: #bababa;
|
||||
opacity: 1;
|
||||
|
||||
&.show {
|
||||
@ -970,7 +1098,7 @@ page {
|
||||
|
||||
:deep(.ql-editor) {
|
||||
padding: 14rpx 22rpx;
|
||||
background-color: #F9F9F9;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 8rpx;
|
||||
outline: none !important;
|
||||
max-height: 294rpx;
|
||||
@ -1011,6 +1139,6 @@ page {
|
||||
.divider {
|
||||
width: 100%;
|
||||
height: 1rpx;
|
||||
background-color: #E7E7E7;
|
||||
background-color: #e7e7e7;
|
||||
}
|
||||
</style>
|
||||
|
@ -11,12 +11,22 @@
|
||||
:maxCount="99"
|
||||
color="#D03050"
|
||||
>
|
||||
<tm-image
|
||||
:width="96"
|
||||
:height="96"
|
||||
:round="12"
|
||||
:src="avatarCpt"
|
||||
></tm-image>
|
||||
<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">
|
||||
@ -54,9 +64,10 @@
|
||||
@click="handleTop"
|
||||
class="w-[156rpx] h-[154rpx] text-[#ffffff] bg-[#F09F1F] flex items-center justify-center"
|
||||
>
|
||||
{{ props.data.is_top === 1 ? "取消置顶" : "置顶" }}
|
||||
{{ props.data.is_top === 1 ? '取消置顶' : '置顶' }}
|
||||
</div>
|
||||
<div
|
||||
@click="handleDelete"
|
||||
class="w-[156rpx] h-[154rpx] text-[#ffffff] bg-[#CF3050] flex items-center justify-center"
|
||||
>
|
||||
删除
|
||||
@ -71,20 +82,17 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
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 zu4989 from "@/static/image/chatList/zu4989@2x.png";
|
||||
import zu4991 from "@/static/image/chatList/zu4991@2x.png";
|
||||
import zu4992 from "@/static/image/chatList/zu4992@2x.png";
|
||||
import zu5296 from "@/static/image/chatList/zu5296@2x.png";
|
||||
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 } = useSessionMenu();
|
||||
const dialogueStore = useDialogueStore();
|
||||
const talkStore = useTalkStore()
|
||||
const { onToTopTalk, onRemoveTalk } = useSessionMenu()
|
||||
const dialogueStore = useDialogueStore()
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
@ -96,35 +104,12 @@ const props = defineProps({
|
||||
default: -1,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const avatarCpt = computed(() => {
|
||||
let avatar = null;
|
||||
if (props.data.avatar !== "") {
|
||||
avatar = props.data.avatar;
|
||||
} else {
|
||||
switch (props.data.group_type) {
|
||||
case 1:
|
||||
avatar = zu4992;
|
||||
break;
|
||||
case 2:
|
||||
avatar = zu4989;
|
||||
break;
|
||||
case 3:
|
||||
avatar = zu4991;
|
||||
break;
|
||||
case 4:
|
||||
avatar = zu5296;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return avatar;
|
||||
});
|
||||
})
|
||||
|
||||
const cellClick = () => {
|
||||
console.log(props.data);
|
||||
console.log(props.data)
|
||||
// 更新编辑信息
|
||||
dialogueStore.setDialogue(props.data);
|
||||
dialogueStore.setDialogue(props.data)
|
||||
|
||||
// 清空消息未读数
|
||||
if (props.data.unread_num > 0) {
|
||||
@ -135,18 +120,24 @@ const cellClick = () => {
|
||||
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)
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.chatItem {
|
||||
@ -161,11 +152,6 @@ const handleTop = () => {
|
||||
}
|
||||
}
|
||||
|
||||
.avatarImg {
|
||||
height: 96rpx;
|
||||
width: 96rpx;
|
||||
}
|
||||
|
||||
.chatInfo {
|
||||
flex: 1;
|
||||
margin-left: 20rpx;
|
||||
|
@ -25,8 +25,13 @@
|
||||
:src="addCircle"
|
||||
></tm-image>
|
||||
<template v-slot:label>
|
||||
<div class="w-full h-[208rpx] pt-[22rpx] pb-[22rpx] pl-[34rpx] pr-[32rpx]" >
|
||||
<div @click="creatGroupChat" class="flex items-center mb-[30rpx]" >
|
||||
<div
|
||||
class="w-full h-[208rpx] pt-[22rpx] pb-[22rpx] pl-[34rpx] pr-[32rpx]"
|
||||
>
|
||||
<div
|
||||
@click="creatGroupChat"
|
||||
class="flex items-center mb-[30rpx]"
|
||||
>
|
||||
<div class="mr-[26rpx] flex items-center">
|
||||
<tm-image
|
||||
:width="40"
|
||||
@ -34,10 +39,17 @@
|
||||
:src="cahtPopover"
|
||||
></tm-image>
|
||||
</div>
|
||||
<div class="leading-[54rpx] text-[32rpx] text-[#FFFFFF] font-bold" >发起群聊</div>
|
||||
<div
|
||||
class="leading-[54rpx] text-[32rpx] text-[#FFFFFF] font-bold"
|
||||
>
|
||||
发起群聊
|
||||
</div>
|
||||
<div class="divider" ></div>
|
||||
<div class="flex items-center mt-[28rpx]" >
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
<div
|
||||
@click="toAddressBookPage"
|
||||
class="flex items-center mt-[28rpx]"
|
||||
>
|
||||
<div class="mr-[26rpx] flex items-center">
|
||||
<tm-image
|
||||
:width="40"
|
||||
@ -45,7 +57,9 @@
|
||||
:src="zu3289"
|
||||
></tm-image>
|
||||
</div>
|
||||
<div class="leading-[54rpx] text-[32rpx] text-[#FFFFFF] font-bold" >
|
||||
<div
|
||||
class="leading-[54rpx] text-[32rpx] text-[#FFFFFF] font-bold"
|
||||
>
|
||||
通讯录
|
||||
</div>
|
||||
</div>
|
||||
@ -78,24 +92,25 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
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 } 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 { 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, 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'
|
||||
|
||||
const talkStore = useTalkStore();
|
||||
const userStore = useUserStore();
|
||||
const { userInfo } = useAuth();
|
||||
const talkStore = useTalkStore()
|
||||
const userStore = useUserStore()
|
||||
const dialogueStore = useDialogueStore()
|
||||
const { userInfo } = useAuth()
|
||||
|
||||
const topItems = computed(() => talkStore.topItems);
|
||||
const topItems = computed(() => talkStore.topItems)
|
||||
const items = computed(() => {
|
||||
// if (searchKeyword.value.length === 0) {
|
||||
return talkStore.talkItems;
|
||||
return talkStore.talkItems
|
||||
// }
|
||||
|
||||
// return talkStore.talkItems.filter((item) => {
|
||||
@ -103,31 +118,64 @@ const items = computed(() => {
|
||||
|
||||
// return keyword.toLowerCase().indexOf(searchKeyword.value.toLowerCase()) != -1
|
||||
// })
|
||||
});
|
||||
})
|
||||
|
||||
const creatGroupChat = () => {
|
||||
uni.navigateTo({
|
||||
url: "/pages/creatGroupChat/index",
|
||||
});
|
||||
};
|
||||
url: '/pages/creatGroupChat/index',
|
||||
})
|
||||
}
|
||||
|
||||
const toSearchPage = () => {
|
||||
uni.navigateTo({
|
||||
url: "/pages/search/index",
|
||||
});
|
||||
};
|
||||
url: '/pages/search/index',
|
||||
})
|
||||
}
|
||||
|
||||
//点击跳转到通讯录页面
|
||||
const toAddressBookPage = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/chooseByDeps/index?chooseMode=3',
|
||||
})
|
||||
}
|
||||
|
||||
watch(
|
||||
() => talkStore,
|
||||
(newValue, oldValue) => {
|
||||
console.log(talkStore);
|
||||
// console.log(talkStore)
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
{ deep: true, immediate: true },
|
||||
)
|
||||
|
||||
onShow(() => {
|
||||
talkStore.loadTalkList();
|
||||
});
|
||||
talkStore.loadTalkList()
|
||||
})
|
||||
|
||||
onLoad((options) => {
|
||||
if (options?.openSessionIndexName) {
|
||||
if (items?.value?.length > 0) {
|
||||
items.value.forEach((openSession) => {
|
||||
if (openSession.index_name === options?.openSessionIndexName) {
|
||||
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,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
uni-page-body,
|
||||
@ -137,7 +185,7 @@ page {
|
||||
.outer-layer {
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
background-image: url("@/static/image/clockIn/z3280@3x.png");
|
||||
background-image: url('@/static/image/clockIn/z3280@3x.png');
|
||||
background-size: cover;
|
||||
padding: 0 32rpx 20rpx 32rpx;
|
||||
display: flex;
|
||||
@ -155,14 +203,14 @@ page {
|
||||
margin-top: 20rpx;
|
||||
background-color: #fff;
|
||||
}
|
||||
.divider{
|
||||
.divider {
|
||||
height: 1rpx;
|
||||
background-color: #7C7C7C;
|
||||
background-color: #7c7c7c;
|
||||
opacity: 0.6;
|
||||
}
|
||||
.popoverBox {
|
||||
:deep(.popover-bcc){
|
||||
transform:translateX(20rpx) translateY(40rpx);
|
||||
:deep(.popover-bcc) {
|
||||
transform: translateX(20rpx) translateY(40rpx);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -9,15 +9,23 @@
|
||||
: ''
|
||||
"
|
||||
>
|
||||
<div
|
||||
class="avatar-img"
|
||||
:class="props?.conditionType ? 'avatar-img-condition' : ''"
|
||||
>
|
||||
<img v-if="avatarImg !== 'textImg'" :src="avatarImg" />
|
||||
<span v-if="avatarImg === 'textImg'" class="text-[32rpx] font-bold">
|
||||
{{ imgText }}
|
||||
</span>
|
||||
</div>
|
||||
<avatarModule
|
||||
:mode="props.searchItem?.group_type === 0 ? 1 : 2"
|
||||
:avatar="avatarImg"
|
||||
:userName="resultName"
|
||||
:groupType="props.searchItem?.group_type"
|
||||
:customStyle="{
|
||||
width: props?.conditionType ? '64rpx' : '96rpx',
|
||||
height: props?.conditionType ? '64rpx' : '96rpx',
|
||||
margin: props?.conditionType ? '0 18rpx 0 0' : '0 20rpx 0 0',
|
||||
}"
|
||||
:customTextStyle="{
|
||||
fontSize: props?.conditionType ? '20rpx' : '32rpx',
|
||||
fontWeight: 'bold',
|
||||
color: '#fff',
|
||||
lineHeight: '44rpx',
|
||||
}"
|
||||
></avatarModule>
|
||||
<div class="result-info">
|
||||
<div
|
||||
class="info-name"
|
||||
@ -75,10 +83,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import zu4992 from '@/static/image/chatList/zu4992@2x.png'
|
||||
import zu4991 from '@/static/image/chatList/zu4991@2x.png'
|
||||
import zu4989 from '@/static/image/chatList/zu4989@2x.png'
|
||||
import zu5296 from '@/static/image/chatList/zu5296@2x.png'
|
||||
import avatarModule from '@/components/avatar-module/index.vue'
|
||||
import {
|
||||
ref,
|
||||
watch,
|
||||
@ -167,14 +172,8 @@ const getKeyValue = (keys) => {
|
||||
//头像
|
||||
const avatarImg = computed(() => {
|
||||
let avatar = getKeyValue(keyMapping[props.searchResultKey]?.avatar)
|
||||
if (!avatar) {
|
||||
avatar = groupTypeMapping[props.searchItem?.group_type]?.defaultImg
|
||||
}
|
||||
if (props?.conditionType) {
|
||||
avatar = props.searchItem.avatar
|
||||
if (!avatar) {
|
||||
avatar = groupTypeMapping[0]?.defaultImg
|
||||
}
|
||||
}
|
||||
return avatar
|
||||
})
|
||||
@ -195,25 +194,20 @@ const imgText = computed(() => {
|
||||
// 映射表-根据groupType设置对应值
|
||||
const groupTypeMapping = {
|
||||
0: {
|
||||
defaultImg: 'textImg',
|
||||
},
|
||||
1: {
|
||||
defaultImg: zu4992,
|
||||
},
|
||||
2: {
|
||||
result_type: t('index.mine.department'),
|
||||
result_type_color: '#377EC6',
|
||||
defaultImg: zu4989,
|
||||
},
|
||||
3: {
|
||||
result_type: t('index.mine.project'),
|
||||
result_type_color: '#C1681C',
|
||||
defaultImg: zu4991,
|
||||
},
|
||||
4: {
|
||||
result_type: t('index.type.company'),
|
||||
result_type_color: '#7A58DE',
|
||||
defaultImg: zu5296,
|
||||
},
|
||||
}
|
||||
//群人数
|
||||
@ -262,35 +256,6 @@ const resultDetail = computed(() => {
|
||||
padding: 22rpx 0 24rpx;
|
||||
border-bottom: 1px solid $theme-border-color;
|
||||
|
||||
.avatar-img {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
margin: 0 20rpx 0 0;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: linear-gradient(to right, #674bbc, #46299d);
|
||||
flex-shrink: 0;
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
span {
|
||||
color: #fff;
|
||||
line-height: 44rpx;
|
||||
}
|
||||
}
|
||||
.avatar-img-condition {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
margin: 0 18rpx 0 0;
|
||||
span {
|
||||
font-size: 20rpx;
|
||||
}
|
||||
}
|
||||
.result-info {
|
||||
width: 100%;
|
||||
.info-name {
|
||||
|
@ -11,13 +11,7 @@
|
||||
:inside-more="true"
|
||||
>
|
||||
<template #top v-if="state.showPageTitle">
|
||||
<tm-navbar :hideBack="false" hideHome title="" :leftWidth="220">
|
||||
<div class="navBar-title flex flex-col items-center justify-center">
|
||||
<span class="text-[34rpx] font-medium">
|
||||
{{ state.pageTitle }}
|
||||
</span>
|
||||
</div>
|
||||
</tm-navbar>
|
||||
<customNavbar :title="state.pageTitle"></customNavbar>
|
||||
</template>
|
||||
<div v-if="state.condition === 'date'" class="search-by-date">
|
||||
<tm-time-picker
|
||||
|
BIN
src/static/image/chatList/groupCompany.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
src/static/image/chatList/groupDepartment.png
Normal file
After Width: | Height: | Size: 6.6 KiB |
BIN
src/static/image/chatList/groupNormal.png
Normal file
After Width: | Height: | Size: 6.3 KiB |
BIN
src/static/image/chatList/groupProject.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 6.1 KiB |
Before Width: | Height: | Size: 6.0 KiB |
BIN
src/static/image/clockIn/addressBook_head.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
@ -5,7 +5,7 @@ import { userInfoApi } from "@/api/user";
|
||||
import {ref} from 'vue'
|
||||
export const useAuth = createGlobalState(() => {
|
||||
// const token = useStorage('token', '', uniStorage)
|
||||
const token = ref('79b5c732d96d2b27a48a99dfd4a5566c43aaa5796242e854ebe3ffc198d6876b9628e7b764d9af65ab5dbb2d517ced88170491b74b048c0ba827c0d3741462cb89dc59ed46653a449af837a8262941ca1430937103230a1e32a1715f569f3efdbe6f8cb8b7b8642bd679668081b9b08f693d1b5be6002d936ec51e1e3e0c4927de9e32ac99a109b326e5d2bda27ec87624bb416ec70d2a95a2e190feeba9f0d6bae8571b3dfe89c824712344759a8f2bff9d70747c52525cf6a5614f9c770bca461a9b9c247b6dca97bcf83bbaf99bb726752c4fe1e9a4aa7de5c4cf3e88a3e480801280d45cdc124f9d8221105d8529c7c268c1251c3477bff5c051043bf9803f725aa97cbe369f1902315052eb4ee0e925840addd5e7796254066f2f9eb3f99f69f3d0db26bd940f47a18fa7b6182c6c7de87ee1a00936b7c90024c6085c65ffc3b8d20562eea48ff0b303a7795c67')
|
||||
const token = ref('79b5c732d96d2b27a48a99dfd4a5566c43aaa5796242e854ebe3ffc198d6876b9628e7b764d9af65ab5dbb2d517ced88170491b74b048c0ba827c0d3741462cb89dc59ed46653a449af837a8262941caaef1334d640773710f8cd96473bacfb190cba595a5d6a9c87d70f0999a3ebb41147213b31b4bdccffca66a56acf3baab5af0154f0dce360079f37709f78e13711036899344bddb0fb4cf0f2890287cb62c3fcbe33368caa5e213624577be8b8420ab75b1f50775ee16142a4321c5d56995f37354a66a969da98d95ba6e65d142ed097e04b411c1ebad2f62866d0ec7e1838420530a9941dbbcd00490199f8b892d39c9c0de979228015326a8fde48c55b8d48be643cb00da04330faa68977c689c6d523213435a2d92e3fab2976a1f5a6d4bfcb6c7aba3b4924873003b6492a2d0b81da4722162c68aeeec942eb2a85d')
|
||||
const refreshToken = useStorage('refreshToken', '', uniStorage)
|
||||
const userInfo = useStorage('userInfo', {}, uniStorage)
|
||||
const leaderList = useStorage('leaderList', [], uniStorage)
|
||||
|
@ -63,6 +63,8 @@ export const useGroupTypeStore = createGlobalState(() => {
|
||||
crumbsIndex.value = 0
|
||||
depCheckedKeys.value = []
|
||||
groupAdmins.value = []
|
||||
membersCheckedKeys.value = []
|
||||
allChooseMembers.value = []
|
||||
}
|
||||
|
||||
const createDepGroup = async (param) => {
|
||||
@ -76,8 +78,10 @@ export const useGroupTypeStore = createGlobalState(() => {
|
||||
}),
|
||||
positionInfos: groupAdmins.value.map((v) => {
|
||||
return {
|
||||
position_id: v.ID,
|
||||
position_name: v.name,
|
||||
dept_id: v.dept_id,
|
||||
dept_name: v.dept_name,
|
||||
position_id: v.position_id,
|
||||
position_name: v.position_name,
|
||||
}
|
||||
}),
|
||||
})
|
||||
|
@ -4,10 +4,12 @@ import {
|
||||
ServeRevokeRecords,
|
||||
ServePublishMessage,
|
||||
ServeCollectEmoticon,
|
||||
ServeEmptyMessage,
|
||||
} from '@/api/chat/index'
|
||||
import { ServeGetGroupMembers } from '@/api/group/index'
|
||||
import { useEditorStore } from './editor'
|
||||
import { useDialogueListStore } from './dialogueList'
|
||||
import { useAuth } from '../auth/index'
|
||||
|
||||
// 键盘消息事件定时器
|
||||
// let keyboardTimeout = null
|
||||
@ -73,7 +75,8 @@ export const useDialogueStore = defineStore('dialogue', {
|
||||
getSilenceMember: (state) =>
|
||||
state.members.filter((item) => item.is_mute === 1),
|
||||
//获取群管理员
|
||||
getAdminList: (state) => state.members.filter((item) => (item.leader === 1 || item.leader === 2)),
|
||||
getAdminList: (state) =>
|
||||
state.members.filter((item) => item.leader === 1 || item.leader === 2),
|
||||
},
|
||||
actions: {
|
||||
// 更新在线状态
|
||||
@ -121,6 +124,8 @@ export const useDialogueStore = defineStore('dialogue', {
|
||||
key: o.key,
|
||||
erp_user_id: o.erp_user_id,
|
||||
is_mute: o.is_mute,
|
||||
is_mine:
|
||||
useAuth()?.userInfo?.value?.ID === o?.erp_user_id ? true : false,
|
||||
}))
|
||||
},
|
||||
|
||||
@ -210,6 +215,20 @@ export const useDialogueStore = defineStore('dialogue', {
|
||||
})
|
||||
},
|
||||
|
||||
//清空聊天记录
|
||||
apiClearRecord() {
|
||||
const { clearDialogueRecord } = useDialogueListStore()
|
||||
ServeEmptyMessage({
|
||||
talk_type: this.talk.talk_type,
|
||||
receiver_id: this.talk.receiver_id,
|
||||
}).then((res) => {
|
||||
if (res.code == 200) {
|
||||
clearDialogueRecord()
|
||||
} else {
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 撤销聊天记录
|
||||
ApiRevokeRecord(msg_id = '') {
|
||||
ServeRevokeRecords({ msg_id }).then((res) => {
|
||||
|
@ -2,42 +2,44 @@ import { defineStore } from 'pinia'
|
||||
import { useDialogueStore } from '@/store'
|
||||
import lodash from 'lodash'
|
||||
|
||||
import {ref} from 'vue'
|
||||
import {createGlobalState,useStorage} from '@vueuse/core'
|
||||
import {uniStorage} from "@/utils/uniStorage.js"
|
||||
import { ref } from 'vue'
|
||||
import { createGlobalState, useStorage } from '@vueuse/core'
|
||||
import { uniStorage } from '@/utils/uniStorage.js'
|
||||
|
||||
export const useDialogueListStore = createGlobalState(() => {
|
||||
const dialogueList = useStorage('dialogueList', [], uniStorage)
|
||||
const zpagingRef = ref()
|
||||
const virtualList = ref([])
|
||||
|
||||
const getDialogueList=(indexName)=>{
|
||||
return dialogueList.value.find(item=>item.index_name===indexName)
|
||||
const getDialogueList = (indexName) => {
|
||||
return dialogueList.value.find((item) => item.index_name === indexName)
|
||||
}
|
||||
|
||||
const addDialogueRecord=(newRecords,type='add')=>{
|
||||
console.log(newRecords);
|
||||
const addDialogueRecord = (newRecords, type = 'add') => {
|
||||
console.log(newRecords)
|
||||
|
||||
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)
|
||||
const existingIndex = dialogueList.value.findIndex(
|
||||
(item) => item.index_name === dialogue.index_name,
|
||||
)
|
||||
if (existingIndex === -1) {
|
||||
// 如果不存在,直接添加
|
||||
dialogueList.value.push(dialogue)
|
||||
} else {
|
||||
// 如果对话存在,处理 records 数组
|
||||
const { records = [] } = dialogue
|
||||
newRecords.forEach(newRecord => {
|
||||
newRecords.forEach((newRecord) => {
|
||||
const recordIndex = dialogueList.value[existingIndex].records.findIndex(
|
||||
record => record.msg_id === newRecord.msg_id
|
||||
(record) => record.msg_id === newRecord.msg_id,
|
||||
)
|
||||
|
||||
if (recordIndex === -1) {
|
||||
// 如果记录不存在,添加到 records 数组
|
||||
if(type==='add'){
|
||||
if (type === 'add') {
|
||||
dialogueList.value[existingIndex].records.push(newRecord)
|
||||
}else{
|
||||
} else {
|
||||
dialogueList.value[existingIndex].records.unshift(newRecord)
|
||||
}
|
||||
}
|
||||
@ -47,66 +49,91 @@ export const useDialogueListStore = createGlobalState(() => {
|
||||
const { index_name, records: _, ...updateProps } = dialogue
|
||||
dialogueList.value[existingIndex] = {
|
||||
...dialogueList.value[existingIndex],
|
||||
...updateProps
|
||||
...updateProps,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const updateDialogueRecord=(record)=>{
|
||||
const updateDialogueRecord = (record) => {
|
||||
const dialogue = lodash.cloneDeep(useDialogueStore())
|
||||
const item = getDialogueList(dialogue.index_name)
|
||||
const recordIndex = item.records.findIndex(item=>item.msg_id===record.msg_id)
|
||||
if(recordIndex!==-1){
|
||||
const recordIndex = item.records.findIndex(
|
||||
(item) => item.msg_id === record.msg_id,
|
||||
)
|
||||
if (recordIndex !== -1) {
|
||||
item.records[recordIndex] = {
|
||||
...item.records[recordIndex],
|
||||
...record
|
||||
...record,
|
||||
}
|
||||
}
|
||||
const virtualIndex = virtualList.value.findIndex(item=>item.msg_id===record.msg_id)
|
||||
if(virtualIndex!==-1){
|
||||
const virtualIndex = virtualList.value.findIndex(
|
||||
(item) => item.msg_id === record.msg_id,
|
||||
)
|
||||
if (virtualIndex !== -1) {
|
||||
virtualList.value[virtualIndex] = {
|
||||
...virtualList.value[virtualIndex],
|
||||
...record
|
||||
...record,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const deleteDialogueRecord=(record)=>{
|
||||
const deleteDialogueRecord = (record) => {
|
||||
const dialogue = lodash.cloneDeep(useDialogueStore())
|
||||
const item = getDialogueList(dialogue.index_name)
|
||||
const recordIndex = item.records.findIndex(item=>item.msg_id===record.msg_id)
|
||||
if(recordIndex!==-1){
|
||||
item.records.splice(recordIndex,1)
|
||||
const recordIndex = item.records.findIndex(
|
||||
(item) => item.msg_id === record.msg_id,
|
||||
)
|
||||
if (recordIndex !== -1) {
|
||||
item.records.splice(recordIndex, 1)
|
||||
}
|
||||
}
|
||||
|
||||
const updateUploadProgress=(id,progress)=>{
|
||||
const record = virtualList.value.find(item=>item.msg_id===id)
|
||||
if(record){
|
||||
const updateUploadProgress = (id, progress) => {
|
||||
const record = virtualList.value.find((item) => item.msg_id === id)
|
||||
if (record) {
|
||||
record.uploadCurrent = progress
|
||||
}
|
||||
}
|
||||
|
||||
const updateZpagingRef=(params)=>{
|
||||
zpagingRef.value=params
|
||||
const updateZpagingRef = (params) => {
|
||||
zpagingRef.value = params
|
||||
}
|
||||
|
||||
const zpagingComplete=(index_name)=>{
|
||||
const zpagingComplete = (index_name) => {
|
||||
const item = getDialogueList(index_name)
|
||||
zpagingRef.value?.complete(lodash.cloneDeep(item.records).reverse())
|
||||
}
|
||||
|
||||
const addChatRecord = (indexName,item)=>{
|
||||
const addChatRecord = (indexName, item) => {
|
||||
const dialogue = lodash.cloneDeep(useDialogueStore())
|
||||
if (dialogue?.index_name === indexName) {
|
||||
zpagingRef.value?.addChatRecordData(item,false,false)
|
||||
zpagingRef.value?.addChatRecordData(item, false, false)
|
||||
}
|
||||
}
|
||||
|
||||
const batchDelDialogueRecord=(msgIds)=>{
|
||||
const batchDelDialogueRecord = (msgIds) => {
|
||||
const dialogue = lodash.cloneDeep(useDialogueStore())
|
||||
const item = getDialogueList(dialogue.index_name)
|
||||
item.records = item.records.filter(item=>!msgIds.includes(item.msg_id))
|
||||
item.records = item.records.filter((item) => !msgIds.includes(item.msg_id))
|
||||
}
|
||||
|
||||
//删除会话时,同时刪除storage中存儲的會話
|
||||
const delDialogueStorage = (indexName) => {
|
||||
if (dialogueList?.value?.length > 0) {
|
||||
dialogueList.value.forEach((item, index) => {
|
||||
if (item?.index_name === indexName) {
|
||||
dialogueList.value.splice(index, 1)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//清空聊天记录时,同时清空本地保存的聊天记录
|
||||
const clearDialogueRecord = () => {
|
||||
const dialogue = lodash.cloneDeep(useDialogueStore())
|
||||
const item = getDialogueList(dialogue.index_name)
|
||||
item.records = []
|
||||
virtualList.value = []
|
||||
}
|
||||
|
||||
return {
|
||||
@ -122,5 +149,7 @@ export const useDialogueListStore = createGlobalState(() => {
|
||||
addChatRecord,
|
||||
virtualList,
|
||||
batchDelDialogueRecord,
|
||||
delDialogueStorage,
|
||||
clearDialogueRecord,
|
||||
}
|
||||
})
|
||||
|
@ -1,9 +1,5 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import {
|
||||
ServeGroupDetail,
|
||||
ServeGetGroupMembers,
|
||||
ServeGetGroupNotices,
|
||||
} from '@/api/group/index'
|
||||
import { ServeGroupDetail, ServeGetGroupNotices } from '@/api/group/index'
|
||||
import { useDialogueStore, useGroupTypeStore } from '@/store'
|
||||
|
||||
export const useGroupStore = defineStore('group', {
|
||||
@ -32,25 +28,28 @@ export const useGroupStore = defineStore('group', {
|
||||
if (code == 200) {
|
||||
console.log(data)
|
||||
let deptPosArr = []
|
||||
if (data.deptInfos?.length > 0) {
|
||||
// if (data.deptInfos?.length > 0) {
|
||||
if (data.positionInfos?.length > 0) {
|
||||
data.deptInfos.forEach((deptInfo) => {
|
||||
data.positionInfos.forEach(async (positionInfo) => {
|
||||
let deptPosItem = groupTypeStore.getDepartmentPositionsById(
|
||||
deptInfo.dept_id,
|
||||
positionInfo.position_id,
|
||||
)
|
||||
if (deptPosItem) {
|
||||
// data.deptInfos.forEach((deptInfo) => {
|
||||
data.positionInfos.forEach((positionInfo) => {
|
||||
// let deptPosItem = groupTypeStore.getDepartmentPositionsById(
|
||||
// deptInfo.dept_id,
|
||||
// positionInfo.position_id,
|
||||
// )
|
||||
// if (deptPosItem) {
|
||||
deptPosArr.push({
|
||||
deptPos: deptPosItem,
|
||||
dept_id: deptInfo.dept_id,
|
||||
deptPos:
|
||||
positionInfo.dept_name + '-' + positionInfo.position_name,
|
||||
dept_id: positionInfo.dept_id,
|
||||
dept_name: positionInfo.dept_name,
|
||||
position_id: positionInfo.position_id,
|
||||
position_name: positionInfo.position_name,
|
||||
})
|
||||
}
|
||||
})
|
||||
// }
|
||||
})
|
||||
// })
|
||||
}
|
||||
}
|
||||
// }
|
||||
data.groupAdminList = deptPosArr
|
||||
this.groupInfo = data
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ export const useTalkStore = defineStore('talk', {
|
||||
// 加载状态[1:未加载;2:加载中;3:加载完成;4:加载失败;]
|
||||
loadStatus: 2,
|
||||
// 会话列表
|
||||
items: []
|
||||
items: [],
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
@ -26,14 +26,14 @@ export const useTalkStore = defineStore('talk', {
|
||||
// 对话列表
|
||||
talkItems: (state) => {
|
||||
let topList = state.items.filter((item) => item.is_top == 1)
|
||||
let listT = state.items.filter(v=>v.is_top !== 1)
|
||||
let listT = state.items.filter((v) => v.is_top !== 1)
|
||||
let listP = topList.sort((a, b) => {
|
||||
return ttime(b.updated_at) - ttime(a.updated_at)
|
||||
})
|
||||
listT = listT.sort((a, b) => {
|
||||
return ttime(b.updated_at) - ttime(a.updated_at)
|
||||
})
|
||||
return [...listP,...listT]
|
||||
return [...listP, ...listT]
|
||||
},
|
||||
|
||||
// 消息未读数总计
|
||||
@ -41,7 +41,7 @@ export const useTalkStore = defineStore('talk', {
|
||||
return state.items.reduce((total, item) => {
|
||||
return total + item.unread_num
|
||||
}, 0)
|
||||
}
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
findItem(index_name) {
|
||||
@ -50,7 +50,9 @@ export const useTalkStore = defineStore('talk', {
|
||||
|
||||
// 更新对话节点
|
||||
updateItem(params) {
|
||||
const item = this.items.find((item) => item.index_name === params.index_name)
|
||||
const item = this.items.find(
|
||||
(item) => item.index_name === params.index_name,
|
||||
)
|
||||
|
||||
item && Object.assign(item, params)
|
||||
},
|
||||
@ -73,7 +75,9 @@ export const useTalkStore = defineStore('talk', {
|
||||
|
||||
// 更新对话消息
|
||||
updateMessage(params) {
|
||||
const item = this.items.find((item) => item.index_name === params.index_name)
|
||||
const item = this.items.find(
|
||||
(item) => item.index_name === params.index_name,
|
||||
)
|
||||
if (item) {
|
||||
item.unread_num++
|
||||
item.msg_text = params.msg_text
|
||||
@ -83,7 +87,9 @@ export const useTalkStore = defineStore('talk', {
|
||||
|
||||
// 更新联系人备注
|
||||
setRemark(params) {
|
||||
const item = this.items.find((item) => item.index_name === `1_${params.user_id}`)
|
||||
const item = this.items.find(
|
||||
(item) => item.index_name === `1_${params.user_id}`,
|
||||
)
|
||||
|
||||
item && (item.remark = params.remark)
|
||||
},
|
||||
@ -126,34 +132,31 @@ export const useTalkStore = defineStore('talk', {
|
||||
return this.items.findIndex((item) => item.index_name === index_name)
|
||||
},
|
||||
|
||||
toTalk(talk_type, receiver_id, router) {
|
||||
const route = {
|
||||
path: '/message',
|
||||
query: {
|
||||
v: new Date().getTime()
|
||||
}
|
||||
}
|
||||
|
||||
toTalk(talk_type, receiver_id, erp_user_id) {
|
||||
if (this.findTalkIndex(`${talk_type}_${receiver_id}`) >= 0) {
|
||||
sessionStorage.setItem(KEY_INDEX_NAME, `${talk_type}_${receiver_id}`)
|
||||
return router.push(route)
|
||||
uni.reLaunch({
|
||||
url: '/pages/index/index?openSessionIndexName=' + `${talk_type}_${receiver_id}`,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
ServeCreateTalkList({
|
||||
talk_type,
|
||||
receiver_id
|
||||
erp_user_id,
|
||||
}).then(({ code, data, message }) => {
|
||||
if (code == 200) {
|
||||
if (this.findTalkIndex(`${talk_type}_${receiver_id}`) === -1) {
|
||||
this.addItem(formatTalkItem(data))
|
||||
}
|
||||
|
||||
sessionStorage.setItem(KEY_INDEX_NAME, `${talk_type}_${receiver_id}`)
|
||||
return router.push(route)
|
||||
uni.reLaunch({
|
||||
url: '/pages/index/index?openSessionIndexName=' + `${talk_type}_${receiver_id}`,
|
||||
})
|
||||
} else {
|
||||
message.warning(message)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -54,7 +54,7 @@
|
||||
<!-- #endif -->
|
||||
<slot name="left"></slot>
|
||||
</view>
|
||||
<view class="flex flex-row-center-center" :style="{ width: contentwidth + 'px' }">
|
||||
<view class="flex flex-row-center-center flex-col" :style="{ width: contentwidth + 'px' }">
|
||||
<slot>
|
||||
<tm-text
|
||||
:unit="props.unit"
|
||||
@ -63,6 +63,7 @@
|
||||
:font-size="props.fontSize"
|
||||
:label="_title"
|
||||
></tm-text>
|
||||
<slot name="subTitle"></slot>
|
||||
</slot>
|
||||
</view>
|
||||
<view class="flex-row flex flex-row-center-end" :style="{ width: _rightWidth + 'rpx' }">
|
||||
|
@ -131,5 +131,23 @@
|
||||
"groupNotice.quit.edit": "退出本次编辑",
|
||||
"groupNotice.continue.edit": "继续编辑",
|
||||
"groupNotice.confirm.quit": "退出",
|
||||
"chatSettings.btn.removeAdmin": "移除"
|
||||
"chatSettings.btn.removeAdmin": "移除",
|
||||
"groupManage.disband.hint": "退出后,本群将被解散",
|
||||
"groupManage.quit.hint": "退出后,聊天记录将被清空",
|
||||
"index.mine.normal": "普通",
|
||||
"select.member.remove": "选择要移除的人",
|
||||
"select.member.num": "已选择",
|
||||
"statistic.unit.person": "人",
|
||||
"pageTitle.create.group": "发起群聊",
|
||||
"pageTitle.select.department": "选择部门",
|
||||
"radio.btn.selectAll": "全选",
|
||||
"choose.deps.nextLevel": "下级",
|
||||
"statistics.selected.deps": "已选择的部门数",
|
||||
"chat.manage.addMembers": "添加群成员",
|
||||
"pageTitle.select.groupType": "选择群类型",
|
||||
"pageTitle.select.shareChat": "选择一个聊天",
|
||||
"button.multiple.choice": "多选",
|
||||
"button.text.close": "关闭",
|
||||
"choose.deps.all": "全部",
|
||||
"choose.deps.current": "当前"
|
||||
}
|
||||
|
@ -1,74 +1,24 @@
|
||||
## 2.7.11(2024-06-28)
|
||||
1.`新增` 方法`updateVirtualListRender`,支持手动触发虚拟列表渲染更新。
|
||||
2.`新增` 延迟加载列表演示。
|
||||
3.`修复` v2.7.8引出的vue3+npm+微信小程序中,`uni.$zp`配置失效的问题。
|
||||
4.`修复` 在本地分页+虚拟列表情况下,虚拟列表cell未被正常销毁的问题。
|
||||
5.`修复` 打开调试模式下无法获取getApp导致的`cannot read property 'zp_handleQueryCallback' of undefined`的报错。
|
||||
6.`修复` 极小概率出现的分页请求较快且快速滚动到底部时未能加载更多的问题。
|
||||
## 2.7.10(2024-05-10)
|
||||
1.`修复` v2.7.8引出的vue3+npm+微信小程序中,uni.$zp配置失效的问题。
|
||||
## 2.7.9(2024-05-10)
|
||||
1.`修复` 在新版HbuilderX+vue3+微信小程序中可能出现的加载第二页数据后返回顶部无法下拉的问题。
|
||||
2.`修复` 在vue3+抖音小程序中可能出现的首次加载reload没有触发的问题。
|
||||
3.`优化` ts类型中,`ZPagingInstance`泛型不必填,默认为`any`。
|
||||
## 2.7.8(2024-05-09)
|
||||
1.`新增` typescript类型声明文件,感谢小何同学提供。
|
||||
2.`新增` 添加极简写法fetch相关配置及示例。
|
||||
3.`新增` `fixed-cellHeight`配置,支持在虚拟列表中自定义固定的cell高度。
|
||||
4.`新增` `refresher-refreshing-scrollable`配置,支持自定义下拉刷新中是否允许列表滚动。
|
||||
5.`修复` 在新版HubilderX+vue3+h5中,`swiper-demo`模式`slot=top`被导航栏遮挡的问题。
|
||||
6.`修复` 下拉进入二楼偶现的二楼高度未铺满全屏的问题。
|
||||
7.`修复` 虚拟列表中complete若传相同对象会导致key重复的问题。
|
||||
8.`修复` 在虚拟列表中调用refresh后缓存高度原始数据未清空的问题。
|
||||
9.`修复` 聊天记录模式删除记录时顶部显示loading的问题。
|
||||
9.`优化` `scrollIntoViewByIndex`支持在虚拟列表中滚动到指定cell。
|
||||
10.`优化` `content-z-index`默认值修改为1。
|
||||
## 2.7.7(2024-04-01)
|
||||
1.`新增` 下拉进入二楼功能及相关配置&demo。
|
||||
2.`新增` 虚拟列表写法添加【非内置列表】写法,可良好兼容vue3中的各平台并有较优的性能表现。
|
||||
3.`新增` `z-paging-cell`补充`@touchstart`事件。
|
||||
4.`修复` 页面滚动模式设置了`auto-full-height`后可能出现的依然有异常空白占位的问题和下拉刷新时列表数据被切割的问题。
|
||||
## 2.7.6(2024-02-29)
|
||||
1.`新增` `max-width`,支持设置`z-paging`的最大宽度,默认`z-paging`宽度铺满窗口。
|
||||
2.`新增` `chat-adjust-position-offset`,支持设置使用聊天记录模式中键盘弹出时占位高度偏移距离。
|
||||
3.`修复` 由于renderjs中聊天记录模式判断不准确导致的可能出现的从聊天记录页面跳转到其他页面后返回页面无法滚动的问题。
|
||||
4.`修复` 聊天记录模式首次加载失败后,发送消息顶部会显示加载失败文字的问题。
|
||||
5.`修复` 聊天记录模式nvue可能出现的键盘弹出无法滚动到底部的问题。
|
||||
6.`修复` 聊天记录模式+nvue滚动条无法展示的问题&底部会显示加载中的问题。
|
||||
7.`修复` 聊天记录模式监听键盘弹出可能出现的无法正常销毁的问题。
|
||||
8.`修复` 直接修改dataList的值组件内部的值未更新的问题。
|
||||
## 2.7.5(2024-01-23)
|
||||
1.`新增` props:`chat-loading-more-default-as-loading`,支持设置在聊天记录模式中滑动到顶部状态为默认状态时,以加载中的状态展示。
|
||||
2.`新增` slots:`chatNoMore`,支持自定义聊天记录模式没有更多数据view。
|
||||
3.`修复` 固定在底部view可能出现默认黄色的问题。
|
||||
4.`优化` 聊天记录加载更多样式,与普通模式对齐,支持点击加载更多&点击重试,并支持加载更多相关配置。
|
||||
5.`优化` 微调下拉刷新和底部加载更多样式。
|
||||
6.`优化` 聊天记录模式自动滚动到底部添加延时以避免可能出现的滚动到底部位置不正确的问题。
|
||||
7.`优化` 使用新的判断滚动到顶部算法以解决在安卓设备中可能出现的因滚动到顶部时scrollTop不为0导致的下拉刷新被禁用的问题。
|
||||
## 2.7.4(2024-01-14)
|
||||
1.`新增` props:`auto-adjust-position-when-chat`,支持设置使用聊天记录模式中键盘弹出时是否自动调整slot="bottom"高度。
|
||||
2.`新增` props:`auto-to-bottom-when-chat`,支持设置使用聊天记录模式中键盘弹出时是否自动滚动到底部。
|
||||
3.`新增` props:`show-chat-loading-when-reload`,支持设置使用聊天记录模式中reload时是否显示chatLoading。
|
||||
4.`修复` 在聊天记录模式中`scrollIntoViewById`和`scrollIntoViewByNodeTop`无效的问题。
|
||||
5.`优化` 聊天记录模式底部安全区域针对键盘开启/关闭兼容处理。
|
||||
6.`优化` 更新内置的空数据图&加载失败图,感谢图鸟UI提供的免费可商用的空数据图和加载失败图!
|
||||
## 2.7.3(2024-01-10)
|
||||
1.`新增` 聊天记录模式支持虚拟列表&添加相关demo。
|
||||
2.`新增` nvue中list添加`@scrollend`监听。
|
||||
3.`优化` 聊天记录模式+vue第一页并且没有更多时不倒置列表。
|
||||
4.`优化` 聊天记录模式+nvue中数据不满屏时默认从顶部开始,不进行列表倒置。
|
||||
## 2.7.2(2024-01-09)
|
||||
1.`修复` `vue3+h5`中报错`uni.onKeyboardHeightChange is not a function`的问题。
|
||||
2.`优化` 聊天记录模式细节:表情面板在触摸列表时隐藏&添加隐藏动画。
|
||||
## 2.7.1(2024-01-08)
|
||||
1.`新增` `keyboardHeightChange` event,支持监听键盘高度改变。
|
||||
2.`新增` 聊天记录模式新增切换表情面板/键盘demo。
|
||||
3.`优化` 键盘弹出占位添加动画效果。
|
||||
## 2.7.0(2024-01-07)
|
||||
2024新年快乐!!祝大家在新的一年里工作顺利,事事顺心!
|
||||
1.`新增` 全新的聊天记录模式设计!将vue中的聊天记录模式与nvue中对齐,完全解决了聊天记录模式滚动到顶部加载更多在vue中抖动的问题,同时将聊天记录模式键盘自动弹出自动上推页面交由`z-paging`处理,解决了由此引发的各种问题,尤其是在微信小程序中导航栏被键盘顶出屏幕外的问题。如果您使用了`z-paging`的聊天记录模式,强烈建议更新,写法有一定变更,具体请参见demo。
|
||||
2.`新增` `swiper-demo`新增`onShow`时候调用reload演示。
|
||||
3.`修复` 修复滚动相关方法在微信小程序中首次滚动动画无效的问题。
|
||||
4.`修复` props设置单位,单位为px时报错的问题。
|
||||
5.`修复` 在某些情况下`z-paging`加载了但是未渲染时,reload无效的问题。
|
||||
6.`修复` 底部loading动画未生效的问题。
|
||||
## 2.8.4(2024-12-02)
|
||||
1.`修复` 在虚拟列表+vue2中,顶部占位采用transformY方案;在虚拟列表+vue3中,顶部占位采用view占位方案。以解决在vue2+微信小程序+安卓+兼容模式中,可能出现的虚拟列表闪动的问题。
|
||||
2.`修复` 在列表渲染时(尤其是在虚拟列表中)偶现的【点击加载更多】闪现的问题。
|
||||
3.`优化` 统一在RefresherStatus枚举中Loading取值。
|
||||
4.`优化` `defaultPageNo`&`defaultPageSize`修改为只允许number类型。
|
||||
5.`优化` 提升兼容性&细节优化。
|
||||
## 2.8.3(2024-11-27)
|
||||
1.`修复` `doInsertVirtualListItem`插入数据无效的问题。
|
||||
2.`优化` 提升兼容性&细节优化。
|
||||
## 2.8.2(2024-11-25)
|
||||
1.`优化` types中`ZPagingRef`和`ZPagingInstance`支持泛型。
|
||||
## 2.8.1(2024-11-24)
|
||||
1.`新增` 完整的`props`、`slots`、`methods`、`events`的typescript types声明,可在ts中获得绝佳的代码提示体验。
|
||||
2.`新增` `virtual-cell-id-prefix`:虚拟列表cell id的前缀,适用于一个页面有多个虚拟列表的情况,用以区分不同虚拟列表cell的id。
|
||||
3.`修复` 在vue3+(微信小程序或QQ小程序)中,使用非内置列表写法时,若`z-paging`在`swiper-item`标签内的情况下存在的无法获取slot插入的cell高度的问题。
|
||||
4.`修复` 在虚拟列表中分页数据小于1页时插入新数据,虚拟列表未生效的问题。
|
||||
5.`修复` 在虚拟列表中调用`refresh`时,cell的index计算不正确的问题。
|
||||
6.`修复` 在快手小程序中内容较少或空数据时`z-paging`未能铺满全屏的问题。
|
||||
7.`优化` `events`中的参数涉及枚举的部分,统一由之前的number类型修改为string类型,展示更直观!涉及的events:`@query`中的`from`参数;`@refresherStatusChange`中的`status`参数;`@loadingStatusChange`中的`status`参数;`slot=refresher`中的`refresherStatus`参数;`slot=chatLoading`中的`loadingMoreStatus`参数。更新版本请特别留意!
|
||||
## 2.8.0(2024-10-21)
|
||||
1.`新增` 全面支持鸿蒙Next。
|
||||
2.`修复` 设置了`refresher-complete-delay`后,在下拉刷新期间调用reload导致的无法再次下拉刷新的问题。
|
||||
3.`优化` 废弃虚拟列表transformY顶部占位方案,修改为空view占位。解决因使用旧方案导致的vue3中可能出现的虚拟列表闪动问题。提升虚拟列表的兼容性。
|
||||
|
||||
|
@ -55,8 +55,12 @@
|
||||
},
|
||||
// 底部加载更多文字
|
||||
ownLoadingMoreText() {
|
||||
const statusTextArr = [this.c.defaultText,this.c.loadingText,this.c.noMoreText,this.c.failText];
|
||||
return statusTextArr[this.finalStatus];
|
||||
return {
|
||||
[this.M.Default]: this.c.defaultText,
|
||||
[this.M.Loading]: this.c.loadingText,
|
||||
[this.M.NoMore]: this.c.noMoreText,
|
||||
[this.M.Fail]: this.c.failText,
|
||||
}[this.finalStatus];
|
||||
},
|
||||
// 底部加载更多状态
|
||||
finalStatus() {
|
||||
|
@ -57,14 +57,21 @@
|
||||
ts() {
|
||||
return this.defaultThemeStyle;
|
||||
},
|
||||
// 当前状态数组
|
||||
statusTextArr() {
|
||||
// 当前状态Map
|
||||
statusTextMap() {
|
||||
this.updateTime();
|
||||
return [this.defaultText, this.pullingText, this.refreshingText, this.completeText, this.goF2Text];
|
||||
const { R, defaultText, pullingText, refreshingText, completeText, goF2Text } = this;
|
||||
return {
|
||||
[R.Default]: defaultText,
|
||||
[R.ReleaseToRefresh]: pullingText,
|
||||
[R.Loading]: refreshingText,
|
||||
[R.Complete]: completeText,
|
||||
[R.GoF2]: goF2Text,
|
||||
};
|
||||
},
|
||||
// 当前状态文字
|
||||
currentTitle() {
|
||||
return this.statusTextArr[this.status] || this.defaultText;
|
||||
return this.statusTextMap[this.status] || this.defaultText;
|
||||
},
|
||||
// 左侧图片class
|
||||
leftImageClass() {
|
||||
|
@ -193,6 +193,10 @@
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.zp-back-to-top-img-inversion {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.zp-empty-view {
|
||||
/* #ifdef APP-NVUE */
|
||||
height: 100%;
|
||||
|
@ -89,10 +89,10 @@ export default {
|
||||
!callbacked && this._handleToTop();
|
||||
})
|
||||
},
|
||||
// 处理滚动到顶部
|
||||
// 处理滚动到顶部(聊天记录模式中为滚动到底部)
|
||||
_handleToTop() {
|
||||
!this.backToTopWithAnimate && this._checkShouldShowBackToTop(0);
|
||||
this.scrollToTop(this.backToTopWithAnimate);
|
||||
!this.useChatRecordMode ? this.scrollToTop(this.backToTopWithAnimate) : this.scrollToBottom(this.backToTopWithAnimate);
|
||||
},
|
||||
// 判断是否要显示返回顶部按钮
|
||||
_checkShouldShowBackToTop(scrollTop) {
|
||||
|
@ -82,7 +82,7 @@ export default {
|
||||
})
|
||||
},
|
||||
// 获取节点尺寸
|
||||
_getNodeClientRect(select, inDom = true, scrollOffset = false) {
|
||||
_getNodeClientRect(select, inDom = true, scrollOffset = false, inParent = false) {
|
||||
if (this.isReadyDestroy) {
|
||||
return Promise.resolve(false);
|
||||
};
|
||||
@ -106,7 +106,8 @@ export default {
|
||||
//#ifdef MP-ALIPAY
|
||||
inDom = false;
|
||||
//#endif
|
||||
let res = !!inDom ? uni.createSelectorQuery().in(inDom === true ? this : inDom) : uni.createSelectorQuery();
|
||||
const inDomObj = inParent ? this.$parent : this;
|
||||
let res = inDom || inParent ? uni.createSelectorQuery().in(inDomObj) : uni.createSelectorQuery();
|
||||
scrollOffset ? res.select(select).scrollOffset() : res.select(select).boundingClientRect();
|
||||
return new Promise((resolve, reject) => {
|
||||
res.exec(data => {
|
||||
|
@ -8,7 +8,7 @@ export default {
|
||||
props: {
|
||||
// 自定义初始的pageNo,默认为1
|
||||
defaultPageNo: {
|
||||
type: [Number, String],
|
||||
type: Number,
|
||||
default: u.gc('defaultPageNo', 1),
|
||||
observer: function(newVal) {
|
||||
this.pageNo = newVal;
|
||||
@ -16,7 +16,7 @@ export default {
|
||||
},
|
||||
// 自定义pageSize,默认为10
|
||||
defaultPageSize: {
|
||||
type: [Number, String],
|
||||
type: Number,
|
||||
default: u.gc('defaultPageSize', 10),
|
||||
validator: (value) => {
|
||||
if (value <= 0) u.consoleErr('default-page-size必须大于0!');
|
||||
@ -473,14 +473,10 @@ export default {
|
||||
if (!isLocal && tempIsUserPullDown && this.isFirstPage) {
|
||||
this.isUserPullDown = false;
|
||||
}
|
||||
if (!this.isFirstPage) {
|
||||
this.listRendering = true;
|
||||
this.$nextTick(() => {
|
||||
u.delay(() => this.listRendering = false);
|
||||
})
|
||||
} else {
|
||||
this.listRendering = false;
|
||||
}
|
||||
let dataTypeRes = this._checkDataType(data, success, isLocal);
|
||||
data = dataTypeRes.data;
|
||||
success = dataTypeRes.success;
|
||||
@ -511,7 +507,9 @@ export default {
|
||||
const localPageNo = this.defaultPageNo;
|
||||
const localPageSize = this.queryFrom !== Enum.QueryFrom.Refresh ? this.defaultPageSize : this.currentRefreshPageSize;
|
||||
this._localPagingQueryList(localPageNo, localPageSize, 0, res => {
|
||||
this.completeByTotal(res, this.totalLocalPagingList.length);
|
||||
u.delay(() => {
|
||||
this.completeByTotal(res, this.totalLocalPagingList.length);;
|
||||
}, 0)
|
||||
})
|
||||
} else {
|
||||
// 如果当前不是本地分页,则按照正常分页逻辑进行数据处理&emit数据
|
||||
@ -538,7 +536,7 @@ export default {
|
||||
this._callDataPromise(false);
|
||||
this.loadingStatus = Enum.More.Fail;
|
||||
this.isHandlingRefreshToPage = false;
|
||||
if (this.loadingType === Enum.LoadingType.LoadingMore) {
|
||||
if (this.loadingType === Enum.LoadingType.LoadMore) {
|
||||
this.pageNo --;
|
||||
}
|
||||
}
|
||||
|
@ -179,12 +179,16 @@ export default {
|
||||
// 是否显示自定义状态下的底部加载更多
|
||||
showLoadingMoreCustom() {
|
||||
return this._showLoadingMore('Custom');
|
||||
}
|
||||
},
|
||||
// 底部加载更多固定高度
|
||||
loadingMoreFixedHeight() {
|
||||
return u.addUnit('80rpx', this.unit);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// 页面滚动到底部时通知z-paging进行进一步处理
|
||||
pageReachBottom() {
|
||||
!this.useChatRecordMode && this._onLoadingMore('toBottom');
|
||||
!this.useChatRecordMode && this.toBottomLoadingMoreEnabled && this._onLoadingMore('toBottom');
|
||||
},
|
||||
// 手动触发上拉加载更多(非必须,可依据具体需求使用)
|
||||
doLoadMore(type) {
|
||||
@ -275,15 +279,15 @@ export default {
|
||||
// 如果是本地分页,则在组件内部对数据进行分页处理,不触发@query事件
|
||||
this._localPagingQueryList(this.pageNo, this.defaultPageSize, this.localPagingLoadingTime, res => {
|
||||
this.completeByTotal(res, this.totalLocalPagingList.length);
|
||||
this.queryFrom = Enum.QueryFrom.LoadingMore;
|
||||
this.queryFrom = Enum.QueryFrom.LoadMore;
|
||||
})
|
||||
} else {
|
||||
// emit @query相关加载更多事件
|
||||
this._emitQuery(this.pageNo, this.defaultPageSize, Enum.QueryFrom.LoadingMore);
|
||||
this._emitQuery(this.pageNo, this.defaultPageSize, Enum.QueryFrom.LoadMore);
|
||||
this._callMyParentQuery();
|
||||
}
|
||||
// 设置当前加载状态为底部加载更多状态
|
||||
this.loadingType = Enum.LoadingType.LoadingMore;
|
||||
this.loadingType = Enum.LoadingType.LoadMore;
|
||||
}
|
||||
},
|
||||
// (预处理)判断当没有更多数据且分页内容未超出z-paging时是否显示没有更多数据的view
|
||||
|
@ -66,7 +66,7 @@ export default {
|
||||
nLoadingMoreFixedHeight: false,
|
||||
nShowRefresherRevealHeight: 0,
|
||||
nOldShowRefresherRevealHeight: -1,
|
||||
nRefresherWidth: uni.upx2px(750),
|
||||
nRefresherWidth: u.rpx2px(750),
|
||||
nF2Opacity: 0
|
||||
}
|
||||
},
|
||||
|
@ -349,7 +349,7 @@ export default {
|
||||
return this.refresherTriggered;
|
||||
},
|
||||
showRefresher() {
|
||||
const showRefresher = this.finalRefresherEnabled && this.useCustomRefresher;
|
||||
const showRefresher = this.finalRefresherEnabled || this.useCustomRefresher && !this.useChatRecordMode;
|
||||
// #ifndef APP-NVUE
|
||||
this.active && this.customRefresherHeight === -1 && showRefresher && this.updateCustomRefresherHeight();
|
||||
// #endif
|
||||
@ -415,7 +415,7 @@ export default {
|
||||
this.$emit('onRestore');
|
||||
this.$emit('Restore');
|
||||
},
|
||||
// #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
|
||||
// #ifndef APP-PLUS || MP-WEIXIN || MP-QQ || H5
|
||||
// touch开始
|
||||
_refresherTouchstart(e) {
|
||||
this._handleListTouchstart();
|
||||
@ -439,8 +439,8 @@ export default {
|
||||
this._cleanRefresherEndTimeout();
|
||||
},
|
||||
|
||||
// 非appvue或微信小程序或QQ小程序或h5平台,使用js控制下拉刷新
|
||||
// #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
|
||||
// 非app-ios/android或微信小程序或QQ小程序或h5平台,使用js控制下拉刷新
|
||||
// #ifndef APP-IOS || APP-ANDROID || MP-WEIXIN || MP-QQ || H5
|
||||
// touch中
|
||||
_refresherTouchmove(e) {
|
||||
const currentTimeStamp = u.getTime();
|
||||
@ -515,7 +515,7 @@ export default {
|
||||
// 下拉刷新距离未超过阈值,显示默认状态
|
||||
this.refresherStatus = Enum.Refresher.Default;
|
||||
}
|
||||
// #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
|
||||
// #ifndef APP-PLUS || MP-WEIXIN || MP-QQ || H5
|
||||
// this.scrollEnable = false;
|
||||
// 通过transform控制下拉刷新view垂直偏移
|
||||
this.refresherTransform = `translateY(${moveDis}px)`;
|
||||
@ -523,7 +523,7 @@ export default {
|
||||
// #endif
|
||||
this.moveDis = moveDis;
|
||||
},
|
||||
// #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
|
||||
// #ifndef APP-PLUS || MP-WEIXIN || MP-QQ || H5
|
||||
// touch结束
|
||||
_refresherTouchend(e) {
|
||||
// 下拉刷新用户手离开屏幕,允许列表滚动
|
||||
@ -553,7 +553,7 @@ export default {
|
||||
this._refresherEnd();
|
||||
} else {
|
||||
// 如果是松手立即刷新状态,则触发下拉刷新
|
||||
// #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
|
||||
// #ifndef APP-PLUS || MP-WEIXIN || MP-QQ || H5
|
||||
this.refresherTransform = `translateY(${refresherThreshold}px)`;
|
||||
this.refresherTransition = 'transform .1s linear';
|
||||
// #endif
|
||||
@ -584,7 +584,7 @@ export default {
|
||||
_handleScrollViewBounce({ bounce }) {
|
||||
if (!this.usePageScroll && !this.scrollToTopBounceEnabled) {
|
||||
if (this.wxsScrollTop <= 5) {
|
||||
// #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5
|
||||
// #ifdef APP-PLUS || MP-WEIXIN || MP-QQ || H5
|
||||
this.refresherTransition = '';
|
||||
// #endif
|
||||
this.scrollEnable = bounce;
|
||||
@ -615,7 +615,9 @@ export default {
|
||||
// 下拉刷新结束
|
||||
_refresherEnd(shouldEndLoadingDelay = true, fromAddData = false, isUserPullDown = false, setLoading = true) {
|
||||
if (this.loadingType === Enum.LoadingType.Refresher) {
|
||||
// 计算当前下拉刷新结束需要延迟的时间
|
||||
const refresherCompleteDelay = (fromAddData && (isUserPullDown || this.showRefresherWhenReload)) ? this.refresherCompleteDelay : 0;
|
||||
// 如果延迟时间大于0,则展示刷新结束状态,否则直接展示默认状态
|
||||
const refresherStatus = refresherCompleteDelay > 0 ? Enum.Refresher.Complete : Enum.Refresher.Default;
|
||||
if (this.finalShowRefresherWhenReload) {
|
||||
const stackCount = this.refresherRevealStackCount;
|
||||
@ -624,7 +626,12 @@ export default {
|
||||
}
|
||||
this._cleanRefresherEndTimeout();
|
||||
this.refresherEndTimeout = u.delay(() => {
|
||||
// 更新下拉刷新状态
|
||||
this.refresherStatus = refresherStatus;
|
||||
// 如果当前下拉刷新状态不是刷新结束,则认为其不在刷新结束状态
|
||||
if (refresherStatus !== Enum.Refresher.Complete) {
|
||||
this.isRefresherInComplete = false;
|
||||
}
|
||||
}, this.refresherStatus !== Enum.Refresher.Default && refresherStatus === Enum.Refresher.Default ? this.refresherCompleteDuration : 0);
|
||||
|
||||
// #ifndef APP-NVUE
|
||||
@ -640,11 +647,11 @@ export default {
|
||||
animateDuration = this.refresherEndBounceEnabled ? this.refresherCompleteDuration / 1000 : this.refresherCompleteDuration / 3000;
|
||||
}
|
||||
this.refresherTransition = `transform ${fromAddData ? animateDuration : this.refresherDefaultDuration / 1000}s ${animateType}`;
|
||||
// #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
|
||||
// #ifndef APP-PLUS || MP-WEIXIN || MP-QQ || H5
|
||||
this.refresherTransform = 'translateY(0px)';
|
||||
this.currentDis = 0;
|
||||
// #endif
|
||||
// #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5
|
||||
// #ifdef APP-PLUS || MP-WEIXIN || MP-QQ || H5
|
||||
this.wxsPropType = this.refresherTransition + 'end' + u.getTime();
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
@ -748,10 +755,10 @@ export default {
|
||||
}
|
||||
// #endif
|
||||
this.refresherRevealStackCount ++;
|
||||
// #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
|
||||
// #ifndef APP-PLUS || MP-WEIXIN || MP-QQ || H5
|
||||
this.refresherTransform = `translateY(${this.finalRefresherThreshold}px)`;
|
||||
// #endif
|
||||
// #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5
|
||||
// #ifdef APP-PLUS || MP-WEIXIN || MP-QQ || H5
|
||||
this.wxsPropType = 'begin' + u.getTime();
|
||||
// #endif
|
||||
this.moveDis = this.finalRefresherThreshold;
|
||||
@ -762,10 +769,10 @@ export default {
|
||||
},
|
||||
// 触发下拉刷新
|
||||
_doRefresherLoad(isUserPullDown = true) {
|
||||
this._onRefresh(false,isUserPullDown);
|
||||
this._onRefresh(false, isUserPullDown);
|
||||
this.loading = true;
|
||||
},
|
||||
// #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
|
||||
// #ifndef APP-PLUS || MP-WEIXIN || MP-QQ || H5
|
||||
// 获取处理后的moveDis
|
||||
_getFinalRefresherMoveDis(moveDis) {
|
||||
let diffDis = moveDis - this.oldCurrentMoveDis;
|
||||
|
@ -240,7 +240,9 @@ export default {
|
||||
},
|
||||
// 当滚动到底部时
|
||||
_onScrollToLower(e) {
|
||||
(!e.detail || !e.detail.direction || e.detail.direction === 'bottom') && this._onLoadingMore(this.useChatRecordMode ? 'click' : 'toBottom')
|
||||
(!e.detail || !e.detail.direction || e.detail.direction === 'bottom')
|
||||
&& this.toBottomLoadingMoreEnabled
|
||||
&& this._onLoadingMore(this.useChatRecordMode ? 'click' : 'toBottom')
|
||||
},
|
||||
// 滚动到顶部
|
||||
_scrollToTop(animate = true, isPrivate = true) {
|
||||
|
@ -74,6 +74,11 @@ export default {
|
||||
type: [Number, String],
|
||||
default: u.gc('virtualScrollFps', 80)
|
||||
},
|
||||
// 虚拟列表cell id的前缀,适用于一个页面有多个虚拟列表的情况,用以区分不同虚拟列表cell的id,注意:请勿传数字或以数字开头的字符串。如设置为list1,则cell的id应为:list1-zp-id-${item.zp_index}
|
||||
virtualCellIdPrefix: {
|
||||
type: String,
|
||||
default: u.gc('virtualCellIdPrefix', '')
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@ -110,6 +115,10 @@ export default {
|
||||
virtualList(newVal){
|
||||
this.$emit('update:virtualList', newVal);
|
||||
this.$emit('virtualListChange', newVal);
|
||||
},
|
||||
// 监听虚拟列表顶部占位高度改变并emit
|
||||
virtualPlaceholderTopHeight(newVal) {
|
||||
this.$emit('virtualTopHeightChange', newVal);
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -139,17 +148,31 @@ export default {
|
||||
finalFixedCellHeight() {
|
||||
return u.convertToPx(this.fixedCellHeight);
|
||||
},
|
||||
fianlVirtualCellIdPrefix() {
|
||||
const prefix = this.virtualCellIdPrefix ? this.virtualCellIdPrefix + '-' : '';
|
||||
return prefix + 'zp-id';
|
||||
},
|
||||
finalPlaceholderTopHeightStyle() {
|
||||
// #ifdef VUE2
|
||||
return { transform: this.virtualPlaceholderTopHeight > 0 ? `translateY(${this.virtualPlaceholderTopHeight}px)` : 'none' };
|
||||
// #endif
|
||||
return {};
|
||||
},
|
||||
virtualRangePageHeight(){
|
||||
return this.finalVirtualPageHeight * this.preloadPage;
|
||||
},
|
||||
virtualScrollDisTimeStamp() {
|
||||
return 1000 / this.virtualScrollFps;
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 在使用动态高度虚拟列表时,若在列表数组中需要插入某个item,需要调用此方法;item:需要插入的item,index:插入的cell位置,若index为2,则插入的item在原list的index=1之后,index从0开始
|
||||
doInsertVirtualListItem(item, index) {
|
||||
if (this.cellHeightMode !== Enum.CellHeightMode.Dynamic) return;
|
||||
this.realTotalData.splice(index, 0, item);
|
||||
// #ifdef VUE3
|
||||
this.realTotalData = [...this.realTotalData];
|
||||
// #endif
|
||||
this.virtualItemInsertedCount ++;
|
||||
if (!item || Object.prototype.toString.call(item) !== '[object Object]') {
|
||||
item = { item };
|
||||
@ -162,7 +185,7 @@ export default {
|
||||
while (retryCount <= 10) {
|
||||
await u.wait(c.delayTime);
|
||||
|
||||
const cellNode = await this._getNodeClientRect(`#zp-id-${item[cellIndexKey]}`, this.finalUseInnerList);
|
||||
const cellNode = await this._getVirtualCellNodeByIndex(item[cellIndexKey]);
|
||||
// 如果获取当前cell的节点信息失败,则重试(不超过10次)
|
||||
if (!cellNode) {
|
||||
retryCount ++;
|
||||
@ -191,12 +214,12 @@ export default {
|
||||
}
|
||||
})
|
||||
},
|
||||
// 在使用动态高度虚拟列表时,手动更新指定cell的缓存高度(当cell高度在初始化之后再次改变时调用);index:需要更新的cell在列表中的位置,从0开始
|
||||
// 在使用动态高度虚拟列表时,手动更新指定cell的缓存高度(当cell高度在初始化之后再次改变后调用);index:需要更新的cell在列表中的位置,从0开始
|
||||
didUpdateVirtualListCell(index) {
|
||||
if (this.cellHeightMode !== Enum.CellHeightMode.Dynamic) return;
|
||||
const currentNode = this.virtualHeightCacheList[index];
|
||||
this.$nextTick(() => {
|
||||
this._getNodeClientRect(`#zp-id-${index}`, this.finalUseInnerList).then(cellNode => {
|
||||
this._getVirtualCellNodeByIndex(index).then(cellNode => {
|
||||
// 更新当前cell的高度
|
||||
const cellNodeHeight = cellNode ? cellNode[0].height : 0;
|
||||
const heightDis = cellNodeHeight - currentNode.height;
|
||||
@ -261,7 +284,7 @@ export default {
|
||||
if (!this.finalFixedCellHeight) {
|
||||
this.$nextTick(() => {
|
||||
u.delay(() => {
|
||||
this._getNodeClientRect(`#zp-id-${0}`,this.finalUseInnerList).then(cellNode => {
|
||||
this._getVirtualCellNodeByIndex(0).then(cellNode => {
|
||||
if (!cellNode) {
|
||||
if (this.getCellHeightRetryCount.fixed > 10) return;
|
||||
this.getCellHeightRetryCount.fixed ++;
|
||||
@ -287,7 +310,7 @@ export default {
|
||||
this.$nextTick(() => {
|
||||
u.delay(async () => {
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
const cellNode = await this._getNodeClientRect(`#zp-id-${list[i][this.virtualCellIndexKey]}`, this.finalUseInnerList);
|
||||
const cellNode = await this._getVirtualCellNodeByIndex(list[i][this.virtualCellIndexKey]);
|
||||
const currentHeight = cellNode ? cellNode[0].height : 0;
|
||||
if (!cellNode) {
|
||||
if (this.getCellHeightRetryCount.dynamic <= 10) {
|
||||
@ -328,8 +351,8 @@ export default {
|
||||
_setCellIndex(list, dataFrom = 'bottom') {
|
||||
let currentItemIndex = 0;
|
||||
const cellIndexKey = this.virtualCellIndexKey;
|
||||
([Enum.QueryFrom.Refresh, Enum.QueryFrom.Reload].indexOf(this.queryFrom) >= 0) && this._resetDynamicListState();
|
||||
if (this.totalData.length) {
|
||||
dataFrom === 'bottom' && ([Enum.QueryFrom.Refresh, Enum.QueryFrom.Reload].indexOf(this.queryFrom) >= 0) && this._resetDynamicListState();
|
||||
if (this.totalData.length && this.queryFrom !== Enum.QueryFrom.Refresh) {
|
||||
if (dataFrom === 'bottom') {
|
||||
currentItemIndex = this.realTotalData.length;
|
||||
const lastItem = this.realTotalData.length ? this.realTotalData.slice(-1)[0] : null;
|
||||
@ -504,6 +527,20 @@ export default {
|
||||
})
|
||||
}
|
||||
},
|
||||
// 获取对应index的虚拟列表cell节点信息
|
||||
_getVirtualCellNodeByIndex(index) {
|
||||
let inParent = false;
|
||||
// 在vue3+(微信小程序或QQ小程序)中,使用非内置列表写法时,若z-paging在swiper-item内存在无法获取slot插入的cell高度的问题
|
||||
// 通过uni.createSelectorQuery().in(this.$parent)来解决此问题
|
||||
// #ifdef VUE3
|
||||
// #ifdef MP-WEIXIN || MP-QQ
|
||||
if (this.forceCloseInnerList) {
|
||||
inParent = true;
|
||||
}
|
||||
// #endif
|
||||
// #endif
|
||||
return this._getNodeClientRect(`#${this.fianlVirtualCellIdPrefix}-${index}`, this.finalUseInnerList, false, inParent);
|
||||
},
|
||||
// 处理使用内置列表时点击了cell事件
|
||||
_innerCellClick(item, index) {
|
||||
this.$emit('innerCellClick', item, index);
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
export default {
|
||||
// 当前版本号
|
||||
version: '2.7.11',
|
||||
version: '2.8.3',
|
||||
// 延迟操作的通用时间
|
||||
delayTime: 100,
|
||||
// 请求失败时候全局emit使用的key
|
||||
|
@ -1,32 +1,32 @@
|
||||
// [z-paging]枚举
|
||||
|
||||
export default {
|
||||
// 当前加载类型 0.下拉刷新 1.上拉加载更多
|
||||
// 当前加载类型 refresher:下拉刷新 load-more:上拉加载更多
|
||||
LoadingType: {
|
||||
Refresher: 0,
|
||||
LoadingMore: 1
|
||||
Refresher: 'refresher',
|
||||
LoadMore: 'load-more'
|
||||
},
|
||||
// 下拉刷新状态 0.默认状态 1.松手立即刷新 2.刷新中 3.刷新结束 4.松手进入二楼
|
||||
// 下拉刷新状态 default:默认状态 release-to-refresh:松手立即刷新 loading:刷新中 complete:刷新结束 go-f2:松手进入二楼
|
||||
Refresher: {
|
||||
Default: 0,
|
||||
ReleaseToRefresh: 1,
|
||||
Loading: 2,
|
||||
Complete: 3,
|
||||
GoF2: 4
|
||||
Default: 'default',
|
||||
ReleaseToRefresh: 'release-to-refresh',
|
||||
Loading: 'loading',
|
||||
Complete: 'complete',
|
||||
GoF2: 'go-f2'
|
||||
},
|
||||
// 底部加载更多状态 0.默认状态 1.加载中 2.没有更多数据 3.加载失败
|
||||
// 底部加载更多状态 default:默认状态 loading:加载中 no-more:没有更多数据 fail:加载失败
|
||||
More: {
|
||||
Default: 0,
|
||||
Loading: 1,
|
||||
NoMore: 2,
|
||||
Fail: 3
|
||||
Default: 'default',
|
||||
Loading: 'loading',
|
||||
NoMore: 'no-more',
|
||||
Fail: 'fail'
|
||||
},
|
||||
// @query触发来源 0.用户主动下拉刷新 1.通过reload触发 2.通过refresh触发 3.通过滚动到底部加载更多或点击底部加载更多触发
|
||||
// @query触发来源 user-pull-down:用户主动下拉刷新 reload:通过reload触发 refresh:通过refresh触发 load-more:通过滚动到底部加载更多或点击底部加载更多触发
|
||||
QueryFrom: {
|
||||
UserPullDown: 0,
|
||||
Reload: 1,
|
||||
Refresh: 2,
|
||||
LoadingMore: 3
|
||||
UserPullDown: 'user-pull-down',
|
||||
Reload: 'reload',
|
||||
Refresh: 'refresh',
|
||||
LoadMore: 'load-more'
|
||||
},
|
||||
// 虚拟列表cell高度模式
|
||||
CellHeightMode: {
|
||||
|
@ -50,8 +50,6 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
// --------------静态资源---------------
|
||||
base64Arrow: zStatic.base64Arrow,
|
||||
base64Flower: zStatic.base64Flower,
|
||||
base64BackToTop: zStatic.base64BackToTop,
|
||||
|
||||
// -------------全局数据相关--------------
|
||||
@ -219,6 +217,9 @@ export default {
|
||||
this.systemInfo = uni.getSystemInfoSync();
|
||||
// 初始化z-paging高度
|
||||
!this.usePageScroll && this.autoHeight && this._setAutoHeight();
|
||||
// #ifdef MP-KUAISHOU
|
||||
this._setFullScrollViewInHeight();
|
||||
// #endif
|
||||
this.loaded = true;
|
||||
u.delay(() => {
|
||||
// 更新fixed模式下z-paging的布局,主要是更新windowTop、windowBottom
|
||||
@ -408,10 +409,7 @@ export default {
|
||||
},
|
||||
// 设置z-paging高度
|
||||
async _setAutoHeight(shouldFullHeight = true, scrollViewNode = null) {
|
||||
let heightKey = 'min-height';
|
||||
// #ifndef APP-NVUE
|
||||
heightKey = 'min-height';
|
||||
// #endif
|
||||
const heightKey = 'min-height';
|
||||
try {
|
||||
if (shouldFullHeight) {
|
||||
// 如果需要铺满全屏,则计算当前全屏可是区域的高度
|
||||
@ -432,6 +430,16 @@ export default {
|
||||
}
|
||||
} catch (e) {}
|
||||
},
|
||||
// #ifdef MP-KUAISHOU
|
||||
// 设置scroll-view内容器的最小高度等于scroll-view的高度(为了解决在快手小程序中内容较少时scroll-view内容器高度无法铺满scroll-view的问题)
|
||||
_setFullScrollViewInHeight() {
|
||||
try {
|
||||
// 如果需要铺满全屏,则计算当前全屏可是区域的高度
|
||||
const scrollViewNode = await this._getNodeClientRect('.zp-scroll-view');
|
||||
scrollViewNode && this.$set(this.scrollViewInStyle, 'min-height', scrollViewNode[0].height + 'px');
|
||||
} catch (e) {}
|
||||
},
|
||||
// #endif
|
||||
// 组件销毁后续处理
|
||||
_handleUnmounted() {
|
||||
this.active = false;
|
||||
|
@ -8,6 +8,10 @@ let config = null;
|
||||
let configLoaded = false;
|
||||
const timeoutMap = {};
|
||||
|
||||
// #ifdef APP-HARMONY
|
||||
let screenWidth = 0;
|
||||
// #endif
|
||||
|
||||
// 获取默认配置信息
|
||||
function gc(key, defaultValue) {
|
||||
// 这里return一个函数以解决在vue3+appvue中,props默认配置读取在main.js之前执行导致uni.$zp全局配置无效的问题。相当于props的default中传入一个带有返回值的函数
|
||||
@ -119,12 +123,25 @@ function convertToPx(text) {
|
||||
text = text.replace('px', '');
|
||||
}
|
||||
if (!isNaN(text)) {
|
||||
if (isRpx) return Number(uni.upx2px(text));
|
||||
if (isRpx) return Number(rpx2px(text));
|
||||
return Number(text);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// rpx => px,兼容鸿蒙
|
||||
function rpx2px(rpx) {
|
||||
// #ifdef APP-HARMONY
|
||||
if (!screenWidth) {
|
||||
screenWidth = uni.getSystemInfoSync().screenWidth;
|
||||
}
|
||||
return (screenWidth * Number.parseFloat(rpx)) / 750;
|
||||
// #endif
|
||||
// #ifndef APP-HARMONY
|
||||
return uni.upx2px(rpx);
|
||||
// #endif
|
||||
}
|
||||
|
||||
// 获取当前时间
|
||||
function getTime() {
|
||||
return (new Date()).getTime();
|
||||
@ -266,5 +283,6 @@ export default {
|
||||
wait,
|
||||
isPromise,
|
||||
addUnit,
|
||||
deepCopy
|
||||
deepCopy,
|
||||
rpx2px
|
||||
};
|
||||
|
@ -4,13 +4,13 @@
|
||||
/ /_____| |_) | (_| | (_| | | | | | (_| |
|
||||
/___| | .__/ \__,_|\__, |_|_| |_|\__, |
|
||||
|_| |___/ |___/
|
||||
v2.7.11 (2024-06-28)
|
||||
by ZXLee
|
||||
v2.8.3 (2024-11-27)
|
||||
@author ZXLee <admin@zxlee.cn>
|
||||
-->
|
||||
<!-- 文档地址:https://z-paging.zxlee.cn -->
|
||||
<!-- github地址:https://github.com/SmileZXLee/uni-z-paging -->
|
||||
<!-- dcloud地址:https://ext.dcloud.net.cn/plugin?id=3935 -->
|
||||
<!-- 反馈QQ群:790460711(已满)、371624008 -->
|
||||
<!-- 反馈QQ群:343409055 -->
|
||||
|
||||
<template name="z-paging">
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
@ -44,17 +44,17 @@ by ZXLee
|
||||
@scrolltoupper="_onScrollToUpper" @refresherrestore="_onRestore" @refresherrefresh="_onRefresh(true)"
|
||||
>
|
||||
<view class="zp-paging-touch-view"
|
||||
<!-- #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5 -->
|
||||
<!-- #ifndef APP-PLUS || MP-WEIXIN || MP-QQ || H5 -->
|
||||
@touchstart="_refresherTouchstart" @touchmove="_refresherTouchmove" @touchend="_refresherTouchend" @touchcancel="_refresherTouchend"
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5 -->
|
||||
<!-- #ifdef APP-PLUS || MP-WEIXIN || MP-QQ || H5 -->
|
||||
@touchstart="pagingWxs.touchstart" @touchmove="pagingWxs.touchmove" @touchend="pagingWxs.touchend" @touchcancel="pagingWxs.touchend"
|
||||
@mousedown="pagingWxs.mousedown" @mousemove="pagingWxs.mousemove" @mouseup="pagingWxs.mouseup" @mouseleave="pagingWxs.mouseleave"
|
||||
<!-- #endif -->
|
||||
>
|
||||
<view v-if="finalRefresherFixedBacHeight>0" class="zp-fixed-bac-view" :style="[{'background': refresherFixedBackground,'height': `${finalRefresherFixedBacHeight}px`}]"></view>
|
||||
<view class="zp-paging-main" :style="[scrollViewInStyle,{'transform': finalRefresherTransform,'transition': refresherTransition}]"
|
||||
<!-- #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5 -->
|
||||
<!-- #ifdef APP-PLUS || MP-WEIXIN || MP-QQ || H5 -->
|
||||
:change:prop="pagingWxs.propObserver" :prop="wxsPropType"
|
||||
:data-refresherThreshold="finalRefresherThreshold" :data-refresherF2Enabled="refresherF2Enabled" :data-refresherF2Threshold="finalRefresherF2Threshold" :data-isIos="isIos"
|
||||
:data-loading="loading||isRefresherInComplete" :data-useChatRecordMode="useChatRecordMode"
|
||||
@ -63,7 +63,7 @@ by ZXLee
|
||||
:data-refresherAecc="refresherAngleEnableChangeContinued" :data-usePageScroll="usePageScroll" :data-watchTouchDirectionChange="watchTouchDirectionChange"
|
||||
:data-oldIsTouchmoving="isTouchmoving" :data-refresherOutRate="finalRefresherOutRate" :data-refresherPullRate="finalRefresherPullRate" :data-hasTouchmove="hasTouchmove"
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-VUE || H5 -->
|
||||
<!-- #ifdef APP-PLUS || H5 -->
|
||||
:change:renderPropIsIosAndH5="pagingRenderjs.renderPropIsIosAndH5Change" :renderPropIsIosAndH5="isIosAndH5"
|
||||
<!-- #endif -->
|
||||
>
|
||||
@ -88,15 +88,19 @@ by ZXLee
|
||||
<!-- 全屏Loading -->
|
||||
<slot v-if="showLoading&&zSlots.loading&&!loadingFullFixed" name="loading" />
|
||||
<!-- 主体内容 -->
|
||||
<view class="zp-paging-container-content" :style="[{transform:virtualPlaceholderTopHeight>0?`translateY(${virtualPlaceholderTopHeight}px)`:'none'},finalPagingContentStyle]">
|
||||
<view class="zp-paging-container-content" :style="[finalPlaceholderTopHeightStyle,finalPagingContentStyle]">
|
||||
<!-- #ifdef VUE3 -->
|
||||
<!-- 虚拟列表顶部占位view -->
|
||||
<view v-if="useVirtualList" class="zp-virtual-placeholder" :style="[{height:virtualPlaceholderTopHeight+'px'}]"/>
|
||||
<!-- #endif -->
|
||||
<slot />
|
||||
<!-- 内置列表&虚拟列表 -->
|
||||
<template v-if="finalUseInnerList">
|
||||
<slot name="header"/>
|
||||
<view class="zp-list-container" :style="[innerListStyle]">
|
||||
<template v-if="finalUseVirtualList">
|
||||
<view class="zp-list-cell" :style="[innerCellStyle]" :id="`zp-id-${item[virtualCellIndexKey]}`" v-for="(item,index) in virtualList" :key="item['zp_unique_index']" @click="_innerCellClick(item,virtualTopRangeIndex+index)">
|
||||
<view v-if="useCompatibilityMode">使用兼容模式请在组件源码z-paging.vue第99行中注释这一行,并打开下面一行注释</view>
|
||||
<view class="zp-list-cell" :style="[innerCellStyle]" :id="`${fianlVirtualCellIdPrefix}-${item[virtualCellIndexKey]}`" v-for="(item,index) in virtualList" :key="item['zp_unique_index']" @click="_innerCellClick(item,virtualTopRangeIndex+index)">
|
||||
<view v-if="useCompatibilityMode">使用兼容模式请在组件源码z-paging.vue第103行中注释这一行,并打开下面一行注释</view>
|
||||
<!-- <zp-public-virtual-cell v-if="useCompatibilityMode" :extraData="extraData" :item="item" :index="virtualTopRangeIndex+index" /> -->
|
||||
<slot v-else name="cell" :item="item" :index="virtualTopRangeIndex+index"/>
|
||||
</view>
|
||||
@ -170,7 +174,7 @@ by ZXLee
|
||||
<!-- 点击返回顶部view -->
|
||||
<view v-if="showBackToTopClass" :class="finalBackToTopClass" :style="[finalBackToTopStyle]" @click.stop="_backToTopClick">
|
||||
<slot v-if="zSlots.backToTop" name="backToTop" />
|
||||
<image v-else class="zp-back-to-top-img" :src="backToTopImg.length?backToTopImg:base64BackToTop" />
|
||||
<image v-else class="zp-back-to-top-img" :class="{'zp-back-to-top-img-inversion': useChatRecordMode&&!backToTopImg.length}" :src="backToTopImg.length?backToTopImg:base64BackToTop" />
|
||||
</view>
|
||||
<!-- 全屏Loading(铺满z-paging并固定) -->
|
||||
<view v-if="showLoading&&zSlots.loading&&loadingFullFixed" class="zp-loading-fixed">
|
||||
@ -261,7 +265,7 @@ by ZXLee
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<view :style="nLoadingMoreFixedHeight?{height:loadingMoreCustomStyle&&loadingMoreCustomStyle.height?loadingMoreCustomStyle.height:'80rpx'}:{}">
|
||||
<view :style="nLoadingMoreFixedHeight?{height:loadingMoreCustomStyle&&loadingMoreCustomStyle.height?loadingMoreCustomStyle.height:loadingMoreFixedHeight}:{}">
|
||||
<slot v-if="showLoadingMoreDefault" name="loadingMoreDefault" />
|
||||
<slot v-else-if="showLoadingMoreLoading" name="loadingMoreLoading" />
|
||||
<slot v-else-if="showLoadingMoreNoMore" name="loadingMoreNoMore" />
|
||||
@ -298,7 +302,7 @@ by ZXLee
|
||||
<!-- 点击返回顶部view -->
|
||||
<view v-if="showBackToTopClass" :class="finalBackToTopClass" :style="[finalBackToTopStyle]" @click.stop="_backToTopClick">
|
||||
<slot v-if="zSlots.backToTop" name="backToTop" />
|
||||
<image v-else class="zp-back-to-top-img" :src="backToTopImg.length?backToTopImg:base64BackToTop" />
|
||||
<image v-else class="zp-back-to-top-img" :class="{'zp-back-to-top-img-inversion': useChatRecordMode&&!backToTopImg.length}" :src="backToTopImg.length?backToTopImg:base64BackToTop" />
|
||||
</view>
|
||||
<!-- 全屏Loading(铺满z-paging并固定) -->
|
||||
<view v-if="showLoading&&zSlots.loading&&loadingFullFixed" class="zp-loading-fixed">
|
||||
@ -307,9 +311,12 @@ by ZXLee
|
||||
</component>
|
||||
<!-- #endif -->
|
||||
</template>
|
||||
<!-- #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5 -->
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
<!-- #ifdef APP-PLUS || MP-WEIXIN || MP-QQ || H5 -->
|
||||
<script src="./wxs/z-paging-wxs.wxs" module="pagingWxs" lang="wxs"></script>
|
||||
<!-- #endif -->
|
||||
<!-- #endif -->
|
||||
|
||||
<script module="pagingRenderjs" lang="renderjs">
|
||||
import pagingRenderjs from './wxs/z-paging-renderjs.js';
|
||||
/**
|
||||
@ -332,9 +339,11 @@ by ZXLee
|
||||
*/
|
||||
export default {
|
||||
name:"z-paging",
|
||||
// #ifdef APP-VUE || H5
|
||||
// #ifndef APP-NVUE
|
||||
// #ifdef APP-PLUS || H5
|
||||
mixins: [pagingRenderjs],
|
||||
// #endif
|
||||
// #endif
|
||||
}
|
||||
</script>
|
||||
<script src="./js/z-paging-main.js" />
|
||||
|
@ -2,8 +2,8 @@
|
||||
"id": "z-paging",
|
||||
"name": "z-paging",
|
||||
"displayName": "【z-paging下拉刷新、上拉加载】高性能,全平台兼容。支持虚拟列表,分页全自动处理",
|
||||
"version": "2.7.11",
|
||||
"description": "超简单、低耦合!使用wxs+renderjs实现。支持自定义下拉刷新、上拉加载更多、虚拟列表、下拉进入二楼、自动管理空数据图、无闪动聊天分页、本地分页、国际化等100+项配置",
|
||||
"version": "2.8.4",
|
||||
"description": "超简单、低耦合!使用wxs+renderjs实现。支持自定义下拉刷新、上拉加载更多、虚拟列表、下拉进入二楼、自动管理空数据图、无闪动聊天分页、本地分页、国际化等数百项配置",
|
||||
"keywords": [
|
||||
"下拉刷新",
|
||||
"上拉加载",
|
||||
@ -12,7 +12,6 @@
|
||||
"虚拟列表"
|
||||
],
|
||||
"repository": "https://github.com/SmileZXLee/uni-z-paging",
|
||||
"types": "global.d.ts",
|
||||
"engines": {
|
||||
"HBuilderX": "^3.0.7"
|
||||
},
|
||||
@ -48,7 +47,9 @@
|
||||
"client": {
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "y"
|
||||
"app-nvue": "y",
|
||||
"app-harmony": "u",
|
||||
"app-uvue": "u"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
|
@ -4,7 +4,7 @@
|
||||
<img alt="logo" src="https://z-paging.zxlee.cn/img/title-logo.png" height="100" style="margin-bottom: 50px;" />
|
||||
</p>
|
||||
|
||||
[](https://github.com/SmileZXLee/uni-z-paging) [](https://en.wikipedia.org/wiki/MIT_License)
|
||||
[](https://github.com/SmileZXLee/uni-z-paging) [](https://en.wikipedia.org/wiki/MIT_License)
|
||||
<img height="0" width="0" src="https://api.z-notify.zxlee.cn/v1/public/statistics/8293556910106066944/addOnly?from=uni" />
|
||||
|
||||
`z-paging-x`现已支持uniapp x,持续完善中,插件地址👉🏻 [https://ext.dcloud.net.cn/plugin?name=z-paging-x](https://ext.dcloud.net.cn/plugin?name=z-paging-x)
|
||||
@ -19,14 +19,15 @@
|
||||
* 【低耦合,低侵入】分页自动管理。在page中无需处理任何分页相关逻辑,无需在data中定义任何分页相关变量,全由z-paging内部处理。
|
||||
* 【超灵活,支持各种类型自定义】支持自定义下拉刷新,自定义上拉加载更多等各种自定义效果;支持使用内置自动分页,同时也支持通过监听下拉刷新和滚动到底部事件自行处理;支持使用自带全屏布局规范,同时也支持将z-paging自由放在任意容器中。
|
||||
* 【功能丰富】支持国际化,支持自定义且自动管理空数据图,支持主题模式切换,支持本地分页,支持无闪动聊天分页模式,支持展示最后更新时间,支持吸顶效果,支持内部scroll-view滚动与页面滚动,支持一键滚动到顶部,支持下拉进入二楼等诸多功能。
|
||||
* 【全平台兼容】支持vue、nvue,vue2、vue3,支持h5、app及各家小程序。
|
||||
* 【高性能】在app-vue、h5、微信小程序、QQ小程序上使用wxs+renderjs从视图层实现下拉刷新;支持虚拟列表,轻松渲染万级数据!
|
||||
* 【【全平台兼容】支持vue&nvue,vue2&vue3,js&ts,支持h5、app、鸿蒙Next及各家小程序。
|
||||
* 【高性能】在app-vue、h5、微信小程序、QQ小程序上使用wxs+renderjs在视图层实现下拉刷新;支持虚拟列表,轻松渲染百万级列表数据!
|
||||
|
||||
***
|
||||
### 反馈qq群
|
||||
* 官方1群`已满`:[790460711](https://jq.qq.com/?_wv=1027&k=vU2fKZZH)
|
||||
|
||||
* 官方2群:[371624008](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=avPmibADf2TNi4LxkIwjCE5vbfXpa-r1&authKey=dQ%2FVDAR87ONxI4b32Py%2BvmXbhnopjHN7%2FJPtdsqJdsCPFZB6zDQ17L06Uh0kITUZ&noverify=0&group_code=371624008)
|
||||
* 官方2群`已满`:[371624008](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=avPmibADf2TNi4LxkIwjCE5vbfXpa-r1&authKey=dQ%2FVDAR87ONxI4b32Py%2BvmXbhnopjHN7%2FJPtdsqJdsCPFZB6zDQ17L06Uh0kITUZ&noverify=0&group_code=371624008)
|
||||
* 官方3群:[343409055](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=sIaNqiCMIjxGQVksjytCw6R8DSiibHR7&authKey=pp995q8ZzFtl5F2xUwvvceP24QTcguWW%2FRVoDnMa8JZF4L2DmS%2B%2FV%2F5sYrcgPsmW&noverify=0&group_code=343409055)
|
||||
|
||||
***
|
||||
|
||||
|
11
src/uni_modules/z-paging/types/comps.d.ts
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
['z-paging']: typeof import('./comps/z-paging')['ZPaging']
|
||||
['z-paging-swiper']: typeof import('./comps/z-paging-swiper')['ZPagingSwiper']
|
||||
['z-paging-swiper-item']: typeof import('./comps/z-paging-swiper-item')['ZPagingSwiperItem']
|
||||
['z-paging-empty-view']: typeof import('./comps/z-paging-empty-view')['ZPagingEmptyView']
|
||||
['z-paging-cell']: typeof import('./comps/z-paging-cell')['ZPagingCell']
|
||||
}
|
||||
}
|
||||
|
||||
export {}
|
9
src/uni_modules/z-paging/types/comps/_common.d.ts
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
export interface AllowedComponentProps {
|
||||
class?: unknown;
|
||||
style?: unknown;
|
||||
}
|
||||
|
||||
export interface VNodeProps {
|
||||
key?: string | number | symbol;
|
||||
ref?: unknown;
|
||||
}
|
29
src/uni_modules/z-paging/types/comps/z-paging-cell.d.ts
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
import { AllowedComponentProps, VNodeProps } from './_common'
|
||||
|
||||
// ****************************** Props ******************************
|
||||
declare interface ZPagingCellProps {
|
||||
/**
|
||||
* z-paging-cell样式
|
||||
*/
|
||||
cellStyle?: Record<string, any>
|
||||
}
|
||||
|
||||
// ****************************** Slots ******************************
|
||||
declare interface ZPagingCellSlots {
|
||||
// ******************** 主体布局Slot ********************
|
||||
/**
|
||||
* 默认插入的view
|
||||
*/
|
||||
['default']?: () => any
|
||||
}
|
||||
|
||||
declare interface _ZPagingCell {
|
||||
new (): {
|
||||
$props: AllowedComponentProps &
|
||||
VNodeProps &
|
||||
ZPagingCellProps
|
||||
$slots: ZPagingCellSlots
|
||||
}
|
||||
}
|
||||
|
||||
export declare const ZPagingCell: _ZPagingCell
|
95
src/uni_modules/z-paging/types/comps/z-paging-empty-view.d.ts
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
import { AllowedComponentProps, VNodeProps } from './_common'
|
||||
|
||||
// ****************************** Props ******************************
|
||||
declare interface ZPagingEmptyViewProps {
|
||||
/**
|
||||
* 空数据图片是否铺满z-paging,默认为是。若设置为否,则为填充满z-paging的剩余部分
|
||||
* @default false
|
||||
* @since 2.0.3
|
||||
*/
|
||||
emptyViewFixed?: boolean;
|
||||
|
||||
/**
|
||||
* 空数据图描述文字
|
||||
* @default "没有数据哦~"
|
||||
*/
|
||||
emptyViewText?: string;
|
||||
|
||||
/**
|
||||
* 空数据图图片,默认使用z-paging内置的图片
|
||||
* - 建议使用绝对路径,开头不要添加"@",请以"/"开头
|
||||
*/
|
||||
emptyViewImg?: string;
|
||||
|
||||
/**
|
||||
* 空数据图点击重新加载文字
|
||||
* @default "重新加载"
|
||||
* @since 1.6.7
|
||||
*/
|
||||
emptyViewReloadText?: string;
|
||||
|
||||
/**
|
||||
* 空数据图样式,可设置空数据view的top等
|
||||
* - 如果空数据图不是fixed布局,则此处是`margin-top`
|
||||
*/
|
||||
emptyViewStyle?: Record<string, any>;
|
||||
|
||||
/**
|
||||
* 空数据图img样式
|
||||
*/
|
||||
emptyViewImgStyle?: Record<string, any>;
|
||||
|
||||
/**
|
||||
* 空数据图描述文字样式
|
||||
*/
|
||||
emptyViewTitleStyle?: Record<string, any>;
|
||||
|
||||
/**
|
||||
* 空数据图重新加载按钮样式
|
||||
* @since 1.6.7
|
||||
*/
|
||||
emptyViewReloadStyle?: Record<string, any>;
|
||||
|
||||
/**
|
||||
* 是否显示空数据图重新加载按钮(无数据时)
|
||||
* @default false
|
||||
* @since 1.6.7
|
||||
*/
|
||||
showEmptyViewReload?: boolean;
|
||||
|
||||
/**
|
||||
* 是否是加载失败
|
||||
* @default false
|
||||
*/
|
||||
isLoadFailed?: boolean;
|
||||
|
||||
/**
|
||||
* 空数据图中布局的单位
|
||||
* @default 'rpx'
|
||||
* @since 2.6.7
|
||||
*/
|
||||
unit?: 'rpx' | 'px';
|
||||
|
||||
// ****************************** Events ******************************
|
||||
/**
|
||||
* 点击了重新加载按钮
|
||||
*/
|
||||
onReload?: () => void
|
||||
|
||||
/**
|
||||
* 点击了空数据view
|
||||
* @since 2.3.3
|
||||
*/
|
||||
onViewClick?: () => void
|
||||
}
|
||||
|
||||
declare interface _ZPagingEmptyView {
|
||||
new (): {
|
||||
$props: AllowedComponentProps &
|
||||
VNodeProps &
|
||||
ZPagingEmptyViewProps
|
||||
}
|
||||
}
|
||||
|
||||
export declare const ZPagingEmptyView: _ZPagingEmptyView
|
||||
|
95
src/uni_modules/z-paging/types/comps/z-paging-swiper-item.d.ts
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
import { AllowedComponentProps, VNodeProps } from './_common'
|
||||
|
||||
// ****************************** Props ******************************
|
||||
declare interface ZPagingSwiperItemProps {
|
||||
/**
|
||||
* 当前组件的index,也就是当前组件是swiper中的第几个
|
||||
* @default 0
|
||||
*/
|
||||
tabIndex?: number
|
||||
|
||||
/**
|
||||
* 当前swiper切换到第几个index
|
||||
* @default 0
|
||||
*/
|
||||
currentIndex?: number
|
||||
|
||||
/**
|
||||
* 是否使用虚拟列表。使用页面滚动或nvue时,不支持虚拟列表。在nvue中z-paging内置了list组件,效果与虚拟列表类似,并且可以提供更好的性能。
|
||||
* @default false
|
||||
*/
|
||||
useVirtualList?: boolean
|
||||
|
||||
/**
|
||||
* 虚拟列表cell高度模式,默认为`fixed`,也就是每个cell高度完全相同,将以第一个cell高度为准进行计算。
|
||||
* @default 'fixed'
|
||||
*/
|
||||
cellHeightMode?: 'fixed' | 'dynamic'
|
||||
|
||||
/**
|
||||
* 预加载的列表可视范围(列表高度)页数。此数值越大,则虚拟列表中加载的dom越多,内存消耗越大(会维持在一个稳定值),但增加预加载页面数量可缓解快速滚动短暂白屏问题。
|
||||
* @default 12
|
||||
*/
|
||||
preloadPage?: number | string
|
||||
|
||||
/**
|
||||
* 虚拟列表列数,默认为1。常用于每行有多列的情况,例如每行有2列数据,需要将此值设置为2。
|
||||
* @default 1
|
||||
* @since 2.2.8
|
||||
*/
|
||||
virtualListCol?: number | string
|
||||
|
||||
/**
|
||||
* 虚拟列表scroll取样帧率,默认为80,过低容易出现白屏问题,过高容易出现卡顿问题
|
||||
* @default 80
|
||||
*/
|
||||
virtualScrollFps?: number | string
|
||||
|
||||
/**
|
||||
* 是否在z-paging内部循环渲染列表(使用内置列表)。
|
||||
* @default false
|
||||
*/
|
||||
useInnerList?: boolean
|
||||
|
||||
/**
|
||||
* 内置列表cell的key名称(仅nvue有效,在nvue中开启use-inner-list时必须填此项)
|
||||
* @since 2.2.7
|
||||
*/
|
||||
cellKeyName?: string
|
||||
|
||||
/**
|
||||
* innerList样式
|
||||
*/
|
||||
innerListStyle?: Record<string, any>
|
||||
}
|
||||
|
||||
// ****************************** Methods ******************************
|
||||
declare interface _ZPagingSwiperItemRef {
|
||||
/**
|
||||
* 重新加载分页数据,pageNo恢复为默认值,相当于下拉刷新的效果
|
||||
*
|
||||
* @param [animate=false] 是否展示下拉刷新动画
|
||||
*/
|
||||
reload: (animate?: boolean) => void;
|
||||
|
||||
/**
|
||||
* 请求结束
|
||||
* - 当通过complete传进去的数组长度小于pageSize时,则判定为没有更多了
|
||||
*
|
||||
* @param [data] 请求结果数组
|
||||
* @param [success=true] 是否请求成功
|
||||
*/
|
||||
complete: (data?: any[] | false, success?: boolean) => void;
|
||||
}
|
||||
|
||||
declare interface _ZPagingSwiperItem {
|
||||
new (): {
|
||||
$props: AllowedComponentProps &
|
||||
VNodeProps &
|
||||
ZPagingSwiperItemProps
|
||||
}
|
||||
}
|
||||
|
||||
export declare const ZPagingSwiperItem: _ZPagingSwiperItem
|
||||
|
||||
export declare const ZPagingSwiperItemRef: _ZPagingSwiperItemRef
|
89
src/uni_modules/z-paging/types/comps/z-paging-swiper.d.ts
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
import { AllowedComponentProps, VNodeProps } from './_common'
|
||||
|
||||
// ****************************** Props ******************************
|
||||
declare interface ZPagingSwiperProps {
|
||||
/**
|
||||
* 是否使用fixed布局,若使用fixed布局,则z-paging-swiper的父view无需固定高度,z-paging高度默认铺满屏幕,页面中的view请放z-paging-swiper标签内,需要固定在顶部的view使用slot="top"包住,需要固定在底部的view使用slot="bottom"包住。
|
||||
* @default true
|
||||
*/
|
||||
fixed?: boolean
|
||||
|
||||
/**
|
||||
* 是否开启底部安全区域适配
|
||||
* @default false
|
||||
*/
|
||||
safeAreaInsetBottom?: boolean
|
||||
|
||||
/**
|
||||
* z-paging-swiper样式
|
||||
*/
|
||||
swiperStyle?: Record<string, any>
|
||||
}
|
||||
|
||||
|
||||
// ****************************** Slots ******************************
|
||||
declare interface ZPagingSwiperSlots {
|
||||
// ******************** 主体布局Slot ********************
|
||||
/**
|
||||
* 默认插入的view
|
||||
*/
|
||||
['default']?: () => any
|
||||
|
||||
/**
|
||||
* 可以将自定义导航栏、tab-view等需要固定的(不需要跟着滚动的)元素放入slot="top"的view中。
|
||||
* 注意,当有多个需要固定的view时,请用一个view包住它们,并且在这个view上设置slot="top"。需要固定在顶部的view请勿设置position: fixed;
|
||||
* @since 1.5.5
|
||||
*/
|
||||
['top']?: () => any
|
||||
|
||||
/**
|
||||
* 可以将需要固定在底部的(不需要跟着滚动的)元素放入slot="bottom"的view中。
|
||||
* 注意,当有多个需要固定的view时,请用一个view包住它们,并且在这个view上设置slot="bottom"。需要固定在底部的view请勿设置position: fixed;
|
||||
* @since 1.6.2
|
||||
*/
|
||||
['bottom']?: () => any
|
||||
|
||||
/**
|
||||
* 可以将需要固定在左侧的(不需要跟着滚动的)元素放入slot="left"的view中。
|
||||
* 注意,当有多个需要固定的view时,请用一个view包住它们,并且在这个view上设置slot="left"。需要固定在左侧的view请勿设置position: fixed;
|
||||
* @since 2.2.3
|
||||
*/
|
||||
['left']?: () => any
|
||||
|
||||
/**
|
||||
* 可以将需要固定在左侧的(不需要跟着滚动的)元素放入slot="right"的view中。
|
||||
* 注意,当有多个需要固定的view时,请用一个view包住它们,并且在这个view上设置slot="right"。需要固定在右侧的view请勿设置position: fixed;
|
||||
* @since 2.2.3
|
||||
*/
|
||||
['right']?: () => any
|
||||
}
|
||||
|
||||
// ****************************** Methods ******************************
|
||||
declare interface _ZPagingSwiperRef {
|
||||
/**
|
||||
* 更新slot="left"和slot="right"宽度,当slot="left"或slot="right"宽度动态改变后调用
|
||||
*
|
||||
* @since 2.3.5
|
||||
*/
|
||||
updateLeftAndRightWidth: () => void;
|
||||
|
||||
/**
|
||||
* 更新fixed模式下z-paging-swiper的布局,在onShow时候调用,以修复在iOS+h5+tabbar+fixed+底部有安全区域的设备中从tabbar页面跳转到无tabbar页面后返回,底部有一段空白区域的问题
|
||||
*
|
||||
* @since 2.6.5
|
||||
*/
|
||||
updateFixedLayout: () => void;
|
||||
}
|
||||
|
||||
declare interface _ZPagingSwiper {
|
||||
new (): {
|
||||
$props: AllowedComponentProps &
|
||||
VNodeProps &
|
||||
ZPagingSwiperProps
|
||||
$slots: ZPagingSwiperSlots
|
||||
}
|
||||
}
|
||||
|
||||
export declare const ZPagingSwiper: _ZPagingSwiper
|
||||
|
||||
export declare const ZPagingSwiperRef: _ZPagingSwiperRef
|
2069
src/uni_modules/z-paging/types/comps/z-paging.d.ts
vendored
Normal file
24
src/uni_modules/z-paging/types/index.d.ts
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
/// <reference path="./comps.d.ts" />
|
||||
declare module 'z-paging' {
|
||||
export function install() : void
|
||||
/**
|
||||
* z-paging全局配置
|
||||
* - uni.$zp
|
||||
*
|
||||
* @since 2.6.5
|
||||
*/
|
||||
interface $zp {
|
||||
/**
|
||||
* 全局配置
|
||||
*/
|
||||
config : Record<string, any>;
|
||||
}
|
||||
global {
|
||||
interface Uni {
|
||||
$zp : $zp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare type ZPagingSwiperRef = typeof import('./comps/z-paging-swiper')['ZPagingSwiperRef']
|
||||
declare type ZPagingSwiperItemRef = typeof import('./comps/z-paging-swiper-item')['ZPagingSwiperItemRef']
|