user-modal.vue 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. <template>
  2. <hc-dialog v-model="isShow" ui="hc-report-tasks-user-modal" widths="960px" title="选择任务人" @close="modalClose">
  3. <div class="card-div-2 h-full w-235px">
  4. <hc-card scrollbar title="角色类型" :loading="signUserLoading">
  5. <template v-for="item in signUserList" :key="item.roleId">
  6. <div class="hc-tasks-user-role-item" :class="{ cur: roleItem.roleId === item.roleId }" @click="roleItemClick(item)">
  7. {{ item.roleName }}
  8. </div>
  9. </template>
  10. </hc-card>
  11. </div>
  12. <div class="card-div-3 h-full w-265px">
  13. <hc-card v-if="positionList.length > 0" scrollbar>
  14. <template #header>
  15. <hc-search-input v-model="positionKey" placeholder="岗位搜索" icon="" @search="positionSearch" />
  16. </template>
  17. <template v-for="item in positionList" :key="item.roleId">
  18. <div class="hc-tasks-user-role-item" :class="{ cur: positionItem.roleId === item.roleId }" @click="positionItemClick(item)">
  19. <i class="i-ph-user-list-light mr-5px" />
  20. <span>{{ item.roleName }}</span>
  21. </div>
  22. </template>
  23. </hc-card>
  24. <div v-else class="card-empty-no">
  25. <hc-empty :src="HcLoadSvg" title="请先选择角色" />
  26. </div>
  27. </div>
  28. <div class="card-div-4 h-full w-235px">
  29. <hc-card v-if="signPfxFileList.length > 0">
  30. <template #header>
  31. <hc-search-input v-model="signUserKey" placeholder="人员搜索" icon="" @search="signUserSearch" />
  32. </template>
  33. <div class="hc-tasks-user-sign-pfx-box">
  34. <el-scrollbar ref="scrollRef">
  35. <template v-for="item in signPfxFileList" :key="item.name">
  36. <div v-if="item.data.length > 0" :id="`hc-sign-pfx-file-item-${item.name}`" class="hc-sign-pfx-file-item">
  37. <div class="hc-tasks-user-letter">{{ item.name }}</div>
  38. <template v-for="(items, index) in item.data" :key="index">
  39. <div class="hc-tasks-user-item" @click="signUserItemClick(items)">
  40. <i class="i-iconoir-user mr-5px" />
  41. <span>{{ items.certificateUserName }}</span>
  42. </div>
  43. </template>
  44. </div>
  45. </template>
  46. </el-scrollbar>
  47. </div>
  48. <div class="hc-tasks-user-letter-index">
  49. <div v-for="item in alphabet" :key="item" class="item" @click="alphabetClick(item)">{{ item }}</div>
  50. </div>
  51. </hc-card>
  52. <div v-else class="card-empty-no">
  53. <hc-empty :src="HcLoadSvg" title="请先选择岗位" />
  54. </div>
  55. </div>
  56. <div class="card-div-5 h-full w-205px">
  57. <hc-card v-if="userData.length > 0" scrollbar>
  58. <template #header>
  59. <span>已选择{{ userData.length || 0 }}人</span>
  60. </template>
  61. <template #extra>
  62. <el-button type="warning" size="small" @click="fixedUserSortClick">调整排序</el-button>
  63. </template>
  64. <div class="hc-tasks-user-cur-box type-1">
  65. <template v-for="(item, index) in userData" :key="index">
  66. <div class="hc-tasks-user-item">
  67. <el-tag closable @close="fixedItemUserListDel(index)">
  68. <i class="i-ri-user-3-fill mr-5px" />
  69. <span>{{ item.userName }}</span>
  70. </el-tag>
  71. </div>
  72. </template>
  73. </div>
  74. </hc-card>
  75. <div v-else class="card-empty-no">
  76. <hc-empty :src="HcLoadSvg" title="请先选择人员" />
  77. </div>
  78. </div>
  79. <template #footer>
  80. <el-button @click="modalClose">取消</el-button>
  81. <el-button type="primary" :loading="confirmLoading" @click="confirmClick">确定</el-button>
  82. </template>
  83. </hc-dialog>
  84. <!-- 任务人排序 -->
  85. <HcSortModal v-model="isUserSort" :data="userSortData" @finish="userSortFinish" />
  86. </template>
  87. <script setup>
  88. import { nextTick, ref, watch } from 'vue'
  89. import { pinyin } from 'pinyin-pro'
  90. import { deepClone, getArrValue, getObjValue, isNullES } from 'js-fast-way'
  91. import HcLoadSvg from '~src/assets/view/load.svg'
  92. import HcSortModal from './sort-modal.vue'
  93. import mainApi from '~api/tasks/flow'
  94. const props = defineProps({
  95. data: {
  96. type: Array,
  97. default: () => ([]),
  98. },
  99. datas: {
  100. type: Object,
  101. default: () => ({}),
  102. },
  103. })
  104. const emit = defineEmits(['finish', 'close'])
  105. //双向绑定
  106. const isShow = defineModel('modelValue', {
  107. default: false,
  108. })
  109. //监听参数
  110. const dataInfo = ref(props.datas)
  111. watch(() => props.datas, (data) => {
  112. dataInfo.value = getObjValue(data)
  113. }, { deep: true, immediate: true })
  114. //监听数据
  115. const userData = ref([])
  116. watch(() => props.data, (data) => {
  117. const res = getArrValue(data)
  118. userData.value = deepClone(res)
  119. }, { deep: true, immediate: true })
  120. watch(isShow, (val) => {
  121. if (val) setInitData()
  122. })
  123. //初始化
  124. const setInitData = async () => {
  125. await nextTick()
  126. await getAllRoleList()
  127. console.log(userData.value)
  128. }
  129. //角色列表
  130. const signUserLoading = ref(false)
  131. const signUserList = ref([])
  132. const getAllRoleList = async () => {
  133. signUserLoading.value = true
  134. const { contractId } = getObjValue(dataInfo.value)
  135. const { data } = await mainApi.queryAllRoleList({ contractId })
  136. signUserList.value = getArrValue(data)
  137. signUserLoading.value = false
  138. }
  139. //角色被点击
  140. const roleItem = ref({})
  141. const roleItemClick = (item) => {
  142. roleItem.value = item
  143. const arr = getArrValue(item.childRoleList)
  144. positionList.value = deepClone(arr)
  145. }
  146. //岗位搜索
  147. const positionKey = ref('')
  148. const positionList = ref([])
  149. const positionSearch = () => {
  150. const key = positionKey.value
  151. const list = getArrValue(roleItem.value?.childRoleList)
  152. const arr = deepClone(list)
  153. if (isNullES(key)) {
  154. positionList.value = arr
  155. return
  156. }
  157. positionList.value = arr.filter(({ roleName }) => roleName.toLowerCase().includes(key.toLowerCase()))
  158. }
  159. //岗位被点击
  160. const positionItem = ref({})
  161. const positionItemClick = (item) => {
  162. positionItem.value = item
  163. setSignPfxUser(item.signPfxFileList)
  164. }
  165. //设置任务人数据
  166. const alphabet = Array.from({ length: 26 }, (_, i) => String.fromCharCode(65 + i))
  167. const setSignPfxUser = (data) => {
  168. const list = deepClone(data)
  169. const arr = getArrValue(list)
  170. arr.forEach(item => {
  171. item.letter = getFirstLetter(item.certificateUserName)
  172. })
  173. signPfxFileData.value = deepClone(arr)
  174. signPfxFileList.value = alphabet.map(letter => ({
  175. name: letter,
  176. data: arr.filter(item => item.letter === letter),
  177. }))
  178. }
  179. //中文转姓氏拼音
  180. const getFirstLetter = (name) => {
  181. return pinyin(name.charAt(0), { pattern: 'first', toneType: 'none', surname: 'head' }).charAt(0).toUpperCase()
  182. }
  183. //搜索任务人
  184. const signPfxFileData = ref([])
  185. const signUserKey = ref('')
  186. const signPfxFileList = ref([])
  187. const signUserSearch = () => {
  188. const key = signUserKey.value
  189. const arr = deepClone(signPfxFileData.value)
  190. if (isNullES(key)) {
  191. setSignPfxUser(arr)
  192. return
  193. }
  194. // 判断是否为全英文
  195. const isAllEnglish = /^[A-Za-z]+$/.test(key)
  196. const letterName = getFirstLetter(key)
  197. //搜索筛选
  198. const filteredData = arr.filter(({ certificateUserName, letter }) => {
  199. if (isAllEnglish) {
  200. // 如果是英文,判断首字母是否一致
  201. return letter.toLowerCase().includes(letterName.toLowerCase())
  202. } else {
  203. // 如果是中文或其他字符,进行精准搜索
  204. return certificateUserName.toLowerCase().includes(key.toLowerCase())
  205. }
  206. })
  207. signPfxFileList.value = alphabet.map(letter => ({
  208. name: letter,
  209. data: filteredData.filter(item => item.letter === letter),
  210. }))
  211. }
  212. //滚动到相关位置
  213. const scrollRef = ref(null)
  214. const alphabetClick = (key) => {
  215. const ids = `hc-sign-pfx-file-item-${key}`
  216. const dom = document.getElementById(ids)
  217. if (isNullES(dom)) return
  218. scrollRef.value?.setScrollTop(dom.offsetTop - 20)
  219. }
  220. //任务人被点击
  221. const signUserItemClick = ({ certificateUserId, certificateUserName }) => {
  222. userData.value.push({ userId: certificateUserId, userName: certificateUserName })
  223. }
  224. //删除选择的任务人
  225. const fixedItemUserListDel = (index) => {
  226. userData.value.splice(index, 1)
  227. }
  228. //任务人排序
  229. const isUserSort = ref(false)
  230. const userSortData = ref([])
  231. const fixedUserSortClick = () => {
  232. const arr = deepClone(userData.value)
  233. if (arr.length <= 0) {
  234. window.$message.warning('请先添加任务人')
  235. return
  236. }
  237. userSortData.value = arr
  238. isUserSort.value = true
  239. }
  240. //任务人排序完成
  241. const userSortFinish = (data) => {
  242. userData.value = getArrValue(data)
  243. }
  244. //确定选择
  245. const confirmLoading = ref(false)
  246. const confirmClick = async () => {
  247. const list = deepClone(userData.value)
  248. if (list.length <= 0) {
  249. window.$message.warning('请先添加任务人')
  250. return
  251. }
  252. emit('finish', list)
  253. modalClose()
  254. }
  255. //关闭窗口
  256. const modalClose = () => {
  257. isShow.value = false
  258. emit('close')
  259. }
  260. </script>