From f9d5d3a50a08fc73779f425a8af0e2a3067de35e Mon Sep 17 00:00:00 2001 From: xingyuv Date: Sat, 18 Mar 2023 23:17:07 +0800 Subject: [PATCH] feat(util): add dict --- src/utils/dict.ts | 124 +++++++++++++++++++++ src/utils/tree.ts | 275 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 399 insertions(+) create mode 100644 src/utils/dict.ts create mode 100644 src/utils/tree.ts diff --git a/src/utils/dict.ts b/src/utils/dict.ts new file mode 100644 index 00000000..3fcad207 --- /dev/null +++ b/src/utils/dict.ts @@ -0,0 +1,124 @@ +/** + * 数据字典工具类 + */ +import { useDictStoreWithOut } from '@/store/modules/dict' + +const dictStore = useDictStoreWithOut() + +/** + * 获取 dictType 对应的数据字典数组 + * + * @param dictType 数据类型 + * @returns {*|Array} 数据字典数组 + */ +export interface DictDataType { + dictType: string + label: string + value: string | number | boolean + colorType: string + cssClass: string +} + +export const getDictOptions = (dictType: string) => { + return dictStore.getDictMap[dictType] +} + +export const getIntDictOptions = (dictType: string) => { + const dictOption: DictDataType[] = [] + const dictOptions: DictDataType[] = getDictOptions(dictType) + dictOptions.forEach((dict: DictDataType) => { + dictOption.push({ + ...dict, + value: parseInt(dict.value + '') + }) + }) + + return dictOption +} + +export const getStrDictOptions = (dictType: string) => { + const dictOption: DictDataType[] = [] + const dictOptions: DictDataType[] = getDictOptions(dictType) + dictOptions.forEach((dict: DictDataType) => { + dictOption.push({ + ...dict, + value: dict.value + '' + }) + }) + return dictOption +} + +export const getBoolDictOptions = (dictType: string) => { + const dictOption: DictDataType[] = [] + const dictOptions: DictDataType[] = getDictOptions(dictType) + dictOptions.forEach((dict: DictDataType) => { + dictOption.push({ + ...dict, + value: dict.value + '' === 'true' ? true : false + }) + }) + return dictOption +} + +export const getDictObj = (dictType: string, value: any) => { + const dictOptions: DictDataType[] = getDictOptions(dictType) + dictOptions.forEach((dict: DictDataType) => { + if (dict.value === value.toString()) { + return dict + } + }) +} + +export enum DICT_TYPE { + USER_TYPE = 'user_type', + COMMON_STATUS = 'common_status', + SYSTEM_TENANT_PACKAGE_ID = 'system_tenant_package_id', + + // ========== SYSTEM 模块 ========== + SYSTEM_USER_SEX = 'system_user_sex', + SYSTEM_MENU_TYPE = 'system_menu_type', + SYSTEM_ROLE_TYPE = 'system_role_type', + SYSTEM_DATA_SCOPE = 'system_data_scope', + SYSTEM_NOTICE_TYPE = 'system_notice_type', + SYSTEM_OPERATE_TYPE = 'system_operate_type', + SYSTEM_LOGIN_TYPE = 'system_login_type', + SYSTEM_LOGIN_RESULT = 'system_login_result', + SYSTEM_SMS_CHANNEL_CODE = 'system_sms_channel_code', + SYSTEM_SMS_TEMPLATE_TYPE = 'system_sms_template_type', + SYSTEM_SMS_SEND_STATUS = 'system_sms_send_status', + SYSTEM_SMS_RECEIVE_STATUS = 'system_sms_receive_status', + SYSTEM_ERROR_CODE_TYPE = 'system_error_code_type', + SYSTEM_OAUTH2_GRANT_TYPE = 'system_oauth2_grant_type', + + // ========== INFRA 模块 ========== + INFRA_BOOLEAN_STRING = 'infra_boolean_string', + INFRA_REDIS_TIMEOUT_TYPE = 'infra_redis_timeout_type', + INFRA_JOB_STATUS = 'infra_job_status', + INFRA_JOB_LOG_STATUS = 'infra_job_log_status', + INFRA_API_ERROR_LOG_PROCESS_STATUS = 'infra_api_error_log_process_status', + INFRA_CONFIG_TYPE = 'infra_config_type', + INFRA_CODEGEN_TEMPLATE_TYPE = 'infra_codegen_template_type', + INFRA_CODEGEN_SCENE = 'infra_codegen_scene', + INFRA_FILE_STORAGE = 'infra_file_storage', + + // ========== BPM 模块 ========== + BPM_MODEL_CATEGORY = 'bpm_model_category', + BPM_MODEL_FORM_TYPE = 'bpm_model_form_type', + BPM_TASK_ASSIGN_RULE_TYPE = 'bpm_task_assign_rule_type', + BPM_PROCESS_INSTANCE_STATUS = 'bpm_process_instance_status', + BPM_PROCESS_INSTANCE_RESULT = 'bpm_process_instance_result', + BPM_TASK_ASSIGN_SCRIPT = 'bpm_task_assign_script', + BPM_OA_LEAVE_TYPE = 'bpm_oa_leave_type', + + // ========== PAY 模块 ========== + PAY_CHANNEL_WECHAT_VERSION = 'pay_channel_wechat_version', // 微信渠道版本 + PAY_CHANNEL_ALIPAY_SIGN_TYPE = 'pay_channel_alipay_sign_type', // 支付渠道支付宝算法类型 + PAY_CHANNEL_ALIPAY_MODE = 'pay_channel_alipay_mode', // 支付宝公钥类型 + PAY_CHANNEL_ALIPAY_SERVER_TYPE = 'pay_channel_alipay_server_type', // 支付宝网关地址 + PAY_CHANNEL_CODE_TYPE = 'pay_channel_code_type', // 支付渠道编码类型 + PAY_ORDER_NOTIFY_STATUS = 'pay_order_notify_status', // 商户支付订单回调状态 + PAY_ORDER_STATUS = 'pay_order_status', // 商户支付订单状态 + PAY_ORDER_REFUND_STATUS = 'pay_order_refund_status', // 商户支付订单退款状态 + PAY_REFUND_ORDER_STATUS = 'pay_refund_order_status', // 退款订单状态 + PAY_REFUND_ORDER_TYPE = 'pay_refund_order_type' // 退款订单类别 +} diff --git a/src/utils/tree.ts b/src/utils/tree.ts new file mode 100644 index 00000000..73400026 --- /dev/null +++ b/src/utils/tree.ts @@ -0,0 +1,275 @@ +interface TreeHelperConfig { + id: string + children: string + pid: string +} +const DEFAULT_CONFIG: TreeHelperConfig = { + id: 'id', + children: 'children', + pid: 'pid' +} +export const defaultProps = { + children: 'children', + label: 'name', + value: 'id' +} + +const getConfig = (config: Partial) => Object.assign({}, DEFAULT_CONFIG, config) + +// tree from list +export const listToTree = (list: any[], config: Partial = {}): T[] => { + const conf = getConfig(config) as TreeHelperConfig + const nodeMap = new Map() + const result: T[] = [] + const { id, children, pid } = conf + + for (const node of list) { + node[children] = node[children] || [] + nodeMap.set(node[id], node) + } + for (const node of list) { + const parent = nodeMap.get(node[pid]) + ;(parent ? parent.children : result).push(node) + } + return result +} + +export const treeToList = (tree: any, config: Partial = {}): T => { + config = getConfig(config) + const { children } = config + const result: any = [...tree] + for (let i = 0; i < result.length; i++) { + if (!result[i][children!]) continue + result.splice(i + 1, 0, ...result[i][children!]) + } + return result +} + +export const findNode = (tree: any, func: Fn, config: Partial = {}): T | null => { + config = getConfig(config) + const { children } = config + const list = [...tree] + for (const node of list) { + if (func(node)) return node + node[children!] && list.push(...node[children!]) + } + return null +} + +export const findNodeAll = (tree: any, func: Fn, config: Partial = {}): T[] => { + config = getConfig(config) + const { children } = config + const list = [...tree] + const result: T[] = [] + for (const node of list) { + func(node) && result.push(node) + node[children!] && list.push(...node[children!]) + } + return result +} + +export const findPath = (tree: any, func: Fn, config: Partial = {}): T | T[] | null => { + config = getConfig(config) + const path: T[] = [] + const list = [...tree] + const visitedSet = new Set() + const { children } = config + while (list.length) { + const node = list[0] + if (visitedSet.has(node)) { + path.pop() + list.shift() + } else { + visitedSet.add(node) + node[children!] && list.unshift(...node[children!]) + path.push(node) + if (func(node)) { + return path + } + } + } + return null +} + +export const findPathAll = (tree: any, func: Fn, config: Partial = {}) => { + config = getConfig(config) + const path: any[] = [] + const list = [...tree] + const result: any[] = [] + const visitedSet = new Set(), + { children } = config + while (list.length) { + const node = list[0] + if (visitedSet.has(node)) { + path.pop() + list.shift() + } else { + visitedSet.add(node) + node[children!] && list.unshift(...node[children!]) + path.push(node) + func(node) && result.push([...path]) + } + } + return result +} + +export const filter = (tree: T[], func: (n: T) => boolean, config: Partial = {}): T[] => { + config = getConfig(config) + const children = config.children as string + function listFilter(list: T[]) { + return list + .map((node: any) => ({ ...node })) + .filter((node) => { + node[children] = node[children] && listFilter(node[children]) + return func(node) || (node[children] && node[children].length) + }) + } + return listFilter(tree) +} + +export const forEach = (tree: T[], func: (n: T) => any, config: Partial = {}): void => { + config = getConfig(config) + const list: any[] = [...tree] + const { children } = config + for (let i = 0; i < list.length; i++) { + // func 返回true就终止遍历,避免大量节点场景下无意义循环,引起浏览器卡顿 + if (func(list[i])) { + return + } + children && list[i][children] && list.splice(i + 1, 0, ...list[i][children]) + } +} + +/** + * @description: Extract tree specified structure + */ +export const treeMap = (treeData: T[], opt: { children?: string; conversion: Fn }): T[] => { + return treeData.map((item) => treeMapEach(item, opt)) +} + +/** + * @description: Extract tree specified structure + */ +export const treeMapEach = (data: any, { children = 'children', conversion }: { children?: string; conversion: Fn }) => { + const haveChildren = Array.isArray(data[children]) && data[children].length > 0 + const conversionData = conversion(data) || {} + if (haveChildren) { + return { + ...conversionData, + [children]: data[children].map((i: number) => + treeMapEach(i, { + children, + conversion + }) + ) + } + } else { + return { + ...conversionData + } + } +} + +/** + * 递归遍历树结构 + * @param treeDatas 树 + * @param callBack 回调 + * @param parentNode 父节点 + */ +export const eachTree = (treeDatas: any[], callBack: Fn, parentNode = {}) => { + treeDatas.forEach((element) => { + const newNode = callBack(element, parentNode) || element + if (element.children) { + eachTree(element.children, callBack, newNode) + } + }) +} + +/** + * 构造树型结构数据 + * @param {*} data 数据源 + * @param {*} id id字段 默认 'id' + * @param {*} parentId 父节点字段 默认 'parentId' + * @param {*} children 孩子节点字段 默认 'children' + */ +export const handleTree = (data: any[], id?: string, parentId?: string, children?: string) => { + if (!Array.isArray(data)) { + console.warn('data must be an array') + return [] + } + const config = { + id: id || 'id', + parentId: parentId || 'parentId', + childrenList: children || 'children' + } + + const childrenListMap = {} + const nodeIds = {} + const tree: any[] = [] + + for (const d of data) { + const parentId = d[config.parentId] + if (childrenListMap[parentId] == null) { + childrenListMap[parentId] = [] + } + nodeIds[d[config.id]] = d + childrenListMap[parentId].push(d) + } + + for (const d of data) { + const parentId = d[config.parentId] + if (nodeIds[parentId] == null) { + tree.push(d) + } + } + + for (const t of tree) { + adaptToChildrenList(t) + } + + function adaptToChildrenList(o) { + if (childrenListMap[o[config.id]] !== null) { + o[config.childrenList] = childrenListMap[o[config.id]] + } + if (o[config.childrenList]) { + for (const c of o[config.childrenList]) { + adaptToChildrenList(c) + } + } + } + return tree +} +/** + * 构造树型结构数据 + * @param {*} data 数据源 + * @param {*} id id字段 默认 'id' + * @param {*} parentId 父节点字段 默认 'parentId' + * @param {*} children 孩子节点字段 默认 'children' + * @param {*} rootId 根Id 默认 0 + */ +export const handleTree2 = (data, id, parentId, children, rootId) => { + id = id || 'id' + parentId = parentId || 'parentId' + children = children || 'children' + rootId = + rootId || + Math.min( + ...data.map((item) => { + return item[parentId] + }) + ) || + 0 + //对源数据深度克隆 + const cloneData = JSON.parse(JSON.stringify(data)) + //循环所有项 + const treeData = cloneData.filter((father) => { + const branchArr = cloneData.filter((child) => { + //返回每一项的子级数组 + return father[id] === child[parentId] + }) + branchArr.length > 0 ? (father.children = branchArr) : '' + //返回第一层 + return father[parentId] === rootId + }) + return treeData !== '' ? treeData : data +}