useDict.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. import { getByTypeKey } from '@/api/dictionaryManage'
  2. import {
  3. getProvinces,
  4. getCityListByPid,
  5. getByCode,
  6. getDistrictTree,
  7. } from '@/api/dictionaryManage'
  8. import { getCatalogList } from '@/api/catalogManage'
  9. // 数据字典
  10. export const dictMixin = {
  11. data() {
  12. return {}
  13. },
  14. created() {
  15. this.getDictType()
  16. },
  17. methods: {
  18. async getDictType() {
  19. if (!this.dictData) this.dictData = {}
  20. const keys = Object.keys(this.dictData)
  21. // 为每个请求添加类型标识,确保能区分不同请求返回的数据
  22. const requests = keys.map((key) =>
  23. getByTypeKey({ typeKey: key }).then((response) => ({
  24. typeKey: key,
  25. data: response,
  26. }))
  27. )
  28. const results = await Promise.all(requests)
  29. // 使用类型标识来处理数据,检查response是否为数组
  30. results.forEach(({ typeKey, data }) => {
  31. this.dictData[typeKey] = Array.isArray(data) ? data : []
  32. })
  33. },
  34. getDictName(typeKey, key) {
  35. const dictData = this.dictData[typeKey]
  36. if (!dictData || !key) return ''
  37. // 检查key是否包含逗号分隔的多个值
  38. if (key.includes(',')) {
  39. // 分割成key数组并去除首尾空格
  40. const keys = key.split(',').map((k) => k.trim())
  41. // 为每个key查找对应的名称
  42. const names = keys.map((k) => {
  43. const item = dictData.find((item) => item.key === k)
  44. return item ? item.name : k // 找不到对应名称时返回原key
  45. })
  46. // 用逗号连接所有名称返回
  47. return names.join(', ')
  48. } else {
  49. // 单个key的处理逻辑保持不变
  50. const item = dictData.find((item) => item.key === key)
  51. return item ? item.name : key // 找不到对应名称时返回原key
  52. }
  53. },
  54. // 根据字典 value 获取字典 name
  55. getDictNameByValue(typeKey, value) {
  56. const dictData = this.dictData[typeKey]
  57. if (!dictData || dictData.length === 0) return ''
  58. if (value === null || value === undefined || value === '') return ''
  59. // 辅助函数:在字典数据中根据 value 查找对应的 item
  60. const findDictItemByValue = (searchValue) => {
  61. if (
  62. searchValue === null ||
  63. searchValue === undefined ||
  64. searchValue === ''
  65. )
  66. return null
  67. // 尝试多种类型的匹配
  68. const strValue = String(searchValue)
  69. const numValue = Number(searchValue)
  70. // 1. 严格匹配(原始类型)
  71. let item = dictData.find((item) => item.value === searchValue)
  72. if (item) return item
  73. // 2. 字符串匹配
  74. item = dictData.find((item) => {
  75. return String(item.value) === strValue
  76. })
  77. if (item) return item
  78. // 3. 数字匹配(如果 value 可以转换为数字)
  79. if (!isNaN(numValue) && !isNaN(searchValue)) {
  80. item = dictData.find((item) => {
  81. const itemNum = Number(item.value)
  82. return !isNaN(itemNum) && itemNum === numValue
  83. })
  84. if (item) return item
  85. }
  86. return null
  87. }
  88. // 检查 value 是否包含逗号分隔的多个值
  89. const valueStr = String(value)
  90. if (valueStr.includes(',')) {
  91. // 处理多个值
  92. const values = valueStr
  93. .split(',')
  94. .map((v) => v.trim())
  95. .filter(Boolean)
  96. const names = values.map((v) => {
  97. const item = findDictItemByValue(v)
  98. return item ? item.name : v // 找不到对应名称时返回原 value
  99. })
  100. return names.join(', ')
  101. } else {
  102. // 处理单个值
  103. const item = findDictItemByValue(value)
  104. return item ? item.name : valueStr // 找不到对应名称时返回原 value
  105. }
  106. },
  107. },
  108. }
  109. // 区域
  110. export const regionMixin = {
  111. data() {
  112. return {
  113. districtTree: [], // 省市区数据
  114. districtTreeData: [], // 省市区数据
  115. provinces: [], // 省份城市数据
  116. regionNameMap: {}, // 存储区域代码和名称
  117. districtTreeCascaderProps: {
  118. checkStrictly: true,
  119. label: 'name',
  120. value: 'code',
  121. children: 'children',
  122. // lazy: true, // 启用懒加载
  123. // lazyLoad: (node, resolve) => {
  124. // // node为当前点击的节点,resolve为数据加载完成的回调函数
  125. // if (node.level > 0) {
  126. // const pid = node?.data?.id
  127. // getCityListByPid({ pid: pid })
  128. // .then((res) => {
  129. // // 确保响应数据存在
  130. // const childNodes = res.value || []
  131. // // 格式化子节点数据
  132. // const children = childNodes.map((item) => {
  133. // const isLastLevel = node.level >= 2 // 假设节点级别从0开始,0=省,1=市,2=区
  134. // return {
  135. // ...item,
  136. // // 动态设置hasChildren属性
  137. // // 如果是最后一级,则设为false,否则设为true
  138. // hasChildren: !isLastLevel,
  139. // }
  140. // })
  141. // resolve(children)
  142. // })
  143. // .catch((error) => {
  144. // console.error('获取子节点数据失败:', error)
  145. // resolve([]) // 出错时返回空数组
  146. // })
  147. // }
  148. // },
  149. },
  150. }
  151. },
  152. created() {
  153. this.getDistrictTreeData()
  154. },
  155. methods: {
  156. getDistrictTreeData() {
  157. getDistrictTree().then((res) => {
  158. this.districtTreeData = res.value
  159. this.districtTree = this.districtTreeData
  160. if (this.$permission.isAdminOrProvince()) {
  161. // 管理员或省级权限,显示所有数据
  162. this.districtTree = this.districtTreeData
  163. } else {
  164. // 非管理员且数据范围为区域时,筛选出当前用户区域下的市区数据
  165. let user = this.$permission.getUserInfo()
  166. if (user.dataScope === 1) {
  167. let arr = []
  168. this.districtTreeData.forEach((item) => {
  169. if (item.children) {
  170. item.children.forEach((child) => {
  171. if (child.code === user.cityCode) {
  172. arr.push(child)
  173. }
  174. })
  175. }
  176. })
  177. this.districtTreeData = arr
  178. } else if (user.dataScope === 2) {
  179. // 只返回当前县的
  180. let arr = []
  181. this.districtTreeData.forEach((item) => {
  182. if (item.children) {
  183. item.children.forEach((child) => {
  184. if (child.code === user.cityCode) {
  185. if (child.children) {
  186. child.children = child.children.filter(
  187. (c) => c.code === user.countyCode
  188. )
  189. }
  190. arr.push(child)
  191. }
  192. })
  193. }
  194. })
  195. this.districtTreeData = arr
  196. }
  197. this.districtTree = this.districtTreeData
  198. }
  199. })
  200. },
  201. // 获取所有省份
  202. getProvincesData() {
  203. getProvinces().then((res) => {
  204. this.provinces = res.value.map((province) => ({
  205. ...province,
  206. hasChildren: true,
  207. }))
  208. })
  209. },
  210. // 批量获取区域名称
  211. async fetchRegionNames(tableData, regionCode) {
  212. for (const row of tableData) {
  213. if (!row[regionCode]) continue
  214. if (!this.regionNameMap[row[regionCode]]) {
  215. try {
  216. const res = await getByCode({ code: row[regionCode] })
  217. if (res.value && res.value.name) {
  218. this.$set(this.regionNameMap, row[regionCode], res.value.name)
  219. }
  220. } catch (error) {
  221. console.error('获取区域名称失败:', error)
  222. }
  223. }
  224. }
  225. },
  226. //
  227. },
  228. }
  229. // 监审目录
  230. export const catalogMixin = {
  231. data() {
  232. return {
  233. catalogListOptions: [],
  234. catalogProps: {
  235. filterable: true,
  236. placeholder: '请选择关联监审项目',
  237. style: 'width: 100%',
  238. showAllLevels: false,
  239. props: {
  240. multiple: true,
  241. children: 'children',
  242. checkStrictly: true,
  243. label: 'catalogName',
  244. value: 'id',
  245. },
  246. },
  247. }
  248. },
  249. created() {
  250. this.getCatalogListOptions()
  251. },
  252. methods: {
  253. getCatalogListOptions() {
  254. getCatalogList({}).then((res) => {
  255. this.catalogListOptions = res.value
  256. // 使用函数式方法直接生成新的tableData
  257. this.catalogListOptions = this.catalogListOptions
  258. .filter((item) => item.children && item.parentId === '0')
  259. .flatMap((item) => item.children)
  260. // 递归过滤出status == 1(启用状态)的数据
  261. this.catalogListOptions = this.filterEnabledCatalogsStrict(
  262. this.catalogListOptions
  263. )
  264. })
  265. },
  266. /**
  267. * 递归过滤出状态为启用(status == 1)的目录数据(严格模式)
  268. * @param {Array} catalogs - 目录数据数组
  269. * @returns {Array} 过滤后的目录数据
  270. */
  271. filterEnabledCatalogsStrict(catalogs) {
  272. if (!Array.isArray(catalogs) || catalogs.length === 0) {
  273. return []
  274. }
  275. const result = []
  276. for (const catalog of catalogs) {
  277. // 检查当前节点是否为启用状态
  278. if (catalog.status == 1) {
  279. // 创建当前节点的副本
  280. const filteredCatalog = { ...catalog }
  281. // 如果有子目录,递归过滤子目录
  282. if (
  283. filteredCatalog.children &&
  284. Array.isArray(filteredCatalog.children)
  285. ) {
  286. filteredCatalog.children = this.filterEnabledCatalogsStrict(
  287. filteredCatalog.children
  288. )
  289. }
  290. result.push(filteredCatalog)
  291. } else if (catalog.children && Array.isArray(catalog.children)) {
  292. // 如果当前节点不是启用状态,但子节点中可能有启用的节点
  293. const filteredChildren = this.filterEnabledCatalogsStrict(
  294. catalog.children
  295. )
  296. // 如果有过滤后的子节点,将它们添加到结果中
  297. result.push(...filteredChildren)
  298. }
  299. }
  300. return result
  301. },
  302. },
  303. }
  304. // 通用方法
  305. export const commonMixin = {
  306. data() {
  307. return {}
  308. },
  309. methods: {
  310. /**
  311. * 处理级联选择器多选提交数据
  312. * @param {Object} config - 需要处理的字段名和选中的数据,格式为 { name: [['1','2], ['1','2] }
  313. * @returns {Object} 处理后的数据对象
  314. */
  315. extractLastLevelValues(config) {
  316. const result = {}
  317. for (const fieldName in config) {
  318. if (config.hasOwnProperty(fieldName)) {
  319. const fieldValue = config[fieldName]
  320. if (Array.isArray(fieldValue)) {
  321. // 获取每个选项的最后一级值
  322. const lastLevelValues = fieldValue.map((item) => {
  323. // 如果是数组,取最后一个元素
  324. if (Array.isArray(item)) {
  325. return item[item.length - 1]
  326. }
  327. // 如果是字符串,直接返回
  328. return item
  329. })
  330. // 转换为逗号分隔的字符串
  331. result[fieldName] = lastLevelValues.join(',')
  332. } else {
  333. result[fieldName] = fieldValue
  334. }
  335. }
  336. }
  337. return result
  338. },
  339. /**
  340. * 处理级联选择器多选回显数据
  341. * @param {Object} config - 需要处理的字段名和选中的数据,格式为 { name: [['1','2], ['1','2] }
  342. * @param {Array} options - 级联选择器选项数据
  343. * @returns {Object} 处理后的数据对象
  344. *
  345. */
  346. formatRelatedItemsForDisplay(config) {
  347. const result = {}
  348. for (const fieldName in config) {
  349. if (config.hasOwnProperty(fieldName)) {
  350. const fieldValue = config[fieldName].value
  351. // 如果是空字符串或null,返回空数组
  352. if (!fieldValue || fieldValue.trim() === '') {
  353. return []
  354. }
  355. // 将逗号分隔的字符串转换为数组
  356. const itemsArray = fieldValue.split(',').map((item) => {
  357. // 返回选中值的完整路径(包括所有父级节点)一级一级查找
  358. // 递归查找当前项的父级项
  359. return this.getFullPath(
  360. item,
  361. config[fieldName].options,
  362. config[fieldName].id,
  363. config[fieldName].parentId
  364. )
  365. })
  366. // 将一维数组转换为二维数组(每个元素是一个包含一个编码的数组)
  367. result[fieldName] = itemsArray
  368. }
  369. }
  370. return result
  371. },
  372. /**
  373. * 在选项树中递归查找指定ID的项
  374. * @param {String} id - 要查找的ID
  375. * @param {Array} options - 选项数组
  376. * @param {String} idKey - ID字段名,默认值为'id'
  377. * @returns {Object|null} 找到的项或null
  378. */
  379. findItemById(id, options, idKey = 'id') {
  380. if (!Array.isArray(options)) {
  381. return null
  382. }
  383. for (const option of options) {
  384. if (option[idKey] === id) {
  385. return option
  386. }
  387. if (option.children && Array.isArray(option.children)) {
  388. const foundInChildren = this.findItemById(id, option.children, idKey)
  389. if (foundInChildren) {
  390. return foundInChildren
  391. }
  392. }
  393. }
  394. return null
  395. },
  396. /**
  397. * 返回选中值的完整路径(包括所有父级节点)
  398. * @param {String} currentItem - 选中的值
  399. * @param {Array} options - 级联选择器选项数据
  400. * @param {String} idKey - 选中值的id的key,默认值为'id'
  401. * @param {String} parentIdKey - 选中值的父级id的key,默认值为'parentId'
  402. * @returns {Array} 选中值的完整路径(包括所有父级节点)
  403. */
  404. getFullPath(currentItem, options, idKey = 'id', parentIdKey = 'parentId') {
  405. // 如果options不是数组,直接返回空数组
  406. if (!Array.isArray(options)) {
  407. return []
  408. }
  409. // 参数校验
  410. if (!currentItem) {
  411. return []
  412. }
  413. // 返回选中值的完整路径(包括所有父级节点)
  414. const path = []
  415. let currentId = currentItem
  416. // 循环查找所有父级节点
  417. while (currentId) {
  418. // 使用findItemById方法查找当前ID对应的节点
  419. const foundItem = this.findItemById(currentId, options, idKey)
  420. if (foundItem) {
  421. // 将找到的节点ID添加到路径开头
  422. path.unshift(foundItem[idKey])
  423. // 继续查找父级节点
  424. currentId = foundItem[parentIdKey]
  425. // 防止死循环:如果父ID与当前ID相同,说明达到了根节点或数据有问题
  426. if (currentId === foundItem[idKey]) {
  427. break
  428. }
  429. } else {
  430. // 如果未找到当前ID对应的节点,结束循环
  431. break
  432. }
  433. }
  434. return path
  435. },
  436. // 生成UUID
  437. generateUUID() {
  438. return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(
  439. /[xy]/g,
  440. function (c) {
  441. const r = (Math.random() * 16) | 0
  442. const v = c === 'x' ? r : (r & 0x3) | 0x8
  443. return v.toString(16)
  444. }
  445. )
  446. },
  447. },
  448. }