| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526 |
- <template>
- <hc-dialog v-model="isShow" ui="hc-tasks-user-modal" widths="1195px" title="选择任务人" :footer="!isView" @close="modalClose">
- <div class="card-div-1 h-full w-235px">
- <hc-body scrollbar padding="0">
- <div class="hc-process-item">
- <div class="process setup" :class="{ disabled: isView }" @click="processSetupClick">
- <div class="icon hc-flex-center">
- <i class="i-hugeicons-flowchart-01" />
- </div>
- <div class="name">流程设置</div>
- </div>
- </div>
- <template v-for="(item, index) in fixedData" :key="index">
- <div class="hc-process-item">
- <div
- class="process content"
- :class="fixedIndex === index ? 's-orange' : item.isDataAdd ? 's-gray' : item.isDataSave ? 's-blue' : 's-gray'"
- @click.capture="fixedItemClick(item, index)"
- >
- <div class="icon hc-flex-center" @click="fixedTypeClick(item)">
- <i :class="getProcessIcon(item)" />
- </div>
- <div class="input-box">
- <div class="width-name">{{ item.name }}</div>
- <input
- v-model="item.name"
- class="input"
- :readonly="isView"
- @click="isView ? null : fixedTypeClick(item)"
- >
- </div>
- <div v-if="!isView" class="del-icon hc-flex-center" @click="fixedDelClick(item, index)">
- <i class="i-ri-delete-bin-2-line" />
- </div>
- </div>
- </div>
- </template>
- <div class="hc-process-item">
- <div v-if="!isView" class="process add" @click="fixedAddClick">
- <div class="icon hc-flex-center">
- <i class="i-iconoir-plus" />
- </div>
- </div>
- </div>
- </hc-body>
- </div>
- <div v-if="fixedIndex === -1" class="card-div-no h-full flex-1">
- <hc-empty :src="HcLoadSvg" title="请选择流程后,设置流程人员" />
- </div>
- <template v-else>
- <div class="cards-wrapper">
- <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 v-if="roleItem.roleId" 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="fixedItem?.userList?.length > 0" scrollbar>
- <template #header>
- <span>已选择{{ fixedItem?.userList?.length || 0 }}人</span>
- </template>
- <template #extra>
- <el-button v-if="!isView" type="warning" size="small" @click="fixedUserSortClick">调整排序</el-button>
- </template>
- <div class="hc-tasks-user-cur-box" :class="`type-${fixedItem.type}`">
- <template v-for="(item, index) in fixedItem?.userList" :key="index">
- <div class="hc-tasks-user-item">
- <el-tag :closable="!isView" @close="fixedItemUserListDel(index)">
- <i class="i-ri-user-3-fill mr-5px" />
- <span>{{ item.userName }}</span>
- </el-tag>
- </div>
- </template>
- </div>
- <template #action>
- <el-button v-if="!isView" block size="default" type="success" @click="singleSaveClick">保存</el-button>
- </template>
- </hc-card>
- <div v-else class="card-empty-no">
- <hc-empty :src="HcLoadSvg" title="请先选择人员" />
- </div>
- </div>
- </div>
- </template>
- <template #footer>
- <el-button @click="modalClose">取消</el-button>
- <el-button type="primary" :loading="confirmLoading" @click="confirmClick">确定</el-button>
- </template>
- </hc-dialog>
- <!-- 流程设置 -->
- <HcProcessModal v-model="isProcessSetup" :data="fixedData" @finish="processSetupFinish" />
- <!-- 任务人排序 -->
- <HcSortModal v-model="isUserSort" :data="userSortData" @finish="userSortFinish" />
- </template>
- <script setup>
- import { nextTick, ref, watch } from 'vue'
- import { HcDelMsg } from 'hc-vue3-ui'
- import { pinyin } from 'pinyin-pro'
- import { deepClone, getArrValue, getObjValue, isNullES } from 'js-fast-way'
- import HcLoadSvg from '~src/assets/view/load.svg'
- import HcProcessModal from './process-modal.vue'
- import HcSortModal from './sort-modal.vue'
- import mainApi from '~api/tasks/flow'
- const props = defineProps({
- data: {
- type: Array,
- default: () => ([]),
- },
- datas: {
- type: Object,
- default: () => ({}),
- },
- isView:{
- type: Boolean,
- default: false,
- },
- })
- 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 isView = ref(props.isView)
- watch(() => props.isView, (data) => {
- isView.value = data
- }, { deep: true, immediate: true })
- //监听数据
- const fixedData = ref([])
- watch(() => props.data, (data) => {
- const res = getArrValue(data)
- fixedData.value = deepClone(res)
- }, { deep: true, immediate: true })
- watch(isShow, (val) => {
- if (val) setInitData()
- })
- //初始化
- const setInitData = async () => {
- await nextTick()
- fixedData.value.forEach(item => {
- item.isDataSave = true
- item.flowTaskType = isNullES(item.flowTaskType) ? 1 : item.flowTaskType
- })
- }
- //流程被点击
- const fixedIndex = ref(-1)
- const fixedItem = ref({})
- const fixedItemClick = (item, index) => {
- item.isDataAdd = false
- fixedIndex.value = index
- fixedItem.value = item
- getAllRoleList()
- getsignPfxFileList()
- }
- const signPfxFileListLoading = ref(false)
- const getsignPfxFileList = async () => {
- signPfxFileListLoading.value = true
- const { contractId } = getObjValue(dataInfo.value)
- const { data } = await mainApi.findAllUserAndRoleList({ contractId, roleId:roleItem.value?.roleId || '' })
- let arr = getObjValue(data)
- setSignPfxUser(arr['userList'])
- signPfxFileListLoading.value = false
- }
- //获取流程图标
- const getProcessIcon = (item) => {
- return item.type === 1 ? 'i-hugeicons-workflow-square-03' : 'i-hugeicons-workflow-square-06'
- }
- //流程类型切换
- const fixedTypeClick = (item) => {
- if (isView.value) return
- item.type = item.type === 1 ? 2 : 1
- }
- //新增流程
- const fixedAddClick = () => {
- fixedData.value.push({
- type: 1,
- flowTaskType: 1,
- name: '流程审批名称',
- isDataAdd: true,
- isDataSave: false,
- userList: [],
- })
- }
- //删除流程
- const fixedDelClick = (item, index) => {
- HcDelMsg({
- title: '确认删除任务流程?',
- text: `确认是否需要删除【${item.name}】?`,
- }, (resolve) => {
- fixedData.value?.splice(index, 1)
- if (fixedIndex.value === index) {
- fixedIndex.value = -1
- }
- resolve()
- })
- }
- //角色列表
- const signUserLoading = ref(false)
- const signUserList = ref([])
- const getAllRoleList = async () => {
- signUserLoading.value = true
- const { contractId } = getObjValue(dataInfo.value)
- const { data } = await mainApi.queryAllRoleList({ contractId, type:1 })
- signUserList.value = getArrValue(data)
- signUserLoading.value = false
- }
- //角色被点击
- const roleItem = ref({})
- const roleItemClick = async (item) => {
-
-
- if (roleItem.value.roleId === item.roleId) {
- // 如果点击的是已选中的角色,则清空选择
- roleItem.value = {}
- positionList.value = []
- positionItem.value = {}
- roleItem.value = {}
- await getsignPfxFileList()
- return
- }
- roleItem.value = item
- const arr = getArrValue(item.childRoleList)
- positionList.value = deepClone(arr)
- setSignPfxUser(item.signPfxFileList)
- }
- //岗位搜索
- 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 = async (item) => {
- if (positionItem.value.roleId === item.roleId) {
- // 如果点击的是已选中的岗位,则清空选择
- positionItem.value = {}
- await getsignPfxFileList()
- return
- }
- 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 isAllEnglish = /^[A-Za-z]+$/.test(key)
- const letterName = getFirstLetter(key)
- //搜索筛选
- const filteredData = arr.filter(({ certificateUserName, letter }) => {
- if (isAllEnglish) {
- // 如果是英文,判断首字母是否一致
- return letter.toLowerCase().includes(letterName.toLowerCase())
- } else {
- // 如果是中文或其他字符,进行精准搜索
- return certificateUserName.toLowerCase().includes(key.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 }) => {
- const arr = fixedData.value, index = fixedIndex.value
- const list = getArrValue(arr[index]?.userList)
- list.push({ userId: certificateUserId, userName: certificateUserName })
- fixedData.value[index].userList = list
- fixedItem.value = fixedData.value[index]
- }
- //删除选择的任务人
- const fixedItemUserListDel = (index) => {
- const arr = fixedData.value, i = fixedIndex.value
- const list = getArrValue(arr[i]?.userList)
- list.splice(index, 1)
- fixedData.value[i].userList = list
- fixedItem.value = fixedData.value[i]
- }
- //单个流程保存
- const singleSaveClick = () => {
- const arr = getArrValue(fixedItem.value?.userList)
- if (arr.length <= 0) {
- window.$message.warning('请选择对应的任务人员')
- return
- }
- window.$message.success('保存成功,全部完成后,请点击确定')
- const index = fixedIndex.value
- fixedData.value[index].isDataSave = true
- fixedData.value[index].isDataAdd = false
- }
- //流程设置
- const isProcessSetup = ref(false)
- const processSetupClick = () => {
- const arr = getArrValue(fixedData.value)
- if (arr.length <= 0) {
- window.$message.warning('请先创建任务流程')
- return
- }
- isProcessSetup.value = true
- }
- //流程设置完成
- const processSetupFinish = (data) => {
- isProcessSetup.value = false
- fixedData.value = getArrValue(data)
- fixedIndex.value = -1
- fixedItem.value = {}
- }
- //任务人排序
- const isUserSort = ref(false)
- const userSortData = ref([])
- const fixedUserSortClick = () => {
- const arr = fixedData.value, index = fixedIndex.value
- const list = getArrValue(arr[index]?.userList)
- if (list.length <= 0) {
- window.$message.warning('请先添加任务人')
- return
- }
- userSortData.value = list
- isUserSort.value = true
- }
- //任务人排序完成
- const userSortFinish = (data) => {
- const index = fixedIndex.value
- fixedData.value[index].userList = getArrValue(data)
- }
- //确定选择
- const confirmLoading = ref(false)
- const confirmClick = async () => {
- const list = deepClone(fixedData.value)
- if (list.length <= 0) {
- window.$message.warning('请先创建人物流程和选择任务人')
- return
- }
- //验证数组
- let isRes = true
- for (let i = 0; i < list.length; i++) {
- delete list[i].isDataAdd
- delete list[i].isDataSave
- const { name, userList } = list[i]
- if (userList.length <= 0) {
- isRes = false
- window.$message.warning(name + ',中没有选择任务人')
- break
- }
- }
- if (!isRes) return
- emit('finish', list)
- modalClose()
- }
- //关闭窗口
- const modalClose = () => {
- isShow.value = false
- emit('close')
- }
- </script>
- <style scoped>
- .cards-wrapper {
- display: flex;
- gap: 12px;
- width: 100%;
- height: 100%;
- }
- .card-div-2,
- .card-div-3,
- .card-div-4,
- .card-div-5 {
- height: 100%;
- }
- .card-div-2 {
- width: 235px;
- flex-shrink: 0;
- }
- .card-div-3 {
- width: 265px;
- flex-shrink: 0;
- }
- .card-div-4,
- .card-div-5 {
- flex: 1;
- min-width: 205px;
- }
- /* 添加禁用状态样式 */
- .disabled {
- cursor: not-allowed !important; /* 鼠标变为禁止样式 */
- opacity: 0.6; /* 降低透明度表示禁用 */
- pointer-events: none; /* 阻止鼠标事件穿透 */
- }
- /* 针对删除图标和添加按钮的额外样式调整 */
- .del-icon.disabled,
- .process.add.disabled {
- filter: grayscale(100%); /* 转为灰度进一步表示禁用 */
- }
- </style>
|