From 83f8bec5db28e8fad18fb8c59bd0d2d9bdfff704 Mon Sep 17 00:00:00 2001 From: xingyu4j Date: Fri, 9 May 2025 14:38:35 +0800 Subject: [PATCH] feat: store and utils --- .../src/locales/langs/en-US/utils.json | 14 + .../src/locales/langs/zh-CN/utils.json | 14 + apps/web-naive/src/store/dict.ts | 74 ++ apps/web-naive/src/utils/constants.ts | 636 ++++++++++++++++++ apps/web-naive/src/utils/dict.ts | 279 ++++++++ apps/web-naive/src/utils/formCreate.ts | 64 ++ apps/web-naive/src/utils/formatTime.ts | 31 + apps/web-naive/src/utils/index.ts | 7 + apps/web-naive/src/utils/rangePickerProps.ts | 59 ++ apps/web-naive/src/utils/routerHelper.ts | 15 + apps/web-naive/src/utils/validator.ts | 17 + 11 files changed, 1210 insertions(+) create mode 100644 apps/web-naive/src/locales/langs/en-US/utils.json create mode 100644 apps/web-naive/src/locales/langs/zh-CN/utils.json create mode 100644 apps/web-naive/src/store/dict.ts create mode 100644 apps/web-naive/src/utils/constants.ts create mode 100644 apps/web-naive/src/utils/dict.ts create mode 100644 apps/web-naive/src/utils/formCreate.ts create mode 100644 apps/web-naive/src/utils/formatTime.ts create mode 100644 apps/web-naive/src/utils/index.ts create mode 100644 apps/web-naive/src/utils/rangePickerProps.ts create mode 100644 apps/web-naive/src/utils/routerHelper.ts create mode 100644 apps/web-naive/src/utils/validator.ts diff --git a/apps/web-naive/src/locales/langs/en-US/utils.json b/apps/web-naive/src/locales/langs/en-US/utils.json new file mode 100644 index 00000000..b9206eff --- /dev/null +++ b/apps/web-naive/src/locales/langs/en-US/utils.json @@ -0,0 +1,14 @@ +{ + "rangePicker": { + "today": "Today", + "last7Days": "Last 7 Days", + "last30Days": "Last 30 Days", + "yesterday": "Yesterday", + "thisWeek": "This Week", + "thisMonth": "This Month", + "lastWeek": "Last Week", + "lastMonth": "Last Month", + "beginTime": "Begin Time", + "endTime": "End Time" + } +} diff --git a/apps/web-naive/src/locales/langs/zh-CN/utils.json b/apps/web-naive/src/locales/langs/zh-CN/utils.json new file mode 100644 index 00000000..d26f1f2c --- /dev/null +++ b/apps/web-naive/src/locales/langs/zh-CN/utils.json @@ -0,0 +1,14 @@ +{ + "rangePicker": { + "today": "今天", + "last7Days": "最近 7 天", + "last30Days": "最近 30 天", + "yesterday": "昨天", + "thisWeek": "本周", + "thisMonth": "本月", + "lastWeek": "上周", + "lastMonth": "上月", + "beginTime": "开始时间", + "endTime": "结束时间" + } +} diff --git a/apps/web-naive/src/store/dict.ts b/apps/web-naive/src/store/dict.ts new file mode 100644 index 00000000..37f636d2 --- /dev/null +++ b/apps/web-naive/src/store/dict.ts @@ -0,0 +1,74 @@ +import { acceptHMRUpdate, defineStore } from 'pinia'; + +export interface DictItem { + colorType?: string; + cssClass?: string; + label: string; + value: string; +} + +export type Dict = Record; + +interface DictState { + dictCache: Dict; +} + +// TODO @芋艿:可以共享么? +export const useDictStore = defineStore('dict', { + actions: { + getDictData(dictType: string, value: any) { + const dict = this.dictCache[dictType]; + if (!dict) { + return undefined; + } + return ( + dict.find((d) => d.value === value || d.value === value.toString()) ?? + undefined + ); + }, + getDictOptions(dictType: string) { + const dictOptions = this.dictCache[dictType]; + if (!dictOptions) { + return []; + } + return dictOptions; + }, + setDictCache(dicts: Dict) { + this.dictCache = dicts; + }, + setDictCacheByApi( + api: (params: Record) => Promise[]>, + params: Record = {}, + labelField: string = 'label', + valueField: string = 'value', + ) { + api(params).then((dicts) => { + const dictCacheData: Dict = {}; + dicts.forEach((dict) => { + dictCacheData[dict.dictType] = dicts + .filter((d) => d.dictType === dict.dictType) + .map((d) => ({ + colorType: d.colorType, + cssClass: d.cssClass, + label: d[labelField], + value: d[valueField], + })); + }); + this.setDictCache(dictCacheData); + }); + }, + }, + persist: { + // 持久化 + pick: ['dictCache'], + }, + state: (): DictState => ({ + dictCache: {}, + }), +}); + +// 解决热更新问题 +const hot = import.meta.hot; +if (hot) { + hot.accept(acceptHMRUpdate(useDictStore, hot)); +} diff --git a/apps/web-naive/src/utils/constants.ts b/apps/web-naive/src/utils/constants.ts new file mode 100644 index 00000000..78da9f94 --- /dev/null +++ b/apps/web-naive/src/utils/constants.ts @@ -0,0 +1,636 @@ +// todo @芋艿:要不要共享 +/** + * Created by 芋道源码 + * + * 枚举类 + */ + +// ========== COMMON 模块 ========== +// 全局通用状态枚举 +export const CommonStatusEnum = { + ENABLE: 0, // 开启 + DISABLE: 1, // 禁用 +}; + +// 全局用户类型枚举 +export const UserTypeEnum = { + MEMBER: 1, // 会员 + ADMIN: 2, // 管理员 +}; + +// ========== SYSTEM 模块 ========== +/** + * 菜单的类型枚举 + */ +export const SystemMenuTypeEnum = { + DIR: 1, // 目录 + MENU: 2, // 菜单 + BUTTON: 3, // 按钮 +}; + +/** + * 角色的类型枚举 + */ +export const SystemRoleTypeEnum = { + SYSTEM: 1, // 内置角色 + CUSTOM: 2, // 自定义角色 +}; + +/** + * 数据权限的范围枚举 + */ +export const SystemDataScopeEnum = { + ALL: 1, // 全部数据权限 + DEPT_CUSTOM: 2, // 指定部门数据权限 + DEPT_ONLY: 3, // 部门数据权限 + DEPT_AND_CHILD: 4, // 部门及以下数据权限 + DEPT_SELF: 5, // 仅本人数据权限 +}; + +/** + * 用户的社交平台的类型枚举 + */ +export const SystemUserSocialTypeEnum = { + DINGTALK: { + title: '钉钉', + type: 20, + source: 'dingtalk', + img: 'https://s1.ax1x.com/2022/05/22/OzMDRs.png', + }, + WECHAT_ENTERPRISE: { + title: '企业微信', + type: 30, + source: 'wechat_enterprise', + img: 'https://s1.ax1x.com/2022/05/22/OzMrzn.png', + }, +}; + +// ========== INFRA 模块 ========== +/** + * 代码生成模板类型 + */ +export const InfraCodegenTemplateTypeEnum = { + CRUD: 1, // 基础 CRUD + TREE: 2, // 树形 CRUD + SUB: 15, // 主子表 CRUD +}; + +/** + * 任务状态的枚举 + */ +export const InfraJobStatusEnum = { + INIT: 0, // 初始化中 + NORMAL: 1, // 运行中 + STOP: 2, // 暂停运行 +}; + +/** + * API 异常数据的处理状态 + */ +export const InfraApiErrorLogProcessStatusEnum = { + INIT: 0, // 未处理 + DONE: 1, // 已处理 + IGNORE: 2, // 已忽略 +}; + +// ========== PAY 模块 ========== +/** + * 支付渠道枚举 + */ +export const PayChannelEnum = { + WX_PUB: { + code: 'wx_pub', + name: '微信 JSAPI 支付', + }, + WX_LITE: { + code: 'wx_lite', + name: '微信小程序支付', + }, + WX_APP: { + code: 'wx_app', + name: '微信 APP 支付', + }, + WX_NATIVE: { + code: 'wx_native', + name: '微信 Native 支付', + }, + WX_WAP: { + code: 'wx_wap', + name: '微信 WAP 网站支付', + }, + WX_BAR: { + code: 'wx_bar', + name: '微信条码支付', + }, + ALIPAY_PC: { + code: 'alipay_pc', + name: '支付宝 PC 网站支付', + }, + ALIPAY_WAP: { + code: 'alipay_wap', + name: '支付宝 WAP 网站支付', + }, + ALIPAY_APP: { + code: 'alipay_app', + name: '支付宝 APP 支付', + }, + ALIPAY_QR: { + code: 'alipay_qr', + name: '支付宝扫码支付', + }, + ALIPAY_BAR: { + code: 'alipay_bar', + name: '支付宝条码支付', + }, + WALLET: { + code: 'wallet', + name: '钱包支付', + }, + MOCK: { + code: 'mock', + name: '模拟支付', + }, +}; + +/** + * 支付的展示模式每局 + */ +export const PayDisplayModeEnum = { + URL: { + mode: 'url', + }, + IFRAME: { + mode: 'iframe', + }, + FORM: { + mode: 'form', + }, + QR_CODE: { + mode: 'qr_code', + }, + APP: { + mode: 'app', + }, +}; + +/** + * 支付类型枚举 + */ +export const PayType = { + WECHAT: 'WECHAT', + ALIPAY: 'ALIPAY', + MOCK: 'MOCK', +}; + +/** + * 支付订单状态枚举 + */ +export const PayOrderStatusEnum = { + WAITING: { + status: 0, + name: '未支付', + }, + SUCCESS: { + status: 10, + name: '已支付', + }, + CLOSED: { + status: 20, + name: '未支付', + }, +}; + +// ========== MALL - 商品模块 ========== +/** + * 商品 SPU 状态 + */ +export const ProductSpuStatusEnum = { + RECYCLE: { + status: -1, + name: '回收站', + }, + DISABLE: { + status: 0, + name: '下架', + }, + ENABLE: { + status: 1, + name: '上架', + }, +}; + +// ========== MALL - 营销模块 ========== +/** + * 优惠劵模板的有限期类型的枚举 + */ +export const CouponTemplateValidityTypeEnum = { + DATE: { + type: 1, + name: '固定日期可用', + }, + TERM: { + type: 2, + name: '领取之后可用', + }, +}; + +/** + * 优惠劵模板的领取方式的枚举 + */ +export const CouponTemplateTakeTypeEnum = { + USER: { + type: 1, + name: '直接领取', + }, + ADMIN: { + type: 2, + name: '指定发放', + }, + REGISTER: { + type: 3, + name: '新人券', + }, +}; + +/** + * 营销的商品范围枚举 + */ +export const PromotionProductScopeEnum = { + ALL: { + scope: 1, + name: '通用劵', + }, + SPU: { + scope: 2, + name: '商品劵', + }, + CATEGORY: { + scope: 3, + name: '品类劵', + }, +}; + +/** + * 营销的条件类型枚举 + */ +export const PromotionConditionTypeEnum = { + PRICE: { + type: 10, + name: '满 N 元', + }, + COUNT: { + type: 20, + name: '满 N 件', + }, +}; + +/** + * 优惠类型枚举 + */ +export const PromotionDiscountTypeEnum = { + PRICE: { + type: 1, + name: '满减', + }, + PERCENT: { + type: 2, + name: '折扣', + }, +}; + +// ========== MALL - 交易模块 ========== +/** + * 分销关系绑定模式枚举 + */ +export const BrokerageBindModeEnum = { + ANYTIME: { + mode: 1, + name: '首次绑定', + }, + REGISTER: { + mode: 2, + name: '注册绑定', + }, + OVERRIDE: { + mode: 3, + name: '覆盖绑定', + }, +}; +/** + * 分佣模式枚举 + */ +export const BrokerageEnabledConditionEnum = { + ALL: { + condition: 1, + name: '人人分销', + }, + ADMIN: { + condition: 2, + name: '指定分销', + }, +}; +/** + * 佣金记录业务类型枚举 + */ +export const BrokerageRecordBizTypeEnum = { + ORDER: { + type: 1, + name: '获得推广佣金', + }, + WITHDRAW: { + type: 2, + name: '提现申请', + }, +}; +/** + * 佣金提现状态枚举 + */ +export const BrokerageWithdrawStatusEnum = { + AUDITING: { + status: 0, + name: '审核中', + }, + AUDIT_SUCCESS: { + status: 10, + name: '审核通过', + }, + AUDIT_FAIL: { + status: 20, + name: '审核不通过', + }, + WITHDRAW_SUCCESS: { + status: 11, + name: '提现成功', + }, + WITHDRAW_FAIL: { + status: 21, + name: '提现失败', + }, +}; +/** + * 佣金提现类型枚举 + */ +export const BrokerageWithdrawTypeEnum = { + WALLET: { + type: 1, + name: '钱包', + }, + BANK: { + type: 2, + name: '银行卡', + }, + WECHAT: { + type: 3, + name: '微信', + }, + ALIPAY: { + type: 4, + name: '支付宝', + }, +}; + +/** + * 配送方式枚举 + */ +export const DeliveryTypeEnum = { + EXPRESS: { + type: 1, + name: '快递发货', + }, + PICK_UP: { + type: 2, + name: '到店自提', + }, +}; +/** + * 交易订单 - 状态 + */ +export const TradeOrderStatusEnum = { + UNPAID: { + status: 0, + name: '待支付', + }, + UNDELIVERED: { + status: 10, + name: '待发货', + }, + DELIVERED: { + status: 20, + name: '已发货', + }, + COMPLETED: { + status: 30, + name: '已完成', + }, + CANCELED: { + status: 40, + name: '已取消', + }, +}; + +// ========== ERP - 企业资源计划 ========== + +export const ErpBizType = { + PURCHASE_ORDER: 10, + PURCHASE_IN: 11, + PURCHASE_RETURN: 12, + SALE_ORDER: 20, + SALE_OUT: 21, + SALE_RETURN: 22, +}; + +// ========== BPM 模块 ========== + +export const BpmModelType = { + BPMN: 10, // BPMN 设计器 + SIMPLE: 20, // 简易设计器 +}; + +export const BpmModelFormType = { + NORMAL: 10, // 流程表单 + CUSTOM: 20, // 业务表单 +}; + +export const BpmProcessInstanceStatus = { + NOT_START: -1, // 未开始 + RUNNING: 1, // 审批中 + APPROVE: 2, // 审批通过 + REJECT: 3, // 审批不通过 + CANCEL: 4, // 已取消 +}; + +export const BpmAutoApproveType = { + NONE: 0, // 不自动通过 + APPROVE_ALL: 1, // 仅审批一次,后续重复的审批节点均自动通过 + APPROVE_SEQUENT: 2, // 仅针对连续审批的节点自动通过 +}; + +// 候选人策略枚举 ( 用于审批节点。抄送节点 ) +export enum CandidateStrategyEnum { + /** + * 审批人自选 + */ + APPROVE_USER_SELECT = 34, + /** + * 部门的负责人 + */ + DEPT_LEADER = 21, + /** + * 部门成员 + */ + DEPT_MEMBER = 20, + /** + * 流程表达式 + */ + EXPRESSION = 60, + /** + * 表单内部门负责人 + */ + FORM_DEPT_LEADER = 51, + /** + * 表单内用户字段 + */ + FORM_USER = 50, + /** + * 连续多级部门的负责人 + */ + MULTI_LEVEL_DEPT_LEADER = 23, + /** + * 指定岗位 + */ + POST = 22, + /** + * 指定角色 + */ + ROLE = 10, + /** + * 发起人自己 + */ + START_USER = 36, + /** + * 发起人部门负责人 + */ + START_USER_DEPT_LEADER = 37, + /** + * 发起人连续多级部门的负责人 + */ + START_USER_MULTI_LEVEL_DEPT_LEADER = 38, + /** + * 发起人自选 + */ + START_USER_SELECT = 35, + /** + * 指定用户 + */ + USER = 30, + /** + * 指定用户组 + */ + USER_GROUP = 40, +} + +/** + * 节点类型 + */ +export enum NodeTypeEnum { + /** + * 子流程节点 + */ + CHILD_PROCESS_NODE = 20, + /** + * 条件分支节点 (对应排他网关) + */ + CONDITION_BRANCH_NODE = 51, + /** + * 条件节点 + */ + CONDITION_NODE = 50, + + /** + * 抄送人节点 + */ + COPY_TASK_NODE = 12, + + /** + * 延迟器节点 + */ + DELAY_TIMER_NODE = 14, + + /** + * 结束节点 + */ + END_EVENT_NODE = 1, + + /** + * 包容分支节点 (对应包容网关) + */ + INCLUSIVE_BRANCH_NODE = 53, + + /** + * 并行分支节点 (对应并行网关) + */ + PARALLEL_BRANCH_NODE = 52, + + /** + * 路由分支节点 + */ + ROUTER_BRANCH_NODE = 54, + /** + * 发起人节点 + */ + START_USER_NODE = 10, + /** + * 办理人节点 + */ + TRANSACTOR_NODE = 13, + + /** + * 触发器节点 + */ + TRIGGER_NODE = 15, + /** + * 审批人节点 + */ + USER_TASK_NODE = 11, +} + +/** + * 任务状态枚举 + */ +export enum TaskStatusEnum { + /** + * 审批通过 + */ + APPROVE = 2, + + /** + * 审批通过中 + */ + APPROVING = 7, + /** + * 已取消 + */ + CANCEL = 4, + /** + * 未开始 + */ + NOT_START = -1, + + /** + * 审批不通过 + */ + REJECT = 3, + + /** + * 已退回 + */ + RETURN = 5, + /** + * 审批中 + */ + RUNNING = 1, + /** + * 待审批 + */ + WAIT = 0, +} diff --git a/apps/web-naive/src/utils/dict.ts b/apps/web-naive/src/utils/dict.ts new file mode 100644 index 00000000..5aaf456e --- /dev/null +++ b/apps/web-naive/src/utils/dict.ts @@ -0,0 +1,279 @@ +import type { SelectOption } from 'naive-ui/es/select'; +// TODO @芋艿:后续再优化 +// TODO @芋艿:可以共享么? + +import { isObject } from '@vben/utils'; + +import { useDictStore } from '#/store'; + +// TODO @dhb52:top-level 调用 导致:"getActivePinia()" was called but there was no active Pinia +// 先临时移入到方法中 +// const dictStore = useDictStore(); + +// TODO @dhb: antd 组件的 color 类型 +type ColorType = 'error' | 'info' | 'success' | 'warning'; + +export interface DictDataType { + dictType: string; + label: string; + value: boolean | number | string; + colorType: ColorType; + cssClass: string; +} + +export interface NumberDictDataType extends DictDataType { + value: number; +} + +export interface StringDictDataType extends DictDataType { + value: string; +} + +/** + * 获取字典标签 + * + * @param dictType 字典类型 + * @param value 字典值 + * @returns 字典标签 + */ +function getDictLabel(dictType: string, value: any) { + const dictStore = useDictStore(); + const dictObj = dictStore.getDictData(dictType, value); + return isObject(dictObj) ? dictObj.label : ''; +} + +/** + * 获取字典对象 + * + * @param dictType 字典类型 + * @param value 字典值 + * @returns 字典对象 + */ +function getDictObj(dictType: string, value: any) { + const dictStore = useDictStore(); + const dictObj = dictStore.getDictData(dictType, value); + return isObject(dictObj) ? dictObj : null; +} + +/** + * 获取字典数组 用于select radio 等 + * + * @param dictType 字典类型 + * @param valueType 字典值类型,默认 string 类型 + * @returns 字典数组 + */ +// TODO @puhui999:貌似可以定义一个类型?不使用 any[] +function getDictOptions( + dictType: string, + valueType: 'boolean' | 'number' | 'string' = 'string', +): any[] { + const dictStore = useDictStore(); + const dictOpts = dictStore.getDictOptions(dictType); + const dictOptions: SelectOption[] = []; + if (dictOpts.length > 0) { + let dictValue: boolean | number | string = ''; + dictOpts.forEach((d) => { + switch (valueType) { + case 'boolean': { + dictValue = `${d.value}` === 'true'; + break; + } + case 'number': { + dictValue = Number.parseInt(`${d.value}`); + break; + } + case 'string': { + dictValue = `${d.value}`; + break; + } + // No default + } + dictOptions.push({ + value: dictValue, + label: d.label, + }); + }); + } + return dictOptions.length > 0 ? dictOptions : []; +} + +// TODO @dhb52:下面的一系列方法,看看能不能复用 getDictOptions 方法 +export const getIntDictOptions = (dictType: string): NumberDictDataType[] => { + // 获得通用的 DictDataType 列表 + const dictOptions = getDictOptions(dictType) as DictDataType[]; + // 转换成 number 类型的 NumberDictDataType 类型 + // why 需要特殊转换:避免 IDEA 在 v-for="dict in getIntDictOptions(...)" 时,el-option 的 key 会告警 + const dictOption: NumberDictDataType[] = []; + dictOptions.forEach((dict: DictDataType) => { + dictOption.push({ + ...dict, + value: Number.parseInt(`${dict.value}`), + }); + }); + return dictOption; +}; + +// TODO @dhb52:下面的一系列方法,看看能不能复用 getDictOptions 方法 +export const getStrDictOptions = (dictType: string) => { + // 获得通用的 DictDataType 列表 + const dictOptions = getDictOptions(dictType) as DictDataType[]; + // 转换成 string 类型的 StringDictDataType 类型 + // why 需要特殊转换:避免 IDEA 在 v-for="dict in getStrDictOptions(...)" 时,el-option 的 key 会告警 + const dictOption: StringDictDataType[] = []; + dictOptions.forEach((dict: DictDataType) => { + dictOption.push({ + ...dict, + value: `${dict.value}`, + }); + }); + return dictOption; +}; + +// TODO @dhb52:下面的一系列方法,看看能不能复用 getDictOptions 方法 +export const getBoolDictOptions = (dictType: string) => { + const dictOption: DictDataType[] = []; + const dictOptions = getDictOptions(dictType) as DictDataType[]; + dictOptions.forEach((dict: DictDataType) => { + dictOption.push({ + ...dict, + value: `${dict.value}` === 'true', + }); + }); + return dictOption; +}; + +enum DICT_TYPE { + AI_GENERATE_MODE = 'ai_generate_mode', // AI 生成模式 + AI_IMAGE_STATUS = 'ai_image_status', // AI 图片状态 + AI_MUSIC_STATUS = 'ai_music_status', // AI 音乐状态 + // ========== AI - 人工智能模块 ========== + AI_PLATFORM = 'ai_platform', // AI 平台 + + AI_WRITE_FORMAT = 'ai_write_format', // AI 写作格式 + AI_WRITE_LANGUAGE = 'ai_write_language', // AI 写作语言 + AI_WRITE_LENGTH = 'ai_write_length', // AI 写作长度 + AI_WRITE_TONE = 'ai_write_tone', // AI 写作语气 + AI_WRITE_TYPE = 'ai_write_type', // AI 写作类型 + BPM_MODEL_FORM_TYPE = 'bpm_model_form_type', + // ========== BPM 模块 ========== + BPM_MODEL_TYPE = 'bpm_model_type', + BPM_OA_LEAVE_TYPE = 'bpm_oa_leave_type', + BPM_PROCESS_INSTANCE_STATUS = 'bpm_process_instance_status', + BPM_PROCESS_LISTENER_TYPE = 'bpm_process_listener_type', + BPM_PROCESS_LISTENER_VALUE_TYPE = 'bpm_process_listener_value_type', + BPM_TASK_CANDIDATE_STRATEGY = 'bpm_task_candidate_strategy', + BPM_TASK_STATUS = 'bpm_task_status', + BROKERAGE_BANK_NAME = 'brokerage_bank_name', // 佣金提现银行 + BROKERAGE_BIND_MODE = 'brokerage_bind_mode', // 分销关系绑定模式 + + BROKERAGE_ENABLED_CONDITION = 'brokerage_enabled_condition', // 分佣模式 + BROKERAGE_RECORD_BIZ_TYPE = 'brokerage_record_biz_type', // 佣金业务类型 + BROKERAGE_RECORD_STATUS = 'brokerage_record_status', // 佣金状态 + BROKERAGE_WITHDRAW_STATUS = 'brokerage_withdraw_status', // 佣金提现状态 + BROKERAGE_WITHDRAW_TYPE = 'brokerage_withdraw_type', // 佣金提现类型 + COMMON_STATUS = 'common_status', + // ========== CRM - 客户管理模块 ========== + CRM_AUDIT_STATUS = 'crm_audit_status', // CRM 审批状态 + CRM_BIZ_TYPE = 'crm_biz_type', // CRM 业务类型 + CRM_BUSINESS_END_STATUS_TYPE = 'crm_business_end_status_type', // CRM 商机结束状态类型 + CRM_CUSTOMER_INDUSTRY = 'crm_customer_industry', // CRM 客户所属行业 + + CRM_CUSTOMER_LEVEL = 'crm_customer_level', // CRM 客户级别 + CRM_CUSTOMER_SOURCE = 'crm_customer_source', // CRM 客户来源 + CRM_FOLLOW_UP_TYPE = 'crm_follow_up_type', // CRM 跟进方式 + CRM_PERMISSION_LEVEL = 'crm_permission_level', // CRM 数据权限的级别 + CRM_PRODUCT_STATUS = 'crm_product_status', // CRM 商品状态 + CRM_PRODUCT_UNIT = 'crm_product_unit', // CRM 产品单位 + CRM_RECEIVABLE_RETURN_TYPE = 'crm_receivable_return_type', // CRM 回款的还款方式 + DATE_INTERVAL = 'date_interval', // 数据间隔 + + // ========== ERP - 企业资源计划模块 ========== + ERP_AUDIT_STATUS = 'erp_audit_status', // ERP 审批状态 + ERP_STOCK_RECORD_BIZ_TYPE = 'erp_stock_record_biz_type', // 库存明细的业务类型 + // ========== MALL - 交易模块 ========== + EXPRESS_CHARGE_MODE = 'trade_delivery_express_charge_mode', // 快递的计费方式 + INFRA_API_ERROR_LOG_PROCESS_STATUS = 'infra_api_error_log_process_status', + // ========== INFRA 模块 ========== + INFRA_BOOLEAN_STRING = 'infra_boolean_string', + INFRA_CODEGEN_FRONT_TYPE = 'infra_codegen_front_type', + INFRA_CODEGEN_SCENE = 'infra_codegen_scene', + + INFRA_CODEGEN_TEMPLATE_TYPE = 'infra_codegen_template_type', + INFRA_CONFIG_TYPE = 'infra_config_type', + + INFRA_FILE_STORAGE = 'infra_file_storage', + INFRA_JOB_LOG_STATUS = 'infra_job_log_status', + + INFRA_JOB_STATUS = 'infra_job_status', + + INFRA_OPERATE_TYPE = 'infra_operate_type', + IOT_DATA_FORMAT = 'iot_data_format', // IOT 数据格式 + IOT_DATA_TYPE = 'iot_data_type', // IOT 数据类型 + IOT_DEVICE_STATUS = 'iot_device_status', // IOT 设备状态 + // ========== IOT - 物联网模块 ========== + IOT_NET_TYPE = 'iot_net_type', // IOT 联网方式 + IOT_PRODUCT_DEVICE_TYPE = 'iot_product_device_type', // IOT 产品设备类型 + IOT_PRODUCT_FUNCTION_TYPE = 'iot_product_function_type', // IOT 产品功能类型 + IOT_PRODUCT_STATUS = 'iot_product_status', // IOT 产品状态 + IOT_PROTOCOL_TYPE = 'iot_protocol_type', // IOT 接入网关协议 + IOT_RW_TYPE = 'iot_rw_type', // IOT 读写类型 + IOT_UNIT_TYPE = 'iot_unit_type', // IOT 单位类型 + IOT_VALIDATE_TYPE = 'iot_validate_type', // IOT 数据校验级别 + MEMBER_EXPERIENCE_BIZ_TYPE = 'member_experience_biz_type', // 会员经验业务类型 + // ========== Member 会员模块 ========== + MEMBER_POINT_BIZ_TYPE = 'member_point_biz_type', // 积分的业务类型 + // ========== MP 模块 ========== + MP_AUTO_REPLY_REQUEST_MATCH = 'mp_auto_reply_request_match', // 自动回复请求匹配类型 + + MP_MESSAGE_TYPE = 'mp_message_type', // 消息类型 + // ========== PAY 模块 ========== + PAY_CHANNEL_CODE = 'pay_channel_code', // 支付渠道编码类型 + PAY_NOTIFY_STATUS = 'pay_notify_status', // 商户支付回调状态 + PAY_NOTIFY_TYPE = 'pay_notify_type', // 商户支付回调状态 + PAY_ORDER_STATUS = 'pay_order_status', // 商户支付订单状态 + PAY_REFUND_STATUS = 'pay_refund_status', // 退款订单状态 + PAY_TRANSFER_STATUS = 'pay_transfer_status', // 转账订单状态 + PAY_TRANSFER_TYPE = 'pay_transfer_type', // 转账订单状态 + // ========== MALL - 商品模块 ========== + PRODUCT_SPU_STATUS = 'product_spu_status', // 商品状态 + + PROMOTION_BANNER_POSITION = 'promotion_banner_position', // banner 定位 + PROMOTION_BARGAIN_RECORD_STATUS = 'promotion_bargain_record_status', // 砍价记录的状态 + PROMOTION_COMBINATION_RECORD_STATUS = 'promotion_combination_record_status', // 拼团记录的状态 + PROMOTION_CONDITION_TYPE = 'promotion_condition_type', // 营销的条件类型枚举 + PROMOTION_COUPON_STATUS = 'promotion_coupon_status', // 优惠劵的状态 + PROMOTION_COUPON_TAKE_TYPE = 'promotion_coupon_take_type', // 优惠劵的领取方式 + PROMOTION_COUPON_TEMPLATE_VALIDITY_TYPE = 'promotion_coupon_template_validity_type', // 优惠劵模板的有限期类型 + // ========== MALL - 营销模块 ========== + PROMOTION_DISCOUNT_TYPE = 'promotion_discount_type', // 优惠类型 + PROMOTION_PRODUCT_SCOPE = 'promotion_product_scope', // 营销的商品范围 + SYSTEM_DATA_SCOPE = 'system_data_scope', + SYSTEM_LOGIN_RESULT = 'system_login_result', + + SYSTEM_LOGIN_TYPE = 'system_login_type', + SYSTEM_MAIL_SEND_STATUS = 'system_mail_send_status', + + SYSTEM_MENU_TYPE = 'system_menu_type', + SYSTEM_NOTICE_TYPE = 'system_notice_type', + SYSTEM_NOTIFY_TEMPLATE_TYPE = 'system_notify_template_type', + SYSTEM_OAUTH2_GRANT_TYPE = 'system_oauth2_grant_type', + SYSTEM_ROLE_TYPE = 'system_role_type', + SYSTEM_SMS_CHANNEL_CODE = 'system_sms_channel_code', + SYSTEM_SMS_RECEIVE_STATUS = 'system_sms_receive_status', + SYSTEM_SMS_SEND_STATUS = 'system_sms_send_status', + SYSTEM_SMS_TEMPLATE_TYPE = 'system_sms_template_type', + + SYSTEM_SOCIAL_TYPE = 'system_social_type', + // ========== SYSTEM 模块 ========== + SYSTEM_USER_SEX = 'system_user_sex', + TERMINAL = 'terminal', // 终端 + TRADE_AFTER_SALE_STATUS = 'trade_after_sale_status', // 售后 - 状态 + TRADE_AFTER_SALE_TYPE = 'trade_after_sale_type', // 售后 - 类型 + TRADE_AFTER_SALE_WAY = 'trade_after_sale_way', // 售后 - 方式 + TRADE_DELIVERY_TYPE = 'trade_delivery_type', // 配送方式 + TRADE_ORDER_ITEM_AFTER_SALE_STATUS = 'trade_order_item_after_sale_status', // 订单项 - 售后状态 + TRADE_ORDER_STATUS = 'trade_order_status', // 订单 - 状态 + TRADE_ORDER_TYPE = 'trade_order_type', // 订单 - 类型 + USER_TYPE = 'user_type', +} +export { DICT_TYPE, getDictLabel, getDictObj, getDictOptions }; diff --git a/apps/web-naive/src/utils/formCreate.ts b/apps/web-naive/src/utils/formCreate.ts new file mode 100644 index 00000000..d7f944c0 --- /dev/null +++ b/apps/web-naive/src/utils/formCreate.ts @@ -0,0 +1,64 @@ +/** + * 针对 https://github.com/xaboy/form-create-designer 封装的工具类 + */ + +import { isRef } from 'vue'; + +// 编码表单 Conf +export const encodeConf = (designerRef: object) => { + // @ts-ignore designerRef.value is dynamically added by form-create-designer + return JSON.stringify(designerRef.value.getOption()); +}; + +// 编码表单 Fields +export const encodeFields = (designerRef: object) => { + // @ts-ignore designerRef.value is dynamically added by form-create-designer + const rule = JSON.parse(designerRef.value.getJson()); + const fields: string[] = []; + rule.forEach((item: unknown) => { + fields.push(JSON.stringify(item)); + }); + return fields; +}; + +// 解码表单 Fields +export const decodeFields = (fields: string[]) => { + const rule: object[] = []; + fields.forEach((item) => { + rule.push(JSON.parse(item)); + }); + return rule; +}; + +// 设置表单的 Conf 和 Fields,适用 FcDesigner 场景 +export const setConfAndFields = ( + designerRef: object, + conf: string, + fields: string, +) => { + // @ts-ignore designerRef.value is dynamically added by form-create-designer + designerRef.value.setOption(JSON.parse(conf)); + // @ts-ignore designerRef.value is dynamically added by form-create-designer + designerRef.value.setRule(decodeFields(fields)); +}; + +// 设置表单的 Conf 和 Fields,适用 form-create 场景 +export const setConfAndFields2 = ( + detailPreview: object, + conf: string, + fields: string[], + value?: object, +) => { + if (isRef(detailPreview)) { + // @ts-ignore detailPreview.value is dynamically added by form-create-designer + detailPreview = detailPreview.value; + } + // @ts-ignore detailPreview properties are dynamically added by form-create-designer + detailPreview.option = JSON.parse(conf); + // @ts-ignore detailPreview properties are dynamically added by form-create-designer + detailPreview.rule = decodeFields(fields); + if (value) { + // @ts-ignore detailPreview properties are dynamically added by form-create-designer + detailPreview.value = value; + } +}; diff --git a/apps/web-naive/src/utils/formatTime.ts b/apps/web-naive/src/utils/formatTime.ts new file mode 100644 index 00000000..3d4e4722 --- /dev/null +++ b/apps/web-naive/src/utils/formatTime.ts @@ -0,0 +1,31 @@ +/** + * 将毫秒,转换成时间字符串。例如说,xx 分钟 + * + * @param ms 毫秒 + * @returns {string} 字符串 + */ +export function formatPast2(ms: number): string { + // 定义时间单位常量,便于维护 + const SECOND = 1000; + const MINUTE = 60 * SECOND; + const HOUR = 60 * MINUTE; + const DAY = 24 * HOUR; + + // 计算各时间单位 + const day = Math.floor(ms / DAY); + const hour = Math.floor((ms % DAY) / HOUR); + const minute = Math.floor((ms % HOUR) / MINUTE); + const second = Math.floor((ms % MINUTE) / SECOND); + + // 根据时间长短返回不同格式 + if (day > 0) { + return `${day} 天${hour} 小时 ${minute} 分钟`; + } + if (hour > 0) { + return `${hour} 小时 ${minute} 分钟`; + } + if (minute > 0) { + return `${minute} 分钟`; + } + return second > 0 ? `${second} 秒` : `${0} 秒`; +} diff --git a/apps/web-naive/src/utils/index.ts b/apps/web-naive/src/utils/index.ts new file mode 100644 index 00000000..022e6441 --- /dev/null +++ b/apps/web-naive/src/utils/index.ts @@ -0,0 +1,7 @@ +export * from './constants'; +export * from './dict'; +export * from './formatTime'; +export * from './formCreate'; +export * from './rangePickerProps'; +export * from './routerHelper'; +export * from './validator'; diff --git a/apps/web-naive/src/utils/rangePickerProps.ts b/apps/web-naive/src/utils/rangePickerProps.ts new file mode 100644 index 00000000..245e3d81 --- /dev/null +++ b/apps/web-naive/src/utils/rangePickerProps.ts @@ -0,0 +1,59 @@ +import type { Dayjs } from 'dayjs'; + +import dayjs from 'dayjs'; + +import { $t } from '#/locales'; + +/** 时间段选择器拓展 */ +export function getRangePickerDefaultProps() { + return { + format: 'YYYY-MM-DD HH:mm:ss', + placeholder: [ + $t('utils.rangePicker.beginTime'), + $t('utils.rangePicker.endTime'), + ], + ranges: { + [$t('utils.rangePicker.today')]: () => + [dayjs().startOf('day'), dayjs().endOf('day')] as [Dayjs, Dayjs], + [$t('utils.rangePicker.last7Days')]: () => + [dayjs().subtract(7, 'day').startOf('day'), dayjs().endOf('day')] as [ + Dayjs, + Dayjs, + ], + [$t('utils.rangePicker.last30Days')]: () => + [dayjs().subtract(30, 'day').startOf('day'), dayjs().endOf('day')] as [ + Dayjs, + Dayjs, + ], + [$t('utils.rangePicker.yesterday')]: () => + [ + dayjs().subtract(1, 'day').startOf('day'), + dayjs().subtract(1, 'day').endOf('day'), + ] as [Dayjs, Dayjs], + [$t('utils.rangePicker.thisWeek')]: () => + [dayjs().startOf('week'), dayjs().endOf('day')] as [Dayjs, Dayjs], + [$t('utils.rangePicker.thisMonth')]: () => + [dayjs().startOf('month'), dayjs().endOf('day')] as [Dayjs, Dayjs], + [$t('utils.rangePicker.lastWeek')]: () => + [dayjs().subtract(1, 'week').startOf('day'), dayjs().endOf('day')] as [ + Dayjs, + Dayjs, + ], + }, + showTime: { + defaultValue: [ + dayjs('00:00:00', 'HH:mm:ss'), + dayjs('23:59:59', 'HH:mm:ss'), + ], + format: 'HH:mm:ss', + }, + transformDateFunc: (dates: any) => { + if (dates && dates.length === 2) { + // 格式化为后台支持的时间格式 + return [dates.createTime[0], dates.createTime[1]].join(','); + } + return {}; + }, + valueFormat: 'YYYY-MM-DD HH:mm:ss', + }; +} diff --git a/apps/web-naive/src/utils/routerHelper.ts b/apps/web-naive/src/utils/routerHelper.ts new file mode 100644 index 00000000..d4c7ac5d --- /dev/null +++ b/apps/web-naive/src/utils/routerHelper.ts @@ -0,0 +1,15 @@ +import { defineAsyncComponent } from 'vue'; + +const modules = import.meta.glob('../views/**/*.{vue,tsx}'); +/** + * 注册一个异步组件 + * @param componentPath 例:/bpm/oa/leave/detail + */ +export const registerComponent = (componentPath: string) => { + for (const item in modules) { + if (item.includes(componentPath)) { + // 使用异步组件的方式来动态加载组件 + return defineAsyncComponent(modules[item] as any); + } + } +}; diff --git a/apps/web-naive/src/utils/validator.ts b/apps/web-naive/src/utils/validator.ts new file mode 100644 index 00000000..3ae62f78 --- /dev/null +++ b/apps/web-naive/src/utils/validator.ts @@ -0,0 +1,17 @@ +// 参数校验,对标 Hutool 的 Validator 工具类 + +/** 手机号正则表达式(中国) */ +const MOBILE_REGEX = /(?:0|86|\+86)?1[3-9]\d{9}/; + +/** + * 验证是否为手机号码(中国) + * + * @param value 值 + * @returns 是否为手机号码(中国) + */ +export function isMobile(value?: null | string): boolean { + if (!value) { + return false; + } + return MOBILE_REGEX.test(value); +}