|
@@ -0,0 +1,273 @@
|
|
|
+<template>
|
|
|
+ <hc-dialog v-model="isShow" ui="hc-report-tasks-user-modal" widths="960px" title="选择任务人" @close="modalClose">
|
|
|
+ <div class="card-div-2 h-full w-235px">
|
|
|
+ <hc-card scrollbar title="角色类型" :loading="signUserLoading">
|
|
|
+ <template v-for="item in signUserList" :key="item.roleId">
|
|
|
+ <div class="hc-tasks-user-role-item" :class="{ cur: roleItem.roleId === item.roleId }" @click="roleItemClick(item)">
|
|
|
+ {{ item.roleName }}
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </hc-card>
|
|
|
+ </div>
|
|
|
+ <div class="card-div-3 h-full w-265px">
|
|
|
+ <hc-card v-if="positionList.length > 0" scrollbar>
|
|
|
+ <template #header>
|
|
|
+ <hc-search-input v-model="positionKey" placeholder="岗位搜索" icon="" @search="positionSearch" />
|
|
|
+ </template>
|
|
|
+ <template v-for="item in positionList" :key="item.roleId">
|
|
|
+ <div class="hc-tasks-user-role-item" :class="{ cur: positionItem.roleId === item.roleId }" @click="positionItemClick(item)">
|
|
|
+ <i class="i-ph-user-list-light mr-5px" />
|
|
|
+ <span>{{ item.roleName }}</span>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </hc-card>
|
|
|
+ <div v-else class="card-empty-no">
|
|
|
+ <hc-empty :src="HcLoadSvg" title="请先选择角色" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="card-div-4 h-full w-235px">
|
|
|
+ <hc-card v-if="signPfxFileList.length > 0">
|
|
|
+ <template #header>
|
|
|
+ <hc-search-input v-model="signUserKey" placeholder="人员搜索" icon="" @search="signUserSearch" />
|
|
|
+ </template>
|
|
|
+ <div class="hc-tasks-user-sign-pfx-box">
|
|
|
+ <el-scrollbar ref="scrollRef">
|
|
|
+ <template v-for="item in signPfxFileList" :key="item.name">
|
|
|
+ <div v-if="item.data.length > 0" :id="`hc-sign-pfx-file-item-${item.name}`" class="hc-sign-pfx-file-item">
|
|
|
+ <div class="hc-tasks-user-letter">{{ item.name }}</div>
|
|
|
+ <template v-for="(items, index) in item.data" :key="index">
|
|
|
+ <div class="hc-tasks-user-item" @click="signUserItemClick(items)">
|
|
|
+ <i class="i-iconoir-user mr-5px" />
|
|
|
+ <span>{{ items.certificateUserName }}</span>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-scrollbar>
|
|
|
+ </div>
|
|
|
+ <div class="hc-tasks-user-letter-index">
|
|
|
+ <div v-for="item in alphabet" :key="item" class="item" @click="alphabetClick(item)">{{ item }}</div>
|
|
|
+ </div>
|
|
|
+ </hc-card>
|
|
|
+ <div v-else class="card-empty-no">
|
|
|
+ <hc-empty :src="HcLoadSvg" title="请先选择岗位" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="card-div-5 h-full w-205px">
|
|
|
+ <hc-card v-if="userData.length > 0" scrollbar>
|
|
|
+ <template #header>
|
|
|
+ <span>已选择{{ userData.length || 0 }}人</span>
|
|
|
+ </template>
|
|
|
+ <template #extra>
|
|
|
+ <el-button type="warning" size="small" @click="fixedUserSortClick">调整排序</el-button>
|
|
|
+ </template>
|
|
|
+ <div class="hc-tasks-user-cur-box type-1">
|
|
|
+ <template v-for="(item, index) in userData" :key="index">
|
|
|
+ <div class="hc-tasks-user-item">
|
|
|
+ <el-tag closable @close="fixedItemUserListDel(index)">
|
|
|
+ <i class="i-ri-user-3-fill mr-5px" />
|
|
|
+ <span>{{ item.userName }}</span>
|
|
|
+ </el-tag>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </div>
|
|
|
+ </hc-card>
|
|
|
+ <div v-else class="card-empty-no">
|
|
|
+ <hc-empty :src="HcLoadSvg" title="请先选择人员" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <template #footer>
|
|
|
+ <el-button @click="modalClose">取消</el-button>
|
|
|
+ <el-button type="primary" :loading="confirmLoading" @click="confirmClick">确定</el-button>
|
|
|
+ </template>
|
|
|
+ </hc-dialog>
|
|
|
+ <!-- 任务人排序 -->
|
|
|
+ <HcSortModal v-model="isUserSort" :data="userSortData" @finish="userSortFinish" />
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { nextTick, ref, watch } from 'vue'
|
|
|
+import { pinyin } from 'pinyin-pro'
|
|
|
+import { deepClone, getArrValue, getObjValue, isNullES } from 'js-fast-way'
|
|
|
+import HcLoadSvg from '~src/assets/view/load.svg'
|
|
|
+import HcSortModal from './sort-modal.vue'
|
|
|
+import mainApi from '~api/tasks/flow'
|
|
|
+
|
|
|
+const props = defineProps({
|
|
|
+ data: {
|
|
|
+ type: Array,
|
|
|
+ default: () => ([]),
|
|
|
+ },
|
|
|
+ datas: {
|
|
|
+ type: Object,
|
|
|
+ default: () => ({}),
|
|
|
+ },
|
|
|
+})
|
|
|
+
|
|
|
+const emit = defineEmits(['finish', 'close'])
|
|
|
+
|
|
|
+//双向绑定
|
|
|
+const isShow = defineModel('modelValue', {
|
|
|
+ default: false,
|
|
|
+})
|
|
|
+
|
|
|
+//监听参数
|
|
|
+const dataInfo = ref(props.datas)
|
|
|
+watch(() => props.datas, (data) => {
|
|
|
+ dataInfo.value = getObjValue(data)
|
|
|
+}, { deep: true, immediate: true })
|
|
|
+
|
|
|
+//监听数据
|
|
|
+const userData = ref([])
|
|
|
+watch(() => props.data, (data) => {
|
|
|
+ const res = getArrValue(data)
|
|
|
+ userData.value = deepClone(res)
|
|
|
+}, { deep: true, immediate: true })
|
|
|
+
|
|
|
+watch(isShow, (val) => {
|
|
|
+ if (val) setInitData()
|
|
|
+})
|
|
|
+
|
|
|
+//初始化
|
|
|
+const setInitData = async () => {
|
|
|
+ await nextTick()
|
|
|
+ await getAllRoleList()
|
|
|
+ console.log(userData.value)
|
|
|
+}
|
|
|
+
|
|
|
+//角色列表
|
|
|
+const signUserLoading = ref(false)
|
|
|
+const signUserList = ref([])
|
|
|
+const getAllRoleList = async () => {
|
|
|
+ signUserLoading.value = true
|
|
|
+ const { contractId } = getObjValue(dataInfo.value)
|
|
|
+ const { data } = await mainApi.queryAllRoleList({ contractId })
|
|
|
+ signUserList.value = getArrValue(data)
|
|
|
+ signUserLoading.value = false
|
|
|
+}
|
|
|
+
|
|
|
+//角色被点击
|
|
|
+const roleItem = ref({})
|
|
|
+const roleItemClick = (item) => {
|
|
|
+ roleItem.value = item
|
|
|
+ const arr = getArrValue(item.childRoleList)
|
|
|
+ positionList.value = deepClone(arr)
|
|
|
+}
|
|
|
+
|
|
|
+//岗位搜索
|
|
|
+const positionKey = ref('')
|
|
|
+const positionList = ref([])
|
|
|
+const positionSearch = () => {
|
|
|
+ const key = positionKey.value
|
|
|
+ const list = getArrValue(roleItem.value?.childRoleList)
|
|
|
+ const arr = deepClone(list)
|
|
|
+ if (isNullES(key)) {
|
|
|
+ positionList.value = arr
|
|
|
+ return
|
|
|
+ }
|
|
|
+ positionList.value = arr.filter(({ roleName }) => roleName.toLowerCase().includes(key.toLowerCase()))
|
|
|
+}
|
|
|
+
|
|
|
+//岗位被点击
|
|
|
+const positionItem = ref({})
|
|
|
+const positionItemClick = (item) => {
|
|
|
+ positionItem.value = item
|
|
|
+ setSignPfxUser(item.signPfxFileList)
|
|
|
+}
|
|
|
+
|
|
|
+//设置任务人数据
|
|
|
+const alphabet = Array.from({ length: 26 }, (_, i) => String.fromCharCode(65 + i))
|
|
|
+const setSignPfxUser = (data) => {
|
|
|
+ const list = deepClone(data)
|
|
|
+ const arr = getArrValue(list)
|
|
|
+ arr.forEach(item => {
|
|
|
+ item.letter = getFirstLetter(item.certificateUserName)
|
|
|
+ })
|
|
|
+ signPfxFileData.value = deepClone(arr)
|
|
|
+ signPfxFileList.value = alphabet.map(letter => ({
|
|
|
+ name: letter,
|
|
|
+ data: arr.filter(item => item.letter === letter),
|
|
|
+ }))
|
|
|
+}
|
|
|
+
|
|
|
+//中文转姓氏拼音
|
|
|
+const getFirstLetter = (name) => {
|
|
|
+ return pinyin(name.charAt(0), { pattern: 'first', toneType: 'none', surname: 'head' }).charAt(0).toUpperCase()
|
|
|
+}
|
|
|
+
|
|
|
+//搜索任务人
|
|
|
+const signPfxFileData = ref([])
|
|
|
+const signUserKey = ref('')
|
|
|
+const signPfxFileList = ref([])
|
|
|
+const signUserSearch = () => {
|
|
|
+ const key = signUserKey.value
|
|
|
+ const arr = deepClone(signPfxFileData.value)
|
|
|
+ if (isNullES(key)) {
|
|
|
+ setSignPfxUser(arr)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const letterName = getFirstLetter(key)
|
|
|
+ const filteredData = arr.filter(({ certificateUserName, letter }) => {
|
|
|
+ return certificateUserName.toLowerCase().includes(key.toLowerCase()) || letter.toLowerCase().includes(letterName.toLowerCase())
|
|
|
+ })
|
|
|
+ signPfxFileList.value = alphabet.map(letter => ({
|
|
|
+ name: letter,
|
|
|
+ data: filteredData.filter(item => item.letter === letter),
|
|
|
+ }))
|
|
|
+}
|
|
|
+
|
|
|
+//滚动到相关位置
|
|
|
+const scrollRef = ref(null)
|
|
|
+const alphabetClick = (key) => {
|
|
|
+ const ids = `hc-sign-pfx-file-item-${key}`
|
|
|
+ const dom = document.getElementById(ids)
|
|
|
+ if (isNullES(dom)) return
|
|
|
+ scrollRef.value?.setScrollTop(dom.offsetTop - 20)
|
|
|
+}
|
|
|
+
|
|
|
+//任务人被点击
|
|
|
+const signUserItemClick = ({ certificateUserId, certificateUserName }) => {
|
|
|
+ userData.value.push({ userId: certificateUserId, userName: certificateUserName })
|
|
|
+}
|
|
|
+
|
|
|
+//删除选择的任务人
|
|
|
+const fixedItemUserListDel = (index) => {
|
|
|
+ userData.value.splice(index, 1)
|
|
|
+}
|
|
|
+
|
|
|
+//任务人排序
|
|
|
+const isUserSort = ref(false)
|
|
|
+const userSortData = ref([])
|
|
|
+const fixedUserSortClick = () => {
|
|
|
+ const arr = deepClone(userData.value)
|
|
|
+ if (arr.length <= 0) {
|
|
|
+ window.$message.warning('请先添加任务人')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ userSortData.value = arr
|
|
|
+ isUserSort.value = true
|
|
|
+}
|
|
|
+
|
|
|
+//任务人排序完成
|
|
|
+const userSortFinish = (data) => {
|
|
|
+ userData.value = getArrValue(data)
|
|
|
+}
|
|
|
+
|
|
|
+//确定选择
|
|
|
+const confirmLoading = ref(false)
|
|
|
+const confirmClick = async () => {
|
|
|
+ const list = deepClone(userData.value)
|
|
|
+ if (list.length <= 0) {
|
|
|
+ window.$message.warning('请先添加任务人')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ emit('finish', list)
|
|
|
+ modalClose()
|
|
|
+}
|
|
|
+
|
|
|
+//关闭窗口
|
|
|
+const modalClose = () => {
|
|
|
+ isShow.value = false
|
|
|
+ emit('close')
|
|
|
+}
|
|
|
+</script>
|