vue3-curd 1.0.6 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"vue3-curd.es.js","sources":["../src/utils/propTypes.ts","../src/components/SvgIcon/index.vue","../src/components/DictTag/index.vue","../src/utils/http.ts","../src/api/system/oss/index.ts","../src/components/OssImagePreview/index.vue","../src/components/ossFile/index.vue","../src/components/CURD/constants.ts","../src/components/CURD/utils.ts","../src/components/CURD/composables/useTableData.ts","../src/components/CURD/composables/useDictData.ts","../src/components/CURD/curdTable.vue","../src/components/FileUpload/index.vue","../src/components/ImageUpload/index.vue","../src/api/app/translation/index.ts","../src/store/modules/translation.ts","../src/utils/translation.ts","../src/components/TranslateInput/index.vue","../src/components/Editor/index.vue","../src/utils/ruoyi.ts","../src/utils/index.ts","../src/components/CURD/composables/useFormData.ts","../src/components/CURD/curdEdit.vue","../src/components/CURD/curdSearch.vue","../src/components/CURD/index.vue","../src/directive/curd-perm.ts","../src/utils/errorCode.ts","../src/utils/download.ts","../src/plugins/modal.ts","../src/api/system/dict/data/index.ts","../src/api/system/commonDict/data/index.ts","../src/store/modules/dict.ts","../src/utils/dict.ts","../src/directive/common/copyText.ts","../src/plugin.ts"],"sourcesContent":["import { CSSProperties } from 'vue';\r\nimport VueTypes, { createTypes, toValidableType, VueTypeValidableDef, VueTypesInterface } from 'vue-types';\r\n\r\ntype PropTypes = VueTypesInterface & {\r\n readonly style: VueTypeValidableDef<CSSProperties>;\r\n readonly fieldOption: VueTypeValidableDef<Array<FieldOption>>;\r\n};\r\n\r\nconst propTypes = createTypes({\r\n func: undefined,\r\n bool: undefined,\r\n string: undefined,\r\n number: undefined,\r\n object: undefined,\r\n integer: undefined\r\n}) as PropTypes;\r\n\r\nexport default class ProjectTypes extends VueTypes {\r\n static get style() {\r\n return toValidableType('style', {\r\n type: [String, Object],\r\n default: undefined\r\n });\r\n }\r\n}\r\nexport { propTypes };\r\n","<template>\r\n <svg :class=\"svgClass\" aria-hidden=\"true\">\r\n <use :xlink:href=\"iconName\" :fill=\"color\" />\r\n </svg>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { propTypes } from '@/utils/propTypes';\r\n\r\nconst props = defineProps({\r\n iconClass: propTypes.string.isRequired,\r\n className: propTypes.string.def(''),\r\n color: propTypes.string.def('')\r\n});\r\nconst iconName = computed(() => `#icon-${props.iconClass}`);\r\nconst svgClass = computed(() => {\r\n if (props.className) {\r\n return `svg-icon ${props.className}`;\r\n }\r\n return 'svg-icon';\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.sub-el-icon,\r\n.nav-icon {\r\n display: inline-block;\r\n font-size: 15px;\r\n margin-right: 12px;\r\n position: relative;\r\n}\r\n\r\n.svg-icon {\r\n width: 1em;\r\n height: 1em;\r\n position: relative;\r\n fill: currentColor;\r\n vertical-align: -2px;\r\n}\r\n</style>\r\n","<template>\r\n <template v-if=\"Array.isArray(tagData)\">\r\n <template v-for=\"item in tagData\">\r\n <el-tag :disable-transitions=\"true\" :key=\"item.value\" :type=\"item.elTagType || 'primary'\" v-if=\"item.label\">\r\n {{ item.label }}\r\n </el-tag>\r\n </template>\r\n </template>\r\n <template v-else>\r\n <el-tag :disable-transitions=\"true\" :key=\"tagData.value\" :type=\"tagData.elTagType || 'primary'\" v-if=\"tagData.label\">\r\n {{ tagData.label }}\r\n </el-tag>\r\n </template>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n\r\nconst props = defineProps({\r\n options: {\r\n type: Array as PropType<DictDataOption[]>,\r\n default: null,\r\n },\r\n value: {\r\n type: [Number, String, Array, undefined] as PropType<number | string | Array<number | string> | undefined>,\r\n default: \"\"\r\n },\r\n});\r\n\r\n\r\nconst tagData = computed(() => {\r\n const pValue = props.value\r\n if (pValue === '' || pValue === null || typeof pValue === \"undefined\") {\r\n return \"\"\r\n }\r\n if (Array.isArray(pValue)) {\r\n const data = <any>[]\r\n props.options.map((res: any) => {\r\n if (pValue.indexOf(res.value) > -1 || pValue.indexOf(res.label) > -1) {\r\n data.push(res)\r\n }\r\n })\r\n return data;\r\n }\r\n else if (typeof pValue === 'string') {\r\n let valueArray = <any>[];\r\n if (pValue.indexOf(\",\") > -1) {\r\n valueArray = pValue.split(',');\r\n }\r\n else if (pValue.indexOf(\"、\") > -1) {\r\n valueArray = pValue.split('、');\r\n }\r\n if (valueArray.length > 0) {\r\n const data = <any>[]\r\n props.options.map((res: any) => {\r\n if (valueArray.indexOf(res.value) > -1 || valueArray.indexOf(res.label) > -1) {\r\n data.push(res)\r\n }\r\n })\r\n return data;\r\n }\r\n }\r\n return props.options.find((res: any) => res.value == pValue || res.label == pValue) || pValue;\r\n\r\n});\r\n</script>\r\n\r\n<style scoped>\r\n.el-tag+.el-tag {\r\n margin-left: 5px;\r\n margin-bottom: 3px;\r\n margin-top: 3px;\r\n}\r\n</style>\r\n","/**\r\n * HTTP 访问器:将 axios 实例、鉴权请求头、baseURL 等应用级耦合点\r\n * 从库内部代码中解耦,由消费者通过 install 插件选项注入。\r\n *\r\n * 默认提供一个仅返回 res.data 的最小 axios 实例,\r\n * 业务侧应通过 setRequest 注入自身携带鉴权/拦截器的实例。\r\n */\r\nimport axios, { type AxiosInstance } from 'axios';\r\n\r\nlet _request: AxiosInstance | null = null;\r\nlet _globalHeaders: () => Record<string, string> = () => ({});\r\nlet _baseUrl = '';\r\n\r\nconst createDefaultRequest = (): AxiosInstance => {\r\n const instance = axios.create({ baseURL: _baseUrl, timeout: 50000 });\r\n instance.interceptors.response.use(\r\n (res) => res.data,\r\n (err) => Promise.reject(err)\r\n );\r\n return instance;\r\n};\r\n\r\nexport const getRequest = (): AxiosInstance => {\r\n if (!_request) _request = createDefaultRequest();\r\n return _request;\r\n};\r\n\r\nexport const setRequest = (instance: AxiosInstance) => {\r\n _request = instance;\r\n};\r\n\r\nexport const getGlobalHeaders = () => _globalHeaders();\r\n\r\nexport const setGlobalHeaders = (fn: () => Record<string, string>) => {\r\n _globalHeaders = fn;\r\n};\r\n\r\nexport const getBaseUrl = () => _baseUrl;\r\n\r\nexport const setBaseUrl = (url: string) => {\r\n _baseUrl = url || '';\r\n};\r\n","import { getRequest } from '@/utils/http';\r\nimport { OssQuery, OssVO } from './types';\r\nimport { AxiosPromise } from 'axios';\r\n\r\n// 查询OSS对象存储列表\r\nexport function listOss(query: OssQuery): AxiosPromise<OssVO[]> {\r\n return getRequest()({\r\n url: '/resource/oss/list',\r\n method: 'get',\r\n params: query\r\n });\r\n}\r\n\r\n// 查询OSS对象基于id串\r\nexport function listByIds(ossId: string | number): AxiosPromise<OssVO[]> {\r\n return getRequest()({\r\n url: '/resource/oss/listByIds/' + ossId,\r\n method: 'get'\r\n });\r\n}\r\n\r\n// 删除OSS对象存储\r\nexport function delOss(ossId: string | number | Array<string | number>) {\r\n return getRequest()({\r\n url: '/resource/oss/' + ossId,\r\n method: 'delete'\r\n });\r\n}\r\n\r\n/**\r\n * 上传文件\r\n * @param formData\r\n * @returns\r\n */\r\nexport function uploadOss(fileData: any, fileName: any) {\r\n const formData = new FormData();\r\n if (fileName) formData.append('file', fileData, fileName);\r\n else formData.append('file', fileData);\r\n return getRequest()({\r\n url: '/resource/oss/upload',\r\n method: 'post',\r\n data: formData\r\n });\r\n}\r\n","<template>\r\n <div>\r\n <el-image\r\n v-if=\"realSrc\"\r\n :src=\"`${realSrc}`\"\r\n fit=\"scale-down\"\r\n loading=\"lazy\"\r\n :style=\"`width:${realWidth};height:${realWidth};`\"\r\n :preview-src-list=\"realSrcList\"\r\n preview-teleported\r\n >\r\n <template #error>\r\n <div class=\"image-slot\">\r\n <el-icon><PictureFilled /></el-icon>\r\n </div>\r\n </template>\r\n </el-image>\r\n <div v-else>\r\n <div class=\"image-slot\" :style=\"`width:${realWidth};height:${realWidth};`\">\r\n <el-icon><PictureFilled /></el-icon>\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n<script setup lang=\"ts\">\r\nimport { PictureFilled } from '@element-plus/icons-vue';\r\nimport { listByIds } from \"@/api/system/oss\";\r\nconst props = defineProps({\r\n modelValue: [String, Object, Array, Number],\r\n width: {\r\n type: [Number, String],\r\n default: \"\",\r\n },\r\n height: {\r\n type: [Number, String],\r\n default: \"\",\r\n },\r\n})\r\n\r\nconst src = ref(\"\")\r\n\r\nconst realSrc = computed(() => {\r\n if (!src.value) return;\r\n const real_src = src.value.split(\",\")[0];\r\n return real_src;\r\n})\r\n\r\nconst realSrcList = computed(() => {\r\n if (!src.value) return;\r\n const real_src_list = src.value.split(\",\");\r\n const srcList = [] as any;\r\n real_src_list.forEach((item) => {\r\n return srcList.push(item);\r\n })\r\n return srcList;\r\n})\r\n\r\nconst realWidth = computed(() => {\r\n const width = props.width\r\n return typeof width == \"string\" ? width : `${width}px`;\r\n})\r\n\r\nconst realHeight = computed(() => {\r\n const height = props.height\r\n return typeof height == \"string\" ? height : `${height}px`;\r\n})\r\n\r\nwatch(() => props.modelValue, async (value) => {\r\n if (value) {\r\n let list;\r\n if (Array.isArray(value)) list = value;\r\n else {\r\n const res = await listByIds(value as any)\r\n list = res.data;\r\n }\r\n src.value = list.map(item => { return item.url; }).join(\",\");\r\n } else src.value = \"\";\r\n}, { immediate: true, deep: true })\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.el-image {\r\n border-radius: 2px;\r\n\r\n :deep(.el-image__inner) {\r\n transition: all 0.3s;\r\n cursor: pointer;\r\n }\r\n\r\n :deep(.image-slot) {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n width: 100%;\r\n height: 100%;\r\n color: #909399;\r\n font-size: 30px;\r\n }\r\n}\r\n\r\n.image-slot {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n width: 110px;\r\n height: 100px;\r\n color: #909399;\r\n font-size: 30px;\r\n border-radius: 5px;\r\n\r\n}\r\n</style>\r\n","<template>\r\n <span>\r\n <template v-if=\"['.jpg', '.png', '.jpeg'].indexOf(data.fileSuffix) > -1\">\r\n <el-image :src=\"data.url\" fit=\"cover\" :preview-src-list=\"[data.url]\" :style=\"`width:${realWidth};height:${realHeight};`\" preview-teleported>\r\n <template #error>\r\n <div class=\"image-slot\">\r\n <el-icon>\r\n <PictureFilled />\r\n </el-icon>\r\n </div>\r\n </template>\r\n </el-image>\r\n </template>\r\n <template v-else-if=\"['.xls', '.xlsx', '.csv'].indexOf(data.fileSuffix) > -1\">\r\n <svg-icon icon-class=\"excel\" :style=\"`width:${realWidth};height:${realHeight};`\" />\r\n </template>\r\n <template v-else-if=\"['.doc', '.docx'].indexOf(data.fileSuffix) > -1\">\r\n <svg-icon icon-class=\"word\" :style=\"`width:${realWidth};height:${realHeight};`\" />\r\n </template>\r\n <template v-else-if=\"['.pdf'].indexOf(data.fileSuffix) > -1\">\r\n <svg-icon icon-class=\"pdf\" :style=\"`width:${realWidth};height:${realHeight};`\" />\r\n </template>\r\n <template v-else-if=\"['.tet'].indexOf(data.fileSuffix) > -1\">\r\n <svg-icon icon-class=\"tet\" :style=\"`width:${realWidth};height:${realHeight};`\" />\r\n </template>\r\n <template v-else-if=\"['.zip'].indexOf(data.fileSuffix) > -1\">\r\n <svg-icon icon-class=\"zip\" :style=\"`width:${realWidth};height:${realHeight};`\" />\r\n </template>\r\n <template v-else>\r\n <svg-icon icon-class=\"unknown\" :style=\"`width:${realWidth};height:${realHeight};`\" />\r\n </template>\r\n </span>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { PictureFilled } from '@element-plus/icons-vue';\r\nimport { OssVO } from '@/api/system/oss/types';\r\n\r\nconst props = defineProps({\r\n data: {\r\n type: Object,\r\n default: () => <OssVO>{}\r\n },\r\n width: {\r\n type: [Number, String],\r\n default: 60\r\n },\r\n height: {\r\n type: [Number, String],\r\n default: 60\r\n },\r\n});\r\nconst realWidth = computed(() =>\r\n typeof props.width == \"string\" ? props.width : `${props.width}px`\r\n);\r\n\r\nconst realHeight = computed(() =>\r\n typeof props.height == \"string\" ? props.height : `${props.height}px`\r\n);\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.el-image {\r\n border-radius: 4px;\r\n\r\n :deep(.el-image__inner) {\r\n transition: all 0.3s;\r\n cursor: pointer;\r\n\r\n &:hover {\r\n transform: scale(1.2);\r\n }\r\n }\r\n\r\n :deep(.image-slot) {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n width: 100%;\r\n height: 100%;\r\n color: #909399;\r\n font-size: 30px;\r\n }\r\n}\r\n</style>\r\n","/**\n * CURD组件常量定义\n */\n\nimport type { DictItem } from './types';\n\n// 本地字典\nexport const LOCAL_DICTS: Record<string, DictItem[]> = {\n bool: [\n { label: '是', value: '1' },\n { label: '否', value: '0' },\n { label: '是', value: 'True' },\n { label: '否', value: 'False' }\n ]\n};\n\n// 固定宽度字段配置\nexport const FIXED_WIDTH_FIELDS = [\n { prop: 'createTime', width: '165px' },\n { prop: 'createBy', width: '100px' },\n { prop: 'orderNum', width: '80px' },\n { prop: 'sort', width: '80px' },\n { prop: 'code', width: '120px' },\n { prop: 'updateTime', width: '165px' }\n];\n\n// 分页大小选项\nexport const PAGE_SIZES = [10, 15, 20, 30, 50, 100];\n\n// 默认分页配置\nexport const DEFAULT_PAGE_OPTIONS = {\n pageSize: 15,\n pageNum: 1,\n total: 0,\n orderByColumn: '',\n isAsc: ''\n};\n\n// 分页布局\nexport const PAGING_LAYOUT = {\n FULL: 'total, sizes, prev, pager, next',\n SIMPLE: 'total'\n};\n\n// 操作按钮最小宽度\nexport const MIN_TOOLBAR_WIDTH = 30;\nexport const TOOLBAR_BUTTON_WIDTH = 28;\n","/**\n * CURD组件工具函数\n */\n\nimport type { ColumnConfig, FormConfig } from './types';\nimport { FIXED_WIDTH_FIELDS } from './constants';\n\n/**\n * 删除空参数\n * @param params 参数对象\n * @returns 过滤后的参数对象\n */\nexport function deleteEmptyParams(params: any): Record<string, any> {\n if (!params) return {};\n\n const result: Record<string, any> = {};\n for (const key in params) {\n const value = params[key];\n if (value !== '' && value !== null && value !== undefined && (!Array.isArray(value) || value.length > 0)) {\n result[key] = value;\n }\n }\n return result;\n}\n\n/**\n * 计算列宽度\n * @param column 列配置\n * @returns 列宽度\n */\nexport function calculateColumnWidth(column: ColumnConfig): string {\n if (column.width) return String(column.width);\n\n const fixedField = FIXED_WIDTH_FIELDS.find((field) => field.prop === column.prop);\n if (fixedField) return fixedField.width;\n\n if (column.prop.includes('time')) return '100px';\n\n return 'auto';\n}\n\n/**\n * 深度获取对象属性值\n * @param data 数据对象\n * @param column 列配置\n * @returns 属性值\n */\nexport function getDeepProperty(data: any, column: ColumnConfig): any {\n if (!column.prop) return '';\n\n let result: any;\n if (column.prop.includes('.')) {\n const keys = column.prop.split('.');\n result = keys.reduce((obj, key) => obj?.[key], data);\n } else {\n result = data[column.prop];\n }\n\n if (result !== null && result !== undefined) {\n const prefix = column.prefix || '';\n const suffix = column.suffix || '';\n return `${prefix}${result}${suffix}`;\n }\n\n return '';\n}\n\n/**\n * 从字典数据中查找标签\n * @param dictData 字典数据\n * @param value 值\n * @returns 标签\n */\nexport function findDictLabel(dictData: any[], value: any): string {\n if (!dictData || !Array.isArray(dictData)) return '';\n\n const item = dictData.find((dict) => dict.value == value);\n return item?.label || '';\n}\n\n/**\n * 从字典数据中查找标签类型\n * @param dictData 字典数据\n * @param value 值\n * @returns 标签类型\n */\nexport function findDictTagType(dictData: any[], value: any): string {\n if (!dictData || !Array.isArray(dictData)) return 'info';\n\n const item = dictData.find((dict) => dict.value == value);\n return item?.tagType || 'info';\n}\n\n/**\n * 初始化列配置\n * @param columns 列配置数组\n */\nexport function initializeColumns(columns: ColumnConfig[]): void {\n columns.forEach((column) => {\n // 初始化表单配置\n if (!column.form) {\n column.form = { order: 100 };\n }\n if (column.form.order == null) {\n column.form.order = 100;\n }\n if (!column.form.type && column.type) {\n column.form.type = column.type as any;\n }\n if (!column.form.prop && column.prop) {\n column.form.prop = column.prop;\n }\n\n // 同步字典类型\n if (column.search && !column.search.dictType && column.dictType) {\n column.search.dictType = column.dictType;\n }\n if (!column.form.dictType && column.dictType) {\n column.form.dictType = column.dictType;\n }\n\n // 同步开关值\n if (!column.form.inactiveValue && column.inactiveValue) {\n column.form.inactiveValue = column.inactiveValue;\n }\n if (!column.form.activeValue && column.activeValue) {\n column.form.activeValue = column.activeValue;\n }\n\n // 复选框特殊处理\n if (column.form.type === 'checkbox') {\n column.label = '';\n }\n\n if (column.search) {\n column.search.prop = column.search.prop ?? column.prop;\n }\n });\n}\n\n/**\n * 初始化表单验证规则\n * @param columns 列配置数组\n */\nexport function initializeFormRules(columns: ColumnConfig[]): void {\n columns.forEach((column) => {\n if (!column.form) return;\n\n const rules = column.form.rules || [];\n if (column.form.required) {\n rules.push({\n required: true,\n message: `${column.label}必填`,\n trigger: 'blur'\n });\n }\n column.form.rules = rules.length > 0 ? rules : undefined;\n });\n}\n\n/**\n * 排序列配置\n * @param columns 列配置数组\n * @param key 排序键(支持嵌套属性,如 'form.order')\n * @returns 排序后的列配置数组\n */\nexport function sortColumns<T extends { [key: string]: any }>(columns: T[], key: string = 'order'): T[] {\n return [...columns].sort((a, b) => {\n // 支持嵌套属性访问\n const getNestedValue = (obj: any, path: string): number => {\n const keys = path.split('.');\n let value = obj;\n for (const k of keys) {\n value = value?.[k];\n }\n return value ?? 100;\n };\n\n const aOrder = getNestedValue(a, key);\n const bOrder = getNestedValue(b, key);\n return aOrder - bOrder;\n });\n}\n\n/**\n * 过滤可编辑的列\n * @param columns 列配置数组\n * @param isUpdate 是否为更新操作\n * @param hasKeyId 是否有主键ID\n * @returns 过滤后的列配置数组\n */\nexport function filterEditableColumns(columns: ColumnConfig[], isUpdate: boolean, hasKeyId: boolean): ColumnConfig[] {\n return columns.filter((column) => {\n if (column.canEdit === false) return false;\n if (hasKeyId && column.canUpdate === false) return false;\n if (!hasKeyId && column.canCreate === false) return false;\n return true;\n });\n}\n\n/**\n * 检查按钮是否可见\n * @param data 行数据\n * @param action 操作配置\n * @returns 是否可见\n */\nexport function isButtonVisible(data: any, action: any): boolean {\n if (!action.show) return true;\n for (const key in action.show) {\n if (action.show[key].includes(data[key])) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * 检查按钮是否禁用\n * @param data 行数据\n * @param action 操作配置\n * @returns 是否可见\n */\nexport function isButtonDisabled(data: any, action: any): boolean {\n if (!action.disable && !action.enable) return false;\n for (const key in action.disable) {\n if (action.disable[key].includes(data[key])) {\n return true;\n }\n }\n for (const key in action.enable) {\n if (!action.enable[key].includes(data[key])) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * 格式化日期范围参数\n * @param queryParams 查询参数\n * @param dateRange 日期范围\n * @param propName 属性名\n * @param proxy Vue实例代理\n */\nexport function formatDateRangeParams(queryParams: any, dateRange: any[], propName: string, proxy: any): void {\n if (dateRange && dateRange[0] && dateRange[1]) {\n queryParams[propName] = `${dateRange[0]},${dateRange[1]}`;\n proxy?.addDateRange(queryParams, dateRange, propName);\n }\n}\n","/**\n * 表格数据处理 Composable\n */\n\nimport { ref, getCurrentInstance, type ComponentInternalInstance } from 'vue';\nimport type { DataSourceConfig, PageOptions } from '../types';\nimport { DEFAULT_PAGE_OPTIONS, PAGING_LAYOUT, PAGE_SIZES } from '../constants';\nimport { deleteEmptyParams } from '../utils';\n\nexport function useTableData(dataSource: DataSourceConfig) {\n const { proxy } = getCurrentInstance() as ComponentInternalInstance;\n\n const listData = ref<any[]>([]);\n const loading = ref(false);\n const multipleSelection = ref<any[]>([]);\n const filterParams = ref<Record<string, any>>({});\n const defaultSort = ref<any>();\n\n const pageOptions = ref<PageOptions>({\n ...DEFAULT_PAGE_OPTIONS,\n pageSize: dataSource.initialPageSize ?? DEFAULT_PAGE_OPTIONS.pageSize\n });\n const listSize = ref(PAGE_SIZES);\n const pagingLayout = ref(PAGING_LAYOUT.FULL);\n\n /**\n * 执行查询\n */\n const executeQuery = async (): Promise<void> => {\n if (!dataSource.listApi) {\n if (dataSource.data) {\n listData.value = Array.isArray(dataSource.data) ? dataSource.data : dataSource.data?.value || [];\n } else {\n listData.value = [];\n }\n return;\n }\n\n loading.value = true;\n try {\n const queryParams = await getQueryParams(null);\n const res = await dataSource.listApi(queryParams);\n // 执行查询后回调\n if (dataSource.afterQuery) {\n await dataSource.afterQuery(res);\n }\n\n let resData = res.rows || res.data;\n // 树形数据处理\n if (dataSource.treeProps) {\n resData = proxy?.handleTree(res.data, dataSource.rowKey);\n }\n listData.value = resData;\n pageOptions.value.total = res.total;\n // 动态调整分页布局\n const isSinglePage = res.total / pageOptions.value.pageSize <= 1;\n pagingLayout.value = isSinglePage ? PAGING_LAYOUT.SIMPLE : PAGING_LAYOUT.FULL;\n proxy?.$forceUpdate();\n } catch (error) {\n console.error('Query failed:', error);\n } finally {\n loading.value = false;\n }\n };\n\n /**\n * 获取查询参数\n */\n const getQueryParams = async (params: any): Promise<Record<string, any>> => {\n if (params) {\n filterParams.value = deleteEmptyParams(params);\n }\n let queryParams = dataSource.queryParams || {};\n\n if (dataSource.beforeQuery) {\n const beforeQueryParams = await dataSource.beforeQuery({});\n queryParams = {\n ...pageOptions.value,\n ...queryParams,\n ...filterParams.value,\n ...beforeQueryParams\n };\n } else {\n queryParams = {\n ...pageOptions.value,\n ...queryParams,\n ...filterParams.value\n };\n }\n\n return queryParams;\n };\n\n /**\n * 刷新表格\n */\n const refreshTable = (params?: any): void => {\n if (params) {\n filterParams.value = deleteEmptyParams(params);\n }\n executeQuery();\n };\n\n /**\n * 页码变化\n */\n const handlePageChange = (pageNum: number): void => {\n if (pageOptions.value.pageNum !== pageNum) {\n pageOptions.value.pageNum = pageNum;\n executeQuery();\n }\n };\n\n /**\n * 页大小变化\n */\n const handleSizeChange = (pageSize: number): void => {\n if (pageOptions.value.pageSize !== pageSize) {\n pageOptions.value.pageSize = pageSize;\n executeQuery();\n }\n };\n\n /**\n * 排序变化\n */\n const handleSortChange = (data: any): void => {\n if (data.order) {\n pageOptions.value.orderByColumn = data.prop;\n pageOptions.value.isAsc = data.order;\n }\n executeQuery();\n };\n\n /**\n * 选择变化\n */\n const handleSelectionChange = (val: any[]): void => {\n multipleSelection.value = val;\n if (dataSource.selectionChange) {\n dataSource.selectionChange(val);\n }\n };\n\n /**\n * 初始化查询参数\n */\n const initQueryParams = () => {\n const queryParams = dataSource.queryParams || {};\n // 初始化排序\n if (queryParams.orderByColumn) {\n pageOptions.value.orderByColumn = queryParams.orderByColumn;\n defaultSort.value = {\n prop: queryParams.orderByColumn,\n order: queryParams.isAsc === 'asc' ? 'ascending' : 'descending'\n };\n }\n\n if (queryParams.isAsc) {\n pageOptions.value.isAsc = queryParams.isAsc;\n }\n };\n\n /**\n * 初始化数据\n */\n const initData = async (): Promise<void> => {\n initQueryParams();\n await executeQuery();\n };\n\n return {\n listData,\n loading,\n multipleSelection,\n pageOptions,\n listSize,\n pagingLayout,\n defaultSort,\n executeQuery,\n getQueryParams,\n refreshTable,\n handlePageChange,\n handleSizeChange,\n handleSortChange,\n handleSelectionChange,\n initData\n };\n}\n","/**\n * 字典数据处理 Composable\n */\n\nimport { getCurrentInstance, toRaw, type ComponentInternalInstance } from 'vue';\nimport type { ColumnConfig } from '../types';\n\nexport function useDictData() {\n const { proxy } = getCurrentInstance() as ComponentInternalInstance;\n\n /**\n * 初始化列的字典数据\n */\n const initColumnDicts = async (columns: ColumnConfig[]): Promise<void> => {\n for (const column of columns) {\n if (column.type === 'dictTag') {\n const data = await proxy?.useOneDict(column.dictType);\n column.data = toRaw(data);\n } else if (column.type === 'commonDictTag') {\n const data = await proxy?.useOneCommonDict(column.dictType);\n column.data = toRaw(data);\n }\n }\n };\n\n /**\n * 初始化搜索字段的字典数据\n */\n const initSearchDicts = async (column: ColumnConfig): Promise<void> => {\n if (!column.search) return;\n\n const searchType = column.search.type;\n\n switch (searchType) {\n case 'selectApi':\n if (column.search.api) {\n await initSelectApi(column);\n } else {\n console.error(`${column.prop}中search对象未设置api参数`);\n }\n break;\n\n case 'treeSelectApi':\n if (column.search.api) {\n await initTreeSelectApi(column);\n } else {\n console.error(`${column.prop}中search对象未设置api参数`);\n }\n break;\n\n case 'selectCommonDict':\n column.search.data = await proxy?.useOneCommonDict(column.search.dictType);\n break;\n\n case 'selectDict':\n column.search.data = await proxy?.useOneDict(column.search.dictType);\n break;\n }\n };\n\n /**\n * 初始化API下拉列表\n */\n const initSelectApi = async (column: ColumnConfig): Promise<void> => {\n if (!column.search?.api) return;\n\n const params = {\n pageNum: 1,\n pageSize: 1000,\n filedName: column.prop,\n ...(column.search.apiParams || {})\n };\n\n try {\n const result = await column.search.api(params);\n column.search.data = result.data || result.rows;\n } catch (error) {\n console.error(`Failed to load API data for ${column.prop}:`, error);\n }\n };\n\n /**\n * 初始化树形选择API\n */\n const initTreeSelectApi = async (column: ColumnConfig): Promise<void> => {\n if (!column.search?.api) return;\n\n try {\n const result = await column.search.api({ pageNum: 1, pageSize: 1000 });\n column.search.data = result.data || result.rows || result;\n\n // 设置默认展开第一级节点\n if (column.search.data && Array.isArray(column.search.data)) {\n const props = column.search.props || { value: 'id', label: 'label', children: 'children' };\n const valueKey = props.value || 'id';\n column.search.defaultExpandedKeys = column.search.data.map((item: any) => item[valueKey]);\n }\n } catch (error) {\n console.error(`Failed to load tree API data for ${column.prop}:`, error);\n }\n };\n\n return {\n initColumnDicts,\n initSearchDicts,\n initSelectApi\n };\n}\n","<template>\r\n <div>\r\n <el-table ref=\"curdTableRef\" v-loading=\"loading\" :data=\"listData\" :row-key=\"props.dataSource.rowKey\"\r\n :element-loading-text=\"$t('數據加載中...')\" stripe :tree-props=\"props.dataSource.treeProps\" :default-sort=\"defaultSort\"\r\n @selection-change=\"onSelectionChange\" @sort-change=\"handleSortChange\" @row-click=\"onRowClick\"\r\n :default-expand-all=\"props.dataSource.treeDefaultExpandAll ?? false\" :max-height=\"tableMaxHeight\"\r\n highlight-current-row>\r\n <el-table-column v-if=\"props.dataSource.showSelection\" type=\"selection\" width=\"60\" :selectable=\"isRowSelectable\"\r\n align=\"center\"></el-table-column>\r\n <el-table-column v-if=\"props.dataSource.showIndex\" type=\"index\" width=\"70\" align=\"center\"\r\n label=\"序號\"></el-table-column>\r\n <template v-for=\"item in props.dataSource.columns\" :key=\"item.prop || item.key\">\r\n <el-table-column v-if=\"item.width !== 0\" :label=\"$t(item.label)\" :width=\"calColumnWidth(item)\"\r\n :min-width=\"item.minWidth\" :prop=\"item.prop\" :align=\"calColumnAlign(item)\"\r\n :sortable=\"item.sort ? 'custom' : false\" :show-overflow-tooltip=\"item.showOverflowTooltip || true\"\r\n :fixed=\"item.fixed\">\r\n <template v-slot=\"{ row }\">\r\n <slot v-if=\"item.slot\" :name=\"item.slot\" :row=\"row\" :column=\"item\" :value=\"row[item.prop]\" />\r\n <ossImagePreview v-else-if=\"item.type === 'image' && row[item.prop]\" v-model=\"row[item.prop]\"\r\n class=\"image-preview\" :width=\"24\" :height=\"24\" />\r\n <view v-else-if=\"item.type === 'networkImage'\" class=\"image-style\">\r\n <el-image :src=\"row[item.prop]\" fit=\"cover\" style=\"height: 24px;\"> </el-image>\r\n </view>\r\n <view v-else-if=\"item.type === 'imageUrl'\" class=\"image-style\">\r\n <el-image :src=\"row[item.prop]\" fit=\"cover\" style=\"height: 24px;\"> </el-image>\r\n </view>\r\n <template v-else-if=\"item.type === 'date'\"> {{ parseTime(row[item.prop], '{y}-{m}-{d}') }}</template>\r\n <template v-else-if=\"item.type === 'timestamp'\"> {{ parseTime(row[item.prop]) }}</template>\r\n <el-checkbox v-else-if=\"item.type === 'bool'\" v-model=\"row[item.prop]\" @click.stop.prevent> </el-checkbox>\r\n <el-switch v-else-if=\"item.type === 'switch'\" v-model=\"row[item.prop]\"\r\n :active-value=\"item.activeValue || '0'\" :inactive-value=\"item.inactiveValue || '1'\" size=\"small\"\r\n :before-change=\"() => { return false }\">\r\n </el-switch>\r\n <div v-else-if=\"item.type === 'color'\" class=\"color-cell\">\r\n <div class=\"color-cell-item\" :style=\"{ backgroundColor: row[item.prop] || '#ffffff' }\"></div>\r\n </div>\r\n <template v-else-if=\"item.type === 'fromDict'\">{{ fromDbDicts(item, row) }}</template>\r\n <el-tag v-else-if=\"item.type === 'fromDataTag'\" :type=\"getTagType(item, row)\"\r\n :effect=\"item.tagEffect ?? 'light'\" class=\"el-column-tag\">{{ fromData(item, row)\r\n }}</el-tag>\r\n <el-tag v-else-if=\"item.type === 'localDict'\" :type=\"(item.tagType as any) || 'info'\"\r\n :effect=\"item.tagEffect || 'light'\" class=\"el-column-tag\">{{ fromLocalDicts(item,\r\n row)\r\n }}</el-tag>\r\n <el-tag v-else-if=\"item.type === 'tag'\" :type=\"(item.tagType as any) || 'info'\"\r\n :effect=\"item.tagEffect || 'light'\" class=\"el-column-tag\" plain>{{\r\n row[item.prop] }}</el-tag>\r\n <el-tag v-else-if=\"item.type === 'tagArrayObj'\" :type=\"(item.tagType as any) || 'info'\"\r\n :effect=\"item.tagEffect || 'light'\" class=\"el-column-tag\" v-for=\"(tag) in row[item.prop]\"\r\n :key=\"tag[item.displayField]\" plain>\r\n {{ tag[item.displayField] }}\r\n </el-tag>\r\n <el-tag v-else-if=\"item.type === 'tags'\" :type=\"(item.tagType as any) || 'info'\"\r\n :effect=\"item.tagEffect || 'light'\" class=\"el-column-tag\" v-for=\"tag in row[item.prop]\"\r\n :key=\"tag[item.tagLabel]\" plain>\r\n {{ tag[item.tagLabel] }}\r\n </el-tag>\r\n <dict-tag v-else-if=\"item.type === 'dictTag' || item.type === 'commonDictTag'\" :options=\"item.data || []\"\r\n :value=\"row[item.prop]\" />\r\n <svg-icon v-else-if=\"item.type === 'svg'\" :icon-class=\"row.icon\" />\r\n <oss-file v-else-if=\"item.type === 'ossFile'\" :data=\"row\" :width=\"32\" :height=\"32\" />\r\n <template v-else> {{ deepJsonProp(row, item) }} </template>\r\n </template>\r\n </el-table-column>\r\n </template>\r\n <el-table-column v-if=\"columnToolbars.length > 0\" align=\"center\" fixed=\"right\" :label=\"$t('操作')\"\r\n :width=\"columnToolbarWidth\">\r\n <template v-slot=\"{ row }\">\r\n <el-tooltip :content=\"$t(ac.text)\" placement=\"top\" v-for=\"ac in columnToolbars\" :key=\"(ac.action as any)\">\r\n <template v-if=\"buttonVisible(row, ac)\">\r\n <el-button link type=\"primary\" icon=\"Edit\" v-if=\"ac.action === 'update'\" @click=\"actionEvent(ac, row)\"\r\n :disabled=\"buttonDisabled(row, ac)\" v-hasPermi=\"ac.permission || null\" />\r\n <el-button link type=\"primary\" icon=\"Delete\" v-else-if=\"ac.action === 'delete'\"\r\n @click=\"actionEvent(ac, row)\" :disabled=\"buttonDisabled(row, ac)\" v-hasPermi=\"ac.permission || null\" />\r\n <el-button link type=\"primary\" icon=\"View\" v-else-if=\"ac.action === 'view' || ac.action === 'viewDetail'\"\r\n @click=\"actionEvent(ac, row)\" :disabled=\"buttonDisabled(row, ac)\" v-hasPermi=\"ac.permission || null\" />\r\n <el-button link type=\"primary\" v-else :icon=\"ac.icon\" @click=\"actionEvent(ac, row)\"\r\n :disabled=\"buttonDisabled(row, ac)\" v-hasPermi=\"ac.permission || null\" />\r\n </template>\r\n </el-tooltip>\r\n </template>\r\n </el-table-column>\r\n <template #empty>\r\n <div style=\"margin: 20px 0px;\">{{ $t('未查詢到數據') }}</div>\r\n </template>\r\n </el-table>\r\n <el-pagination v-if=\"props.dataSource.paging !== false\" class=\"paging\" background :layout=\"pagingLayout\"\r\n @size-change=\"handleSizeChange\" @current-change=\"handlePageChange\" :page-size=\"pageOptions.pageSize\"\r\n v-model:current-page=\"pageOptions.pageNum\" :total=\"pageOptions.total\" :page-sizes=\"listSize\">\r\n </el-pagination>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport ossImagePreview from \"@/components/OssImagePreview/index.vue\";\r\nimport OssFile from \"@/components/ossFile/index.vue\";\r\nimport { useTableData } from './composables/useTableData';\r\nimport { useDictData } from './composables/useDictData';\r\nimport { calculateColumnWidth, getDeepProperty, isButtonDisabled, isButtonVisible } from './utils';\r\nimport { LOCAL_DICTS, MIN_TOOLBAR_WIDTH, TOOLBAR_BUTTON_WIDTH } from './constants';\r\nimport type { DataSourceConfig, ActionConfig } from './types';\r\n\r\nconst { proxy } = getCurrentInstance() as ComponentInternalInstance;\r\n\r\nconst emits = defineEmits([\"actionEvent\", \"selectionChange\", \"rowClick\"]);\r\nconst props = withDefaults(defineProps<{\r\n dataSource: DataSourceConfig;\r\n /** @deprecated 請使用 dataSource.maxHeight */\r\n maxHeight?: number | string;\r\n}>(), {})\r\n\r\n/** 優先 dataSource.maxHeight,兼容舊版單獨傳 maxHeight */\r\nconst tableMaxHeight = computed(() => props.dataSource.maxHeight ?? props.maxHeight);\r\n\r\nconst curdTableRef = ref<any>();\r\n\r\n// 使用 composables\r\nconst {\r\n listData,\r\n loading,\r\n multipleSelection,\r\n pageOptions,\r\n listSize,\r\n pagingLayout,\r\n defaultSort,\r\n refreshTable,\r\n handlePageChange,\r\n handleSizeChange,\r\n handleSortChange,\r\n handleSelectionChange,\r\n initData,\r\n getQueryParams,\r\n} = useTableData(props.dataSource);\r\n\r\nconst { initColumnDicts } = useDictData();\r\n\r\nconst localDicts = ref(LOCAL_DICTS);\r\n\r\n\r\n\r\nconst getRowKeyValue = (row: any) => row?.[props.dataSource.rowKey];\r\n\r\nconst columnToolbars = computed(() => {\r\n if (!props.dataSource.actions) return [];\r\n return props.dataSource.actions.filter((item: ActionConfig) =>\r\n item.action !== 'create' &&\r\n item.action !== 'back' &&\r\n (!item.position || item.position === 'table')\r\n );\r\n});\r\n\r\nconst isRowSelectable = (row: any) => {\r\n return !props.dataSource.disabledCheckedRowKeys?.includes(getRowKeyValue(row));\r\n}\r\n\r\nconst columnToolbarWidth = computed(() => {\r\n const customWidth = props.dataSource.actionColumnWidth;\r\n if (customWidth !== undefined && customWidth !== null && customWidth !== '') {\r\n return typeof customWidth === 'number' ? `${customWidth}px` : customWidth;\r\n }\r\n let width = columnToolbars.value.length * TOOLBAR_BUTTON_WIDTH;\r\n if (width < MIN_TOOLBAR_WIDTH) width = MIN_TOOLBAR_WIDTH;\r\n return `${width + 40}px`;\r\n});\r\n\r\n/**\r\n * 操作按鈕隱藏顯示\r\n */\r\nconst buttonVisible = (data: any, action: any) => {\r\n return isButtonVisible(data, action);\r\n}\r\n\r\n/**\r\n * 操作按鈕隱藏顯示\r\n */\r\nconst buttonDisabled = (data: any, action: any) => {\r\n return isButtonDisabled(data, action);\r\n}\r\n\r\n/**\r\n * 自定義宽度\r\n */\r\nconst calColumnWidth = (item: any) => {\r\n return calculateColumnWidth(item);\r\n}\r\n\r\n/**\r\n *\r\n * @param item\r\n */\r\nconst calColumnAlign = (item: any) => {\r\n const align = item.align || props.dataSource.align\r\n if (align) return align;\r\n else if ((item.label?.indexOf('時間') > -1 ? 'center' : '')) return 'center';\r\n else if (item.type == 'dictTag' || item.type == 'image' || item.type == 'switch') return 'center';\r\n else return 'left'\r\n}\r\n\r\n/**\r\n * 单元格内容从字典表\r\n * @param {*} item\r\n * @param {*} row\r\n */\r\nconst fromDbDicts = (item: any, row: any) => {\r\n const dictData = proxy?.useDict(item.dictType)[item.dictType];\r\n if (dictData != null) {\r\n const data = row[item.prop];\r\n const res = dictData.filter((res) => res.value == data);\r\n if (res.length == 0) return \"\";\r\n else return res[0].label;\r\n } else console.error(item.dictType + \" 字典不存在\");\r\n}\r\n\r\n/**\r\n * 单元格内容从本地字典表\r\n * @param {*} item\r\n * @param {*} row\r\n */\r\nconst fromLocalDicts = (item: any, row: any) => {\r\n const dictData = localDicts.value[item.dictType];\r\n if (dictData != null) {\r\n const data = row[item.prop];\r\n const res = dictData.find((res: any) => res.value == data);\r\n if (res == null) return \"\";\r\n else return res.label;\r\n } else console.error(item.dictType + \" 字典不存在\");\r\n}\r\n\r\n/**\r\n * 從data數據中獲取數據\r\n * @param item\r\n * @param row\r\n */\r\nconst fromData = (item: any, row: any) => {\r\n if (item.data != null) {\r\n const data = row[item.prop];\r\n const res = item.data.find((res: any) => res.value == data);\r\n if (res == null) return \"\";\r\n else return res.label;\r\n } else console.error(\"未配置data屬性\");\r\n}\r\n\r\nconst getTagType = (item: any, row: any) => {\r\n if (item.data != null) {\r\n const data = row[item.prop];\r\n const res = item.data.find((res: any) => res.value == data);\r\n if (res == null) return \"info\";\r\n else return res.tagType;\r\n } else console.error(\"未配置data屬性\");\r\n}\r\n\r\n/**\r\n * 支持深度属性绑定\r\n */\r\nconst deepJsonProp = (data: any, item: any) => {\r\n return getDeepProperty(data, item);\r\n}\r\n\r\n/**\r\n * 选中变化时通知父组件\r\n */\r\nconst onSelectionChange = (val: any[]) => {\r\n const normalizedSelection = Array.isArray(val) ? val : [];\r\n handleSelectionChange(normalizedSelection);\r\n emits(\"selectionChange\", normalizedSelection);\r\n};\r\n\r\n/**\r\n * 行點擊事件\r\n */\r\nconst onRowClick = (row: any, column: any, event: Event) => props.dataSource.rowClick?.(row, column, event);\r\n\r\n/**\r\n * 操作栏按钮点击事件\r\n * @param {*} ev\r\n * @param {*} data\r\n */\r\nconst actionEvent = (ev: object, data: any) => emits(\"actionEvent\", ev, data)\r\n\r\n/**\r\n * 获取选中项\r\n */\r\nconst getSelection = () => {\r\n return multipleSelection.value;\r\n}\r\n\r\n/**\r\n * 初始化選擇行\r\n */\r\nconst applyDefaultSelectedRows = async () => {\r\n if (!props.dataSource.showSelection) return;\r\n if (!props.dataSource.checkedRowKeys?.length) return;\r\n await nextTick();\r\n\r\n const table = curdTableRef.value;\r\n if (!table?.toggleRowSelection) return;\r\n\r\n for (const row of listData.value) {\r\n const key = getRowKeyValue(row);\r\n if (key === undefined || key === null) continue;\r\n const keyStr = String(key);\r\n if (props.dataSource.checkedRowKeys?.includes(keyStr)) {\r\n table.toggleRowSelection(row, true);\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * 初始化禁用選擇行\r\n */\r\nconst applyDefaultDisabledSelectedRows = async () => {\r\n if (!props.dataSource.showSelection) return;\r\n if (!props.dataSource.disabledCheckedRowKeys?.length) return;\r\n await nextTick();\r\n\r\n const table = curdTableRef.value;\r\n if (!table?.toggleRowSelection) return;\r\n\r\n for (const row of listData.value) {\r\n const key = getRowKeyValue(row);\r\n if (key === undefined || key === null) continue;\r\n const keyStr = String(key);\r\n if (props.dataSource.disabledCheckedRowKeys?.includes(keyStr)) {\r\n table.toggleRowSelection(row, true);\r\n }\r\n }\r\n};\r\n\r\n\r\n\r\nconst initTableData = async () => {\r\n await initColumnDicts(props.dataSource.columns);\r\n await initData();\r\n await applyDefaultDisabledSelectedRows();\r\n await applyDefaultSelectedRows();\r\n}\r\n\r\nonMounted(() => initTableData());\r\ndefineExpose({ refreshTable, getSelection, initData: initTableData, getQueryParams })\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.paging {\r\n padding: 12px 15px 15px 15px;\r\n font-size: 14px;\r\n // background-color: white;\r\n}\r\n\r\n.image-preview {\r\n display: flex;\r\n justify-content: center;\r\n}\r\n\r\n.el-switch .is-disabled {\r\n opacity: 1;\r\n}\r\n\r\n.image-style {\r\n display: flex;\r\n justify-content: center;\r\n}\r\n\r\n.tc-text {\r\n display: flex;\r\n text-overflow: ellipsis;\r\n line-clamp: 2;\r\n -webkit-line-clamp: 2;\r\n overflow: hidden;\r\n -webkit-box-orient: vertical;\r\n}\r\n\r\n.el-column-tag {\r\n margin-right: 2px;\r\n}\r\n\r\n.color-cell {\r\n display: flex;\r\n justify-content: center;\r\n\r\n &-item {\r\n width: 24px;\r\n height: 24px;\r\n border-radius: 2px;\r\n }\r\n}\r\n</style>\r\n","<template>\r\n <div class=\"upload-file\">\r\n <el-upload\r\n multiple\r\n :action=\"uploadFileUrl\"\r\n :before-upload=\"handleBeforeUpload\"\r\n :file-list=\"fileList\"\r\n :limit=\"limit\"\r\n :on-error=\"handleUploadError\"\r\n :on-exceed=\"handleExceed\"\r\n :on-success=\"handleUploadSuccess\"\r\n :show-file-list=\"false\"\r\n :headers=\"headers\"\r\n class=\"upload-file-uploader\"\r\n ref=\"fileUploadRef\"\r\n >\r\n <!-- 上传按钮 -->\r\n <el-button type=\"primary\">选取文件</el-button>\r\n </el-upload>\r\n <!-- 上传提示 -->\r\n <div class=\"el-upload__tip\" v-if=\"showTip\">\r\n 請上傳\r\n <template v-if=\"fileSize\">\r\n 大小不超過 <b style=\"color: #f56c6c\">{{ fileSize }}MB</b>\r\n </template>\r\n <template v-if=\"fileType\">\r\n 格式為 <b style=\"color: #f56c6c\">{{ fileType.join(\"/\") }}</b>\r\n </template>\r\n 的文件\r\n </div>\r\n <!-- 文件列表 -->\r\n <transition-group v-if=\"showFileList\" class=\"upload-file-list el-upload-list el-upload-list--text\" name=\"el-fade-in-linear\" tag=\"ul\">\r\n <li :key=\"file.uid\" class=\"el-upload-list__item ele-upload-list__item-content\" v-for=\"(file, index) in fileList\">\r\n <el-link :href=\"`${file.url}`\" :underline=\"false\" target=\"_blank\">\r\n <span class=\"el-icon-document\"> {{ getFileName(file.name) }} </span>\r\n </el-link>\r\n <div class=\"ele-upload-list__item-content-action\">\r\n <el-link :underline=\"false\" @click=\"handleDelete(index)\" type=\"danger\">删除</el-link>\r\n </div>\r\n </li>\r\n </transition-group>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { listByIds, delOss } from \"@/api/system/oss\";\r\nimport { propTypes } from '@/utils/propTypes';\r\nimport { getGlobalHeaders, getBaseUrl } from \"@/utils/http\";\r\n\r\nconst props = defineProps({\r\n modelValue: [String, Object, Array],\r\n // 数量限制\r\n limit: propTypes.number.def(5),\r\n // 大小限制(MB)\r\n fileSize: propTypes.number.def(5),\r\n // 文件类型, 例如['png', 'jpg', 'jpeg']\r\n fileType: propTypes.array.def([\"doc\", \"xls\", \"ppt\", \"txt\", \"pdf\"]),\r\n // 是否显示提示\r\n showTip: propTypes.bool.def(true),\r\n // 是否显示文件列表\r\n showFileList: propTypes.bool.def(true),\r\n});\r\n\r\nconst { proxy } = getCurrentInstance() as ComponentInternalInstance;\r\nconst emit = defineEmits(['update:modelValue']);\r\nconst number = ref(0);\r\nconst uploadList = ref<any[]>([]);\r\n\r\nconst baseUrl = getBaseUrl();\r\nconst uploadFileUrl = ref(baseUrl + \"/resource/oss/upload\"); // 上传文件服务器地址\r\nconst headers = ref(getGlobalHeaders());\r\n\r\nconst fileList = ref<any[]>([]);\r\nconst showTip = computed(\r\n () => props.showTip && (props.fileType || props.fileSize)\r\n);\r\n\r\nconst fileUploadRef = ref<ElUploadInstance>();\r\n\r\nwatch(() => props.modelValue, async val => {\r\n if (val) {\r\n if (!props.showFileList) return;\r\n let temp = 1;\r\n // 首先将值转为数组\r\n let list = [];\r\n if (Array.isArray(val)) {\r\n list = val;\r\n } else {\r\n const res = await listByIds(val as string)\r\n list = res.data.map((oss) => {\r\n const data = { name: oss.originalName, url: oss.url, ossId: oss.ossId };\r\n return data;\r\n });\r\n }\r\n // 然后将数组转为对象数组\r\n fileList.value = list.map(item => {\r\n item = { name: item.name, url: item.url, ossId: item.ossId };\r\n item.uid = item.uid || new Date().getTime() + temp++;\r\n return item;\r\n });\r\n } else {\r\n fileList.value = [];\r\n return [];\r\n }\r\n}, { deep: true, immediate: true });\r\n\r\n// 上传前校检格式和大小\r\nconst handleBeforeUpload = (file: any) => {\r\n // 校检文件类型\r\n if (props.fileType.length) {\r\n const fileName = file.name.split('.');\r\n const fileExt = fileName[fileName.length - 1];\r\n const isTypeOk = props.fileType.indexOf(fileExt) >= 0;\r\n if (!isTypeOk) {\r\n proxy?.$modal.msgError(`文件格式不正確, 請上傳${props.fileType.join(\"/\")}格式文件!`);\r\n return false;\r\n }\r\n }\r\n // 校检文件大小\r\n if (props.fileSize) {\r\n const isLt = file.size / 1024 / 1024 < props.fileSize;\r\n if (!isLt) {\r\n proxy?.$modal.msgError(`上傳文件大小不超過 ${props.fileSize} MB!`);\r\n return false;\r\n }\r\n }\r\n proxy?.$modal.loading(\"正在上傳文件,請稍等...\");\r\n number.value++;\r\n return true;\r\n}\r\n\r\n// 文件个数超出\r\nconst handleExceed = () => {\r\n proxy?.$modal.msgError(`上傳文件數量不能超過 ${props.limit} 个!`);\r\n}\r\n\r\n// 上传失败\r\nconst handleUploadError = () => {\r\n proxy?.$modal.msgError(\"上傳文件失敗\");\r\n}\r\n\r\n// 上传成功回调\r\nconst handleUploadSuccess = (res: any, file: UploadFile) => {\r\n if (res.code === 200) {\r\n uploadList.value.push({ name: res.data.fileName, url: res.data.url, ossId: res.data.ossId });\r\n uploadedSuccessfully();\r\n } else {\r\n number.value--;\r\n proxy?.$modal.closeLoading();\r\n proxy?.$modal.msgError(res.msg);\r\n fileUploadRef.value?.handleRemove(file);\r\n uploadedSuccessfully();\r\n }\r\n}\r\n\r\n// 删除文件\r\nconst handleDelete = (index: number) => {\r\n const ossId = fileList.value[index].ossId;\r\n delOss(ossId);\r\n fileList.value.splice(index, 1);\r\n emit(\"update:modelValue\", listToString(fileList.value));\r\n}\r\n\r\n// 上传结束处理\r\nconst uploadedSuccessfully = () => {\r\n if (number.value > 0 && uploadList.value.length === number.value) {\r\n fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value);\r\n uploadList.value = [];\r\n number.value = 0;\r\n emit(\"update:modelValue\", listToString(fileList.value));\r\n proxy?.$modal.closeLoading();\r\n }\r\n}\r\n\r\n// 获取文件名称\r\nconst getFileName = (name: string) => {\r\n // 如果是url那么取最后的名字 如果不是直接返回\r\n if (name.lastIndexOf(\"/\") > -1) {\r\n return name.slice(name.lastIndexOf(\"/\") + 1);\r\n } else {\r\n return name;\r\n }\r\n}\r\n\r\n// 对象转成指定字符串分隔\r\nconst listToString = (list: any[], separator?: string) => {\r\n let strs = \"\";\r\n separator = separator || \",\";\r\n list.forEach(item => {\r\n if (item.ossId) {\r\n strs += item.ossId + separator;\r\n }\r\n })\r\n return strs != \"\" ? strs.substring(0, strs.length - 1) : \"\";\r\n}\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n.upload-file-uploader {\r\n margin-bottom: 5px;\r\n}\r\n\r\n.upload-file-list .el-upload-list__item {\r\n border: 1px solid #e4e7ed;\r\n line-height: 2;\r\n margin-bottom: 10px;\r\n position: relative;\r\n padding-left: 10px;\r\n}\r\n\r\n.upload-file-list .ele-upload-list__item-content {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n color: inherit;\r\n}\r\n\r\n.ele-upload-list__item-content-action .el-link {\r\n margin-right: 10px;\r\n}\r\n</style>\r\n","<template>\r\n <div>\r\n <el-upload\r\n ref=\"imageUploadRef\"\r\n multiple\r\n :action=\"uploadImgUrl\"\r\n list-type=\"picture-card\"\r\n :on-success=\"handleUploadSuccess\"\r\n :before-upload=\"handleBeforeUpload\"\r\n :limit=\"limit\"\r\n :accept=\"fileAccept\"\r\n :on-error=\"handleUploadError\"\r\n :on-exceed=\"handleExceed\"\r\n :before-remove=\"handleDelete\"\r\n :show-file-list=\"true\"\r\n :headers=\"headers\"\r\n :file-list=\"fileList\"\r\n :on-preview=\"handlePictureCardPreview\"\r\n :class=\"{ hide: fileList.length >= limit }\"\r\n >\r\n <el-icon class=\"avatar-uploader-icon\">\r\n <plus />\r\n </el-icon>\r\n </el-upload>\r\n <!-- 上傳提示 -->\r\n <div v-if=\"showTip\" class=\"el-upload__tip\">\r\n 請上傳\r\n <template v-if=\"fileSize\">\r\n 大小不超過 <b style=\"color: #f56c6c\">{{ fileSize }}MB</b>\r\n </template>\r\n <template v-if=\"fileType\">\r\n <template v-if=\"fileSize\">、</template>格式為 <b style=\"color: #f56c6c\">{{ fileType.join('/') }}</b>\r\n </template>\r\n <template v-if=\"imageSize\">\r\n <template v-if=\"fileSize || fileType\">。</template>建議尺寸 <b style=\"color: #f56c6c\">{{ imageSize }}</b>\r\n </template>\r\n 的文件\r\n </div>\r\n\r\n <el-dialog v-model=\"dialogVisible\" title=\"預覽\" width=\"800px\" append-to-body>\r\n <img :src=\"dialogImageUrl\" style=\"display: block; max-width: 100%; margin: 0 auto\" />\r\n </el-dialog>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { listByIds, delOss } from '@/api/system/oss';\r\nimport { OssVO } from '@/api/system/oss/types';\r\nimport { propTypes } from '@/utils/propTypes';\r\nimport { getGlobalHeaders, getBaseUrl } from '@/utils/http';\r\nimport { compressAccurately } from 'image-conversion';\r\n\r\nconst props = defineProps({\r\n modelValue: {\r\n type: [String, Object, Array],\r\n default: () => []\r\n },\r\n // 圖片數量限制\r\n limit: propTypes.number.def(5),\r\n // 大小限制(MB)\r\n fileSize: propTypes.number.def(5),\r\n // 文件類型, 例如['png', 'jpg', 'jpeg']\r\n fileType: propTypes.array.def(['png', 'jpg', 'jpeg']),\r\n // 是否顯示提示\r\n isShowTip: {\r\n type: Boolean,\r\n default: true\r\n },\r\n // 建議圖片尺寸,如 \"200×200px\"\r\n imageSize: propTypes.string.def(''),\r\n // 是否支援壓縮,預設否\r\n compressSupport: {\r\n type: Boolean,\r\n default: false\r\n },\r\n // 壓縮目標大小,單位KB。預設300KB以上文件才壓縮,並壓縮至300KB以內\r\n compressTargetSize: propTypes.number.def(300),\r\n // 自訂高度\r\n height: propTypes.string.def('148px')\r\n});\r\n\r\nconst { proxy } = getCurrentInstance() as ComponentInternalInstance;\r\nconst emit = defineEmits(['update:modelValue']);\r\nconst number = ref(0);\r\nconst uploadList = ref<any[]>([]);\r\nconst dialogImageUrl = ref('');\r\nconst dialogVisible = ref(false);\r\n\r\nconst baseUrl = getBaseUrl();\r\nconst uploadImgUrl = ref(baseUrl + '/resource/oss/upload'); // 上傳的圖片伺服器地址\r\nconst headers = ref(getGlobalHeaders());\r\n\r\nconst fileList = ref<any[]>([]);\r\nconst showTip = computed(() => props.isShowTip && (props.fileType || props.fileSize || props.imageSize));\r\n\r\nconst imageUploadRef = ref<ElUploadInstance>();\r\n\r\n// 監聽 fileType 變化,更新 fileAccept\r\nconst fileAccept = computed(() => props.fileType.map((type) => `.${type}`).join(','));\r\n\r\nwatch(\r\n () => props.modelValue,\r\n async (val: string) => {\r\n if (val) {\r\n // 首先將值轉為數組\r\n let list: OssVO[] = [];\r\n if (Array.isArray(val)) {\r\n list = val as OssVO[];\r\n } else {\r\n const res = await listByIds(val);\r\n list = res.data;\r\n }\r\n // 然後將數組轉為對象數組\r\n fileList.value = list.map((item) => {\r\n // 字符串回顯處理 如果此處存的是url可直接回顯 如果存的是id需要調用接口查出來\r\n let itemData;\r\n if (typeof item === 'string') {\r\n itemData = { name: item, url: item };\r\n } else {\r\n // 此處name使用ossId 防止刪除出現重名\r\n itemData = { name: item.ossId, url: item.url, ossId: item.ossId };\r\n }\r\n return itemData;\r\n });\r\n } else {\r\n fileList.value = [];\r\n return [];\r\n }\r\n },\r\n { deep: true, immediate: true }\r\n);\r\n\r\n/** 上傳前loading加載 */\r\nconst handleBeforeUpload = (file: any) => {\r\n let isImg = false;\r\n if (props.fileType.length) {\r\n let fileExtension = '';\r\n if (file.name.lastIndexOf('.') > -1) {\r\n fileExtension = file.name.slice(file.name.lastIndexOf('.') + 1);\r\n }\r\n isImg = props.fileType.some((type: any) => {\r\n if (file.type.indexOf(type) > -1) return true;\r\n if (fileExtension && fileExtension.indexOf(type) > -1) return true;\r\n return false;\r\n });\r\n } else {\r\n isImg = file.type.indexOf('image') > -1;\r\n }\r\n if (!isImg) {\r\n proxy?.$modal.msgError(`文件格式不正確, 請上傳${props.fileType.join('/')}圖片格式文件!`);\r\n return false;\r\n }\r\n if (file.name.includes(',')) {\r\n proxy?.$modal.msgError('文件名不正確,不能包含英文逗號!');\r\n return false;\r\n }\r\n if (props.fileSize) {\r\n const isLt = file.size / 1024 / 1024 < props.fileSize;\r\n if (!isLt) {\r\n proxy?.$modal.msgError(`上傳圖片大小不能超過 ${props.fileSize} MB!`);\r\n return false;\r\n }\r\n }\r\n\r\n //壓縮圖片,開啟壓縮並且大於指定的壓縮大小時才壓縮\r\n if (props.compressSupport && file.size / 1024 > props.compressTargetSize) {\r\n proxy?.$modal.loading('正在上傳圖片,請稍候...');\r\n number.value++;\r\n return compressAccurately(file, props.compressTargetSize);\r\n } else {\r\n proxy?.$modal.loading('正在上傳圖片,請稍候...');\r\n number.value++;\r\n }\r\n};\r\n\r\n// 文件個數超出\r\nconst handleExceed = () => {\r\n proxy?.$modal.msgError(`上傳文件數量不能超過 ${props.limit} 個!`);\r\n};\r\n\r\n// 上傳成功回調\r\nconst handleUploadSuccess = (res: any, file: UploadFile) => {\r\n if (res.code === 200) {\r\n uploadList.value.push({ name: res.data.fileName, url: res.data.url, ossId: res.data.ossId });\r\n uploadedSuccessfully();\r\n } else {\r\n number.value--;\r\n proxy?.$modal.closeLoading();\r\n proxy?.$modal.msgError(res.msg);\r\n imageUploadRef.value?.handleRemove(file);\r\n uploadedSuccessfully();\r\n }\r\n};\r\n\r\n// 刪除圖片\r\nconst handleDelete = (file: UploadFile): boolean => {\r\n const index = fileList.value.map((f) => f.name).indexOf(file.name);\r\n if (index > -1 && uploadList.value.length === number.value) {\r\n const ossId = fileList.value[index].ossId;\r\n delOss(ossId);\r\n fileList.value.splice(index, 1);\r\n emit('update:modelValue', listToString(fileList.value));\r\n return false;\r\n }\r\n return true;\r\n};\r\n\r\n// 上傳結束處理\r\nconst uploadedSuccessfully = () => {\r\n if (number.value > 0 && uploadList.value.length === number.value) {\r\n fileList.value = fileList.value.filter((f) => f.url !== undefined).concat(uploadList.value);\r\n uploadList.value = [];\r\n number.value = 0;\r\n emit('update:modelValue', listToString(fileList.value));\r\n proxy?.$modal.closeLoading();\r\n }\r\n};\r\n\r\n// 上傳失敗\r\nconst handleUploadError = () => {\r\n proxy?.$modal.msgError('上傳圖片失敗');\r\n proxy?.$modal.closeLoading();\r\n};\r\n\r\n// 預覽\r\nconst handlePictureCardPreview = (file: any) => {\r\n dialogImageUrl.value = file.url;\r\n dialogVisible.value = true;\r\n};\r\n\r\n// 對象轉成指定字符串分隔\r\nconst listToString = (list: any[], separator?: string) => {\r\n let strs = '';\r\n separator = separator || ',';\r\n for (const i in list) {\r\n if (undefined !== list[i].ossId && list[i].url.indexOf('blob:') !== 0) {\r\n strs += list[i].ossId + separator;\r\n }\r\n }\r\n return strs != '' ? strs.substring(0, strs.length - 1) : '';\r\n};\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n// .el-upload--picture-card 控制加號部分\r\n:deep(.hide .el-upload--picture-card) {\r\n display: none;\r\n}\r\n\r\n// 自定义高度样式\r\n:deep(.el-upload--picture-card) {\r\n width: v-bind('props.height');\r\n height: v-bind('props.height');\r\n line-height: v-bind('props.height');\r\n}\r\n\r\n:deep(.el-upload-list__item) {\r\n width: v-bind('props.height');\r\n height: v-bind('props.height');\r\n}\r\n</style>\r\n","import { getRequest } from '@/utils/http';\r\nimport { AxiosPromise } from 'axios';\r\nimport { TranslationVO, TranslationForm, TranslationQuery } from '@/api/app/translation/types';\r\n\r\n/**\r\n * 查询語言翻譯列表\r\n * @param query\r\n * @returns {*}\r\n */\r\n\r\nexport const listTranslation = (query?: TranslationQuery): AxiosPromise<TranslationVO[]> => {\r\n return getRequest()({\r\n url: '/app/translation/list',\r\n method: 'get',\r\n params: query\r\n });\r\n};\r\n\r\n/**\r\n * 查询語言翻譯详细\r\n * @param translationId\r\n */\r\nexport const getTranslation = (translationId: string | number): AxiosPromise<TranslationVO> => {\r\n return getRequest()({\r\n url: '/app/translation/' + translationId,\r\n method: 'get'\r\n });\r\n};\r\n\r\n/**\r\n * 查询語言翻譯根據名稱\r\n * @param translationId\r\n */\r\nexport const getTranslationByName = (name: string): AxiosPromise<TranslationVO> => {\r\n return getRequest()({\r\n url: '/app/translation/getByName',\r\n method: 'post',\r\n headers: {\r\n repeatSubmit: false\r\n },\r\n data: { name: name }\r\n });\r\n};\r\n\r\n/**\r\n * 新增語言翻譯\r\n * @param data\r\n */\r\nexport const addTranslation = (data: TranslationForm) => {\r\n return getRequest()({\r\n url: '/app/translation',\r\n method: 'post',\r\n data: data\r\n });\r\n};\r\n\r\n/**\r\n * 新增語言翻譯\r\n * @param data\r\n */\r\nexport const addTranslationBatch = (data: Array<TranslationForm>) => {\r\n return getRequest()({\r\n url: '/app/translation/batchAdd',\r\n method: 'post',\r\n data: data\r\n });\r\n};\r\n\r\n/**\r\n * 修改語言翻譯\r\n * @param data\r\n */\r\nexport const updateTranslation = (data: TranslationForm) => {\r\n return getRequest()({\r\n url: '/app/translation',\r\n method: 'put',\r\n data: data\r\n });\r\n};\r\n\r\n/**\r\n * 删除語言翻譯\r\n * @param translationId\r\n */\r\nexport const delTranslation = (translationId: string | number | Array<string | number>) => {\r\n return getRequest()({\r\n url: '/app/translation/' + translationId,\r\n method: 'delete'\r\n });\r\n};\r\n","import { TranslationVO } from '@/api/app/translation/types';\r\n\r\nexport const useTranslationStore = defineStore('translation', () => {\r\n const translation = ref<\r\n Array<{\r\n key: string;\r\n value: TranslationVO;\r\n }>\r\n >([]);\r\n\r\n /**\r\n * 获取語言\r\n * @param _key 語言key\r\n */\r\n const getTranslation = (_key: string): TranslationVO | null => {\r\n if (_key == null && _key == '') {\r\n return null;\r\n }\r\n try {\r\n for (let i = 0; i < translation.value.length; i++) {\r\n if (translation.value[i].key == _key) {\r\n return translation.value[i].value;\r\n }\r\n }\r\n } catch (e) {\r\n return null;\r\n }\r\n return null;\r\n };\r\n\r\n /**\r\n * 设置語言\r\n * @param _key 語言key\r\n * @param _value 語言value\r\n */\r\n const setTranslation = (_key: string, _value: TranslationVO) => {\r\n if (_key !== null && _key !== '') {\r\n translation.value.push({\r\n key: _key,\r\n value: _value\r\n });\r\n }\r\n };\r\n\r\n /**\r\n * 清除翻譯\r\n */\r\n const clearTranslation = () => {\r\n translation.value = [];\r\n };\r\n\r\n return {\r\n translation,\r\n getTranslation,\r\n setTranslation,\r\n clearTranslation\r\n };\r\n});\r\n\r\nexport default useTranslationStore;\r\n","import { addTranslationBatch, getTranslationByName } from '@/api/app/translation';\r\nimport { TranslationVO } from '@/api/app/translation/types';\r\nimport { useTranslationStore } from '@/store/modules/translation';\r\n/**\r\n * 获取翻譯数据\r\n */\r\nexport const useTranslation = async (name: string): Promise<{ [key: string]: TranslationVO }> => {\r\n const res = ref<{\r\n [key: string]: TranslationVO;\r\n }>({});\r\n const dataVo = useTranslationStore().getTranslation(name);\r\n if (dataVo) {\r\n res.value[name] = dataVo;\r\n } else {\r\n const { data } = await getTranslationByName(name);\r\n res.value[name] = data;\r\n useTranslationStore().setTranslation(name, data);\r\n }\r\n return res.value;\r\n};\r\n\r\n/**\r\n * 保存翻譯\r\n */\r\nexport const saveTranslation = async (translateInputRefs: any) => {\r\n if (translateInputRefs != null) {\r\n const list = <any>[];\r\n for (let i = 0; i < translateInputRefs.length; i++) {\r\n const result = translateInputRefs[i].getTranslate();\r\n if (list.some((item: any) => item.name === result.name)) {\r\n continue; // 如果已经存在同名翻译,则跳过\r\n }\r\n list.push(result);\r\n }\r\n await addTranslationBatch(list);\r\n useTranslationStore().clearTranslation();\r\n }\r\n};\r\n","<template>\r\n <div style=\"width: 100%;\">\r\n <div style=\"margin-bottom: 2px;\">\r\n <el-button\r\n size=\"small\"\r\n v-for=\"item in languages\"\r\n :key=\"item.id\"\r\n :type=\"item.selected ? 'primary' : 'default'\"\r\n @click=\"changeLanguage(item)\"\r\n style=\"margin-right: -8px;\"\r\n >{{\r\n item.name }}</el-button\r\n >\r\n </div>\r\n <template v-for=\"item in languages\" :key=\"item.id\">\r\n <el-input\r\n v-show=\"item.selected\"\r\n v-model=\"item.text\"\r\n :type=\"props.type\"\r\n :placeholder=\"props.placeholder + '(' + item.name + ')'\"\r\n :disabled=\"props.disabled\"\r\n :rows=\"props.rows\"\r\n :show-word-limit=\"props.showWordLimit\"\r\n :maxlength=\"props.maxlength\"\r\n @input=\"(text: any) => updateModelValue(item, text)\"\r\n @blur=\"zhHkOnBlur(item)\"\r\n @focus=\"setInputFocus()\"\r\n >\r\n </el-input>\r\n </template>\r\n </div>\r\n</template>\r\n<script setup lang=\"ts\">\r\nimport { useTranslation } from '@/utils/translation';\r\nconst emits = defineEmits([\"update:modelValue\", \"focus\"]);\r\nconst props = defineProps(['modelValue', 'placeholder', \"disabled\", \"type\", \"rows\", \"showWordLimit\", \"maxlength\"])\r\nconst languages = ref<any>([{ name: '繁體', id: 'zhHk', selected: true, text: \"\" }, { name: '簡體', id: 'zhCn', selected: false, text: '' }, { name: '英文', id: 'enUs', selected: false, text: '' }])\r\nconst loading = ref(false);\r\nconst hasInit = ref(false);\r\nconst updateModelValue = (item: any, text: any) => {\r\n item.text = text;\r\n if (item.id == \"zhHk\") {\r\n emits(\"update:modelValue\", text);\r\n }\r\n};\r\n\r\nconst zhHkOnBlur = async (item: any) => {\r\n if (item.id == \"zhHk\") {\r\n await getTranslateData()\r\n }\r\n}\r\n\r\n/**\r\n * 獲取翻譯查詢數據\r\n */\r\nconst getTranslateData = async () => {\r\n if (loading.value) return;\r\n const name = languages.value[0].text;\r\n if (name != '') {\r\n loading.value = true\r\n const data = await useTranslation(name);\r\n loading.value = false\r\n if (data[name] != null) {\r\n for (let i = 0; i < languages.value.length; i++) {\r\n if (!languages.value[i].text) {\r\n const languageTemp = languages.value[i]\r\n languageTemp.text = data[name][languageTemp.id];\r\n }\r\n }\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 切换语言\r\n */\r\nconst changeLanguage = async (lang: any) => {\r\n languages.value.map((res: any) => res.selected = false)\r\n lang.selected = true\r\n // await getTranslateData()\r\n}\r\n\r\n/**\r\n * 獲取翻譯\r\n */\r\nconst getTranslate = () => {\r\n const data = <any>{ name: languages.value[0].text };\r\n languages.value.forEach((element: { id: string | number; text: any; }) => data[element.id] = element.text);\r\n return data;\r\n}\r\n\r\nconst setInputFocus = () => emits(\"focus\");\r\n\r\nwatch(() => props.modelValue, (newVal) => {\r\n if (newVal) {\r\n const item = languages.value.filter((res: any) => res.selected)[0]\r\n item.text = newVal;\r\n if (!hasInit.value) {\r\n getTranslateData();\r\n }\r\n hasInit.value = true;\r\n }\r\n}, {\r\n immediate: true,\r\n deep: true\r\n});\r\n\r\ndefineExpose({ getTranslate })\r\n</script>\r\n<style scoped></style>\r\n","<template>\r\n <div>\r\n <el-upload\r\n v-if=\"type === 'url'\"\r\n :action=\"upload.url\"\r\n :before-upload=\"handleBeforeUpload\"\r\n :on-success=\"handleUploadSuccess\"\r\n :on-error=\"handleUploadError\"\r\n class=\"editor-img-uploader\"\r\n name=\"file\"\r\n :show-file-list=\"false\"\r\n :headers=\"upload.headers\"\r\n >\r\n <i ref=\"uploadRef\"></i>\r\n </el-upload>\r\n </div>\r\n <div class=\"editor\">\r\n <quill-editor\r\n ref=\"quillEditorRef\"\r\n v-model:content=\"content\"\r\n content-type=\"html\"\r\n :options=\"options\"\r\n :style=\"styles\"\r\n @text-change=\"(e: any) => $emit('update:modelValue', content)\"\r\n />\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport '@vueup/vue-quill/dist/vue-quill.snow.css';\r\n\r\nimport { QuillEditor, Quill } from '@vueup/vue-quill';\r\nimport { propTypes } from '@/utils/propTypes';\r\nimport { getGlobalHeaders, getBaseUrl } from '@/utils/http';\r\n\r\ndefineEmits(['update:modelValue']);\r\n\r\nconst props = defineProps({\r\n /* 编辑器的内容 */\r\n modelValue: propTypes.string,\r\n /* 高度 */\r\n height: propTypes.number.def(400),\r\n /* 最小高度 */\r\n minHeight: propTypes.number.def(400),\r\n /* 只读 */\r\n readOnly: propTypes.bool.def(false),\r\n /* 上传文件大小限制(MB) */\r\n fileSize: propTypes.number.def(5),\r\n /* 类型(base64格式、url格式) */\r\n type: propTypes.string.def('url')\r\n});\r\n\r\nconst { proxy } = getCurrentInstance() as ComponentInternalInstance;\r\n\r\nconst upload = reactive<UploadOption>({\r\n headers: getGlobalHeaders(),\r\n url: getBaseUrl() + '/resource/oss/upload'\r\n});\r\nconst quillEditorRef = ref();\r\nconst uploadRef = ref<HTMLDivElement>();\r\n\r\nconst options = ref<any>({\r\n theme: 'snow',\r\n bounds: document.body,\r\n debug: 'warn',\r\n modules: {\r\n // 工具栏配置\r\n toolbar: {\r\n container: [\r\n ['bold', 'italic', 'underline', 'strike'], // 加粗 斜体 下划线 删除线\r\n ['blockquote', 'code-block'], // 引用 代码块\r\n [{ list: 'ordered' }, { list: 'bullet' }], // 有序、无序列表\r\n [{ indent: '-1' }, { indent: '+1' }], // 缩进\r\n [{ size: ['small', false, 'large', 'huge'] }], // 字体大小\r\n [{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题\r\n [{ color: [] }, { background: [] }], // 字体颜色、字体背景颜色\r\n [{ align: [] }], // 对齐方式\r\n ['clean'], // 清除文本格式\r\n ['link', 'image', 'video'] // 链接、图片、视频\r\n ],\r\n handlers: {\r\n image: (value: boolean) => {\r\n if (value) {\r\n // 调用element图片上传\r\n uploadRef.value.click();\r\n } else {\r\n (Quill as any).format('image', true);\r\n }\r\n }\r\n }\r\n }\r\n },\r\n placeholder: '请输入内容',\r\n readOnly: props.readOnly\r\n});\r\n\r\nconst styles = computed(() => {\r\n const style: any = {};\r\n if (props.minHeight) {\r\n style.minHeight = `${props.minHeight}px`;\r\n }\r\n if (props.height) {\r\n style.height = `${props.height}px`;\r\n }\r\n return style;\r\n});\r\n\r\nconst content = ref('');\r\nwatch(\r\n () => props.modelValue,\r\n (v: string) => {\r\n if (v !== content.value) {\r\n content.value = v || '<p></p>';\r\n }\r\n },\r\n { immediate: true }\r\n);\r\n\r\n// 图片上传成功返回图片地址\r\nconst handleUploadSuccess = (res: any) => {\r\n // 如果上传成功\r\n if (res.code === 200) {\r\n // 获取富文本实例\r\n const quill = toRaw(quillEditorRef.value).getQuill();\r\n // 获取光标位置\r\n const length = quill.selection.savedRange.index;\r\n // 插入图片,res为服务器返回的图片链接地址\r\n quill.insertEmbed(length, 'image', res.data.url);\r\n // 调整光标到最后\r\n quill.setSelection(length + 1);\r\n proxy?.$modal.closeLoading();\r\n } else {\r\n proxy?.$modal.msgError('图片插入失败');\r\n proxy?.$modal.closeLoading();\r\n }\r\n};\r\n\r\n// 图片上传前拦截\r\nconst handleBeforeUpload = (file: any) => {\r\n const type = ['image/jpeg', 'image/jpg', 'image/png', 'image/svg'];\r\n const isJPG = type.includes(file.type);\r\n //检验文件格式\r\n if (!isJPG) {\r\n proxy?.$modal.msgError(`图片格式错误!`);\r\n return false;\r\n }\r\n // 校检文件大小\r\n if (props.fileSize) {\r\n const isLt = file.size / 1024 / 1024 < props.fileSize;\r\n if (!isLt) {\r\n proxy?.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`);\r\n return false;\r\n }\r\n }\r\n proxy?.$modal.loading('正在上传文件,请稍候...');\r\n return true;\r\n};\r\n\r\n// 图片失败拦截\r\nconst handleUploadError = (err: any) => {\r\n proxy?.$modal.msgError('上传文件失败');\r\n};\r\n</script>\r\n\r\n<style>\r\n.editor-img-uploader {\r\n display: none;\r\n}\r\n\r\n.editor,\r\n.ql-toolbar {\r\n white-space: pre-wrap !important;\r\n line-height: normal !important;\r\n}\r\n\r\n.quill-img {\r\n display: none;\r\n}\r\n\r\n.ql-snow .ql-tooltip[data-mode='link']::before {\r\n content: '请输入链接地址:';\r\n}\r\n\r\n.ql-snow .ql-tooltip.ql-editing a.ql-action::after {\r\n border-right: 0;\r\n content: '保存';\r\n padding-right: 0;\r\n}\r\n\r\n.ql-snow .ql-tooltip[data-mode='video']::before {\r\n content: '请输入视频地址:';\r\n}\r\n\r\n.ql-snow .ql-picker.ql-size .ql-picker-label::before,\r\n.ql-snow .ql-picker.ql-size .ql-picker-item::before {\r\n content: '14px';\r\n}\r\n\r\n.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='small']::before,\r\n.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='small']::before {\r\n content: '10px';\r\n}\r\n\r\n.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='large']::before,\r\n.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='large']::before {\r\n content: '18px';\r\n}\r\n\r\n.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='huge']::before,\r\n.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='huge']::before {\r\n content: '32px';\r\n}\r\n\r\n.ql-snow .ql-picker.ql-header .ql-picker-label::before,\r\n.ql-snow .ql-picker.ql-header .ql-picker-item::before {\r\n content: '文本';\r\n}\r\n\r\n.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='1']::before,\r\n.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='1']::before {\r\n content: '标题1';\r\n}\r\n\r\n.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='2']::before,\r\n.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='2']::before {\r\n content: '标题2';\r\n}\r\n\r\n.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='3']::before,\r\n.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='3']::before {\r\n content: '标题3';\r\n}\r\n\r\n.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='4']::before,\r\n.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='4']::before {\r\n content: '标题4';\r\n}\r\n\r\n.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='5']::before,\r\n.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='5']::before {\r\n content: '标题5';\r\n}\r\n\r\n.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='6']::before,\r\n.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='6']::before {\r\n content: '标题6';\r\n}\r\n\r\n.ql-snow .ql-picker.ql-font .ql-picker-label::before,\r\n.ql-snow .ql-picker.ql-font .ql-picker-item::before {\r\n content: '标准字体';\r\n}\r\n\r\n.ql-snow .ql-picker.ql-font .ql-picker-label[data-value='serif']::before,\r\n.ql-snow .ql-picker.ql-font .ql-picker-item[data-value='serif']::before {\r\n content: '衬线字体';\r\n}\r\n\r\n.ql-snow .ql-picker.ql-font .ql-picker-label[data-value='monospace']::before,\r\n.ql-snow .ql-picker.ql-font .ql-picker-item[data-value='monospace']::before {\r\n content: '等宽字体';\r\n}\r\n</style>\r\n","// 日期格式化\r\nexport function parseTime(time: any, pattern?: string) {\r\n if (arguments.length === 0 || !time) {\r\n return null;\r\n }\r\n const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}';\r\n let date;\r\n if (typeof time === 'object') {\r\n date = time;\r\n } else {\r\n if (typeof time === 'string' && /^[0-9]+$/.test(time)) {\r\n time = parseInt(time);\r\n } else if (typeof time === 'string') {\r\n time = time\r\n .replace(new RegExp(/-/gm), '/')\r\n .replace('T', ' ')\r\n .replace(new RegExp(/\\.[\\d]{3}/gm), '');\r\n }\r\n if (typeof time === 'number' && time.toString().length === 10) {\r\n time = time * 1000;\r\n }\r\n date = new Date(time);\r\n }\r\n const formatObj: { [key: string]: any } = {\r\n y: date.getFullYear(),\r\n m: date.getMonth() + 1,\r\n d: date.getDate(),\r\n h: date.getHours(),\r\n i: date.getMinutes(),\r\n s: date.getSeconds(),\r\n a: date.getDay()\r\n };\r\n return format.replace(/{(y|m|d|h|i|s|a)+}/g, (result: string, key: string) => {\r\n let value = formatObj[key];\r\n // Note: getDay() returns 0 on Sunday\r\n if (key === 'a') {\r\n return ['日', '一', '二', '三', '四', '五', '六'][value];\r\n }\r\n if (result.length > 0 && value < 10) {\r\n value = '0' + value;\r\n }\r\n return value || 0;\r\n });\r\n}\r\n\r\n/**\r\n * 添加日期范围\r\n * @param params\r\n * @param dateRange\r\n * @param propName\r\n */\r\nexport const addDateRange = (params: any, dateRange: any[], propName?: string) => {\r\n const search = params;\r\n search.params = typeof search.params === 'object' && search.params !== null && !Array.isArray(search.params) ? search.params : {};\r\n dateRange = Array.isArray(dateRange) ? dateRange : [];\r\n if (dateRange[0] && dateRange[0].length == 10) {\r\n dateRange[0] = dateRange[0] + ' 00:00:00';\r\n dateRange[1] = dateRange[1] + ' 23:59:59';\r\n }\r\n if (typeof propName === 'undefined') {\r\n search.params['beginTime'] = dateRange[0];\r\n search.params['endTime'] = dateRange[1];\r\n } else {\r\n propName = propName.charAt(0).toUpperCase() + propName.slice(1);\r\n search.params['begin' + propName] = dateRange[0];\r\n search.params['end' + propName] = dateRange[1];\r\n }\r\n return search;\r\n};\r\n\r\n// 回显数据字典\r\nexport const selectDictLabel = (datas: any, value: number | string) => {\r\n if (value === undefined) {\r\n return '';\r\n }\r\n const actions: Array<string | number> = [];\r\n Object.keys(datas).some((key) => {\r\n if (datas[key].value == '' + value) {\r\n actions.push(datas[key].label);\r\n return true;\r\n }\r\n });\r\n if (actions.length === 0) {\r\n actions.push(value);\r\n }\r\n return actions.join('');\r\n};\r\n\r\n// 回显数据字典(字符串数组)\r\nexport const selectDictLabels = (datas: any, value: any, separator: any) => {\r\n if (value === undefined || value.length === 0) {\r\n return '';\r\n }\r\n if (Array.isArray(value)) {\r\n value = value.join(',');\r\n }\r\n const actions: any[] = [];\r\n const currentSeparator = undefined === separator ? ',' : separator;\r\n const temp = value.split(currentSeparator);\r\n Object.keys(value.split(currentSeparator)).some((val) => {\r\n let match = false;\r\n Object.keys(datas).some((key) => {\r\n if (datas[key].value == '' + temp[val]) {\r\n actions.push(datas[key].label + currentSeparator);\r\n match = true;\r\n }\r\n });\r\n if (!match) {\r\n actions.push(temp[val] + currentSeparator);\r\n }\r\n });\r\n return actions.join('').substring(0, actions.join('').length - 1);\r\n};\r\n\r\n// 字符串格式化(%s )\r\nexport function sprintf(str: string) {\r\n if (arguments.length !== 0) {\r\n let flag = true,\r\n i = 1;\r\n str = str.replace(/%s/g, function () {\r\n const arg = arguments[i++];\r\n if (typeof arg === 'undefined') {\r\n flag = false;\r\n return '';\r\n }\r\n return arg;\r\n });\r\n return flag ? str : '';\r\n }\r\n}\r\n\r\n// 转换字符串,undefined,null等转化为\"\"\r\nexport const parseStrEmpty = (str: any) => {\r\n if (!str || str == 'undefined' || str == 'null') {\r\n return '';\r\n }\r\n return str;\r\n};\r\n\r\n// 数据合并\r\nexport const mergeRecursive = (source: any, target: any) => {\r\n for (const p in target) {\r\n try {\r\n if (target[p].constructor == Object) {\r\n source[p] = mergeRecursive(source[p], target[p]);\r\n } else {\r\n source[p] = target[p];\r\n }\r\n } catch (e) {\r\n source[p] = target[p];\r\n }\r\n }\r\n return source;\r\n};\r\n\r\n/**\r\n * 构造树型结构数据\r\n * @param {*} data 数据源\r\n * @param {*} id id字段 默认 'id'\r\n * @param {*} parentId 父节点字段 默认 'parentId'\r\n * @param {*} children 孩子节点字段 默认 'children'\r\n */\r\nexport const handleTree = <T>(data: any[], id?: string, parentId?: string, children?: string): T[] => {\r\n const config: {\r\n id: string;\r\n parentId: string;\r\n childrenList: string;\r\n } = {\r\n id: id || 'id',\r\n parentId: parentId || 'parentId',\r\n childrenList: children || 'children'\r\n };\r\n\r\n const childrenListMap: any = {};\r\n const tree: T[] = [];\r\n for (const d of data) {\r\n const id = d[config.id];\r\n childrenListMap[id] = d;\r\n if (!d[config.childrenList]) {\r\n d[config.childrenList] = [];\r\n }\r\n }\r\n\r\n for (const d of data) {\r\n const parentId = d[config.parentId];\r\n const parentObj = childrenListMap[parentId];\r\n if (!parentObj) {\r\n tree.push(d);\r\n } else {\r\n parentObj[config.childrenList].push(d);\r\n }\r\n }\r\n return tree;\r\n};\r\n\r\n/**\r\n * 参数处理\r\n * @param {*} params 参数\r\n */\r\nexport const tansParams = (params: any) => {\r\n let result = '';\r\n for (const propName of Object.keys(params)) {\r\n const value = params[propName];\r\n const part = encodeURIComponent(propName) + '=';\r\n if (value !== null && value !== '' && typeof value !== 'undefined') {\r\n if (typeof value === 'object') {\r\n for (const key of Object.keys(value)) {\r\n if (value[key] !== null && value[key] !== '' && typeof value[key] !== 'undefined') {\r\n const params = propName + '[' + key + ']';\r\n const subPart = encodeURIComponent(params) + '=';\r\n result += subPart + encodeURIComponent(value[key]) + '&';\r\n }\r\n }\r\n } else {\r\n result += part + encodeURIComponent(value) + '&';\r\n }\r\n }\r\n }\r\n return result;\r\n};\r\n\r\n// 返回项目路径\r\nexport const getNormalPath = (p: string): string => {\r\n if (p.length === 0 || !p || p === 'undefined') {\r\n return p;\r\n }\r\n const res = p.replace('//', '/');\r\n if (res[res.length - 1] === '/') {\r\n return res.slice(0, res.length - 1);\r\n }\r\n return res;\r\n};\r\n\r\n// 验证是否为blob格式\r\nexport const blobValidate = (data: any) => {\r\n return data.type !== 'application/json';\r\n};\r\n\r\nexport default {\r\n handleTree\r\n};\r\n","import { parseTime } from '@/utils/ruoyi';\r\n\r\n/**\r\n * 表格时间格式化\r\n */\r\nexport const formatDate = (cellValue: string) => {\r\n if (cellValue == null || cellValue == '') return '';\r\n const date = new Date(cellValue);\r\n const year = date.getFullYear();\r\n const month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1;\r\n const day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate();\r\n const hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours();\r\n const minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes();\r\n const seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds();\r\n return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds;\r\n};\r\n\r\n/**\r\n * @param {number} time\r\n * @param {string} option\r\n * @returns {string}\r\n */\r\nexport const formatTime = (time: string, option: string) => {\r\n let t: number;\r\n if (('' + time).length === 10) {\r\n t = parseInt(time) * 1000;\r\n } else {\r\n t = +time;\r\n }\r\n const d: any = new Date(t);\r\n const now = Date.now();\r\n\r\n const diff = (now - d) / 1000;\r\n\r\n if (diff < 30) {\r\n return '刚刚';\r\n } else if (diff < 3600) {\r\n // less 1 hour\r\n return Math.ceil(diff / 60) + '分钟前';\r\n } else if (diff < 3600 * 24) {\r\n return Math.ceil(diff / 3600) + '小时前';\r\n } else if (diff < 3600 * 24 * 2) {\r\n return '1天前';\r\n }\r\n if (option) {\r\n return parseTime(t, option);\r\n } else {\r\n return d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分';\r\n }\r\n};\r\n\r\n/**\r\n * @param {string} url\r\n * @returns {Object}\r\n */\r\nexport const getQueryObject = (url: string) => {\r\n url = url == null ? window.location.href : url;\r\n const search = url.substring(url.lastIndexOf('?') + 1);\r\n const obj: { [key: string]: string } = {};\r\n const reg = /([^?&=]+)=([^?&=]*)/g;\r\n search.replace(reg, (rs, $1, $2) => {\r\n const name = decodeURIComponent($1);\r\n let val = decodeURIComponent($2);\r\n val = String(val);\r\n obj[name] = val;\r\n return rs;\r\n });\r\n return obj;\r\n};\r\n\r\n/**\r\n * @param {string} input value\r\n * @returns {number} output value\r\n */\r\nexport const byteLength = (str: string) => {\r\n // returns the byte length of an utf8 string\r\n let s = str.length;\r\n for (let i = str.length - 1; i >= 0; i--) {\r\n const code = str.charCodeAt(i);\r\n if (code > 0x7f && code <= 0x7ff) s++;\r\n else if (code > 0x7ff && code <= 0xffff) s += 2;\r\n if (code >= 0xdc00 && code <= 0xdfff) i--;\r\n }\r\n return s;\r\n};\r\n\r\n/**\r\n * @param {Array} actual\r\n * @returns {Array}\r\n */\r\nexport const cleanArray = (actual: Array<any>) => {\r\n const newArray: any[] = [];\r\n for (let i = 0; i < actual.length; i++) {\r\n if (actual[i]) {\r\n newArray.push(actual[i]);\r\n }\r\n }\r\n return newArray;\r\n};\r\n\r\n/**\r\n * @param {Object} json\r\n * @returns {Array}\r\n */\r\nexport const param = (json: any) => {\r\n if (!json) return '';\r\n return cleanArray(\r\n Object.keys(json).map((key) => {\r\n if (json[key] === undefined) return '';\r\n return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]);\r\n })\r\n ).join('&');\r\n};\r\n\r\n/**\r\n * @param {string} url\r\n * @returns {Object}\r\n */\r\nexport const param2Obj = (url: string) => {\r\n const search = decodeURIComponent(url.split('?')[1]).replace(/\\+/g, ' ');\r\n if (!search) {\r\n return {};\r\n }\r\n const obj: any = {};\r\n const searchArr = search.split('&');\r\n searchArr.forEach((v) => {\r\n const index = v.indexOf('=');\r\n if (index !== -1) {\r\n const name = v.substring(0, index);\r\n const val = v.substring(index + 1, v.length);\r\n obj[name] = val;\r\n }\r\n });\r\n return obj;\r\n};\r\n\r\n/**\r\n * @param {string} val\r\n * @returns {string}\r\n */\r\nexport const html2Text = (val: string) => {\r\n const div = document.createElement('div');\r\n div.innerHTML = val;\r\n return div.textContent || div.innerText;\r\n};\r\n\r\n/**\r\n * Merges two objects, giving the last one precedence\r\n * @param {Object} target\r\n * @param {(Object|Array)} source\r\n * @returns {Object}\r\n */\r\nexport const objectMerge = (target: any, source: any | any[]) => {\r\n if (typeof target !== 'object') {\r\n target = {};\r\n }\r\n if (Array.isArray(source)) {\r\n return source.slice();\r\n }\r\n Object.keys(source).forEach((property) => {\r\n const sourceProperty = source[property];\r\n if (typeof sourceProperty === 'object') {\r\n target[property] = objectMerge(target[property], sourceProperty);\r\n } else {\r\n target[property] = sourceProperty;\r\n }\r\n });\r\n return target;\r\n};\r\n\r\n/**\r\n * @param {HTMLElement} element\r\n * @param {string} className\r\n */\r\nexport const toggleClass = (element: HTMLElement, className: string) => {\r\n if (!element || !className) {\r\n return;\r\n }\r\n let classString = element.className;\r\n const nameIndex = classString.indexOf(className);\r\n if (nameIndex === -1) {\r\n classString += '' + className;\r\n } else {\r\n classString = classString.substring(0, nameIndex) + classString.substring(nameIndex + className.length);\r\n }\r\n element.className = classString;\r\n};\r\n\r\n/**\r\n * @param {string} type\r\n * @returns {Date}\r\n */\r\nexport const getTime = (type: string) => {\r\n if (type === 'start') {\r\n return new Date().getTime() - 3600 * 1000 * 24 * 90;\r\n } else {\r\n return new Date(new Date().toDateString());\r\n }\r\n};\r\n\r\n/**\r\n * @param {Function} func\r\n * @param {number} wait\r\n * @param {boolean} immediate\r\n * @return {*}\r\n */\r\nexport const debounce = (func: any, wait: number, immediate: boolean) => {\r\n let timeout: any, args: any, context: any, timestamp: any, result: any;\r\n\r\n const later = function () {\r\n // 据上一次触发时间间隔\r\n const last = +new Date() - timestamp;\r\n\r\n // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait\r\n if (last < wait && last > 0) {\r\n timeout = setTimeout(later, wait - last);\r\n } else {\r\n timeout = null;\r\n // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用\r\n if (!immediate) {\r\n result = func.apply(context, args);\r\n if (!timeout) context = args = null;\r\n }\r\n }\r\n };\r\n\r\n return (...args: any) => {\r\n context = this;\r\n timestamp = +new Date();\r\n const callNow = immediate && !timeout;\r\n // 如果延时不存在,重新设定延时\r\n if (!timeout) timeout = setTimeout(later, wait);\r\n if (callNow) {\r\n result = func.apply(context, args);\r\n context = args = null;\r\n }\r\n return result;\r\n };\r\n};\r\n\r\n/**\r\n * This is just a simple version of deep copy\r\n * Has a lot of edge cases bug\r\n * If you want to use a perfect deep copy, use lodash's _.cloneDeep\r\n * @param {Object} source\r\n * @returns {Object}\r\n */\r\nexport const deepClone = (source: any) => {\r\n if (!source && typeof source !== 'object') {\r\n throw new Error('error arguments', 'deepClone' as any);\r\n }\r\n const targetObj: any = source.constructor === Array ? [] : {};\r\n Object.keys(source).forEach((keys) => {\r\n if (source[keys] && typeof source[keys] === 'object') {\r\n targetObj[keys] = deepClone(source[keys]);\r\n } else {\r\n targetObj[keys] = source[keys];\r\n }\r\n });\r\n return targetObj;\r\n};\r\n\r\n/**\r\n * @param {Array} arr\r\n * @returns {Array}\r\n */\r\nexport const uniqueArr = (arr: any) => {\r\n return Array.from(new Set(arr));\r\n};\r\n\r\n/**\r\n * @returns {string}\r\n */\r\nexport const createUniqueString = (): string => {\r\n const timestamp = +new Date() + '';\r\n const num = (1 + Math.random()) * 65536;\r\n const randomNum = parseInt(num + '');\r\n return (+(randomNum + timestamp)).toString(32);\r\n};\r\n\r\n/**\r\n * Check if an element has a class\r\n * @param ele\r\n * @param {string} cls\r\n * @returns {boolean}\r\n */\r\nexport const hasClass = (ele: HTMLElement, cls: string): boolean => {\r\n return !!ele.className.match(new RegExp('(\\\\s|^)' + cls + '(\\\\s|$)'));\r\n};\r\n\r\n/**\r\n * Add class to element\r\n * @param ele\r\n * @param {string} cls\r\n */\r\nexport const addClass = (ele: HTMLElement, cls: string) => {\r\n if (!hasClass(ele, cls)) ele.className += ' ' + cls;\r\n};\r\n\r\n/**\r\n * Remove class from element\r\n * @param ele\r\n * @param {string} cls\r\n */\r\nexport const removeClass = (ele: HTMLElement, cls: string) => {\r\n if (hasClass(ele, cls)) {\r\n const reg = new RegExp('(\\\\s|^)' + cls + '(\\\\s|$)');\r\n ele.className = ele.className.replace(reg, ' ');\r\n }\r\n};\r\n\r\n/**\r\n * @param {string} path\r\n * @returns {Boolean}\r\n */\r\nexport const isExternal = (path: string) => {\r\n return /^(https?:|http?:|mailto:|tel:)/.test(path);\r\n};\r\n\r\nexport const guid = () => {\r\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {\r\n const r = (Math.random() * 16) | 0,\r\n v = c == 'x' ? r : (r & 0x3) | 0x8;\r\n return v.toString(16);\r\n });\r\n};\r\n\r\n//封装一个方法\r\nexport const getMonthAndDay = (dateStr: string): string => {\r\n // 方案1:优先用 Date 对象解析(兼容 ISO 标准格式)\r\n const date = new Date(dateStr);\r\n if (!isNaN(date.getTime())) {\r\n // 验证解析是否成功\r\n // 获取月份(Date.getMonth() 返回 0-11,需+1)\r\n const month = date.getMonth() + 1;\r\n // 获取日期\r\n const day = date.getDate();\r\n\r\n // 核心改动:小于10补零,使用padStart(2, '0')确保长度为2,不足补0\r\n const monthStr = month.toString().padStart(2, '0'); // 1→\"01\",12→\"12\"\r\n const dayStr = day.toString().padStart(2, '0'); // 2→\"02\",31→\"31\"\r\n\r\n return `${monthStr}-${dayStr}`;\r\n }\r\n return '';\r\n};\r\n","/**\n * 表单数据处理 Composable\n */\n\nimport { ref, getCurrentInstance, type ComponentInternalInstance } from 'vue';\nimport { deepClone } from '@/utils/index';\nimport type { ColumnConfig, DataSourceConfig, FormFieldType } from '../types';\n\nexport function useFormData(dataSource: DataSourceConfig, editData: any) {\n const { proxy } = getCurrentInstance() as ComponentInternalInstance;\n const formData = ref<any>({});\n\n /**\n * 根据列的类型获取表单组件默认值\n */\n const getFormComponentDefaultValue = async (column: ColumnConfig): Promise<any> => {\n const form = column.form;\n if (!form) return '';\n\n switch (form.type) {\n case 'checkboxGroup':\n case 'checkboxDict':\n return [];\n\n case 'treeSelectApi':\n if (form.api) {\n const res = await form.api({ pageNum: 1, pageSize: 1000, ...form.apiParams });\n form.data = res.rows || res.data;\n // 仅在未显式配置 defaultExpandedKeys 且允许自动展开时,默认展开第一级\n if (form.data && Array.isArray(form.data) && !Array.isArray(form.defaultExpandedKeys) && form.autoExpandFirstLevel !== false) {\n const props = form.props || { value: 'id', label: 'label', children: 'children' };\n const valueKey = form.valueKey || props.value || 'id';\n form.defaultExpandedKeys = form.data.map((item: any) => item[valueKey]);\n }\n }\n return '';\n case 'radioApi':\n case 'selectApi':\n if (form.api) {\n const res = await form.api({ pageNum: 1, pageSize: 1000, ...form.apiParams });\n form.data = res.rows || res.data;\n }\n return '';\n\n case 'checkboxCommonDict':\n form.data = await proxy?.useOneCommonDict(form.dictType);\n return [];\n\n case 'radioCommonDict':\n case 'selectCommonDict':\n form.data = await proxy?.useOneCommonDict(form.dictType);\n return '';\n\n case 'radioDict':\n case 'selectDict':\n form.data = await proxy?.useOneDict(form.dictType);\n return '';\n\n case 'daterange':\n case 'timerange':\n return [];\n\n default:\n return '';\n }\n };\n\n /**\n * 初始化表单数据\n */\n const initFormData = async (currentEditData?: any): Promise<void> => {\n let formDataTemp: any = {};\n const columns = dataSource.columns;\n\n // 使用傳入的 currentEditData 或原始的 editData\n const dataToUse = currentEditData !== undefined ? currentEditData : editData;\n\n // 初始化所有字段的默认值\n for (const column of columns) {\n const formProp = column.form?.prop || column.prop;\n formDataTemp[formProp] = await getFormComponentDefaultValue(column);\n }\n\n // 合併編輯數據到表單數據中\n if (dataToUse && typeof dataToUse === 'object') {\n const clonedEditData = deepClone(dataToUse);\n formDataTemp = { ...formDataTemp, ...clonedEditData };\n }\n\n // 执行自定义初始化\n if (dataSource.initEditData) {\n dataSource.initEditData(formDataTemp);\n }\n if (dataSource.beforeInitEdit) {\n dataSource.beforeInitEdit(formDataTemp);\n }\n\n // 初始化更新数据\n initUpdateFormData(formDataTemp);\n\n formData.value = formDataTemp;\n };\n\n /**\n * 初始化更新数据(处理特殊字段类型)\n */\n const initUpdateFormData = (data: any): void => {\n const columns = dataSource.columns;\n\n columns.forEach((column) => {\n const formType = column.form?.type;\n const prop = column.form?.prop || column.prop;\n\n switch (formType) {\n case 'checkboxCommonDict':\n if (Array.isArray(data[prop])) {\n data[prop] = data[prop].map((item: any) => item[column.form!.value!] || item);\n }\n break;\n\n case 'selectDict':\n case 'radioDict':\n case 'switch':\n data[prop] = data[prop] || column.form?.defaultValue;\n break;\n\n case 'daterange':\n case 'timerange':\n // 支持从 beginXxx/endXxx 或 \"start,end\" 字符串初始化\n const rangeBeginProp = (column.form as any)?.rangeBeginProp;\n const rangeEndProp = (column.form as any)?.rangeEndProp;\n if (rangeBeginProp && rangeEndProp && data[rangeBeginProp] && data[rangeEndProp]) {\n data[prop] = [data[rangeBeginProp], data[rangeEndProp]];\n } else if (typeof data[prop] === 'string' && data[prop].includes(',')) {\n data[prop] = data[prop].split(',');\n } else if (!data[prop] || !Array.isArray(data[prop])) {\n data[prop] = data[prop] || [];\n }\n break;\n }\n });\n };\n\n /**\n * 准备保存数据\n */\n const prepareSaveData = async (saveParams: any): Promise<any> => {\n const mergedParams = { ...formData.value, ...saveParams };\n\n // 展开 daterange/timerange 为 beginXxx/endXxx\n dataSource.columns.forEach((column) => {\n const formType = column.form?.type;\n const prop = column.form?.prop || column.prop;\n if ((formType === 'daterange' || formType === 'timerange') && Array.isArray(mergedParams[prop])) {\n const arr = mergedParams[prop];\n const rangeBeginProp = (column.form as any)?.rangeBeginProp;\n const rangeEndProp = (column.form as any)?.rangeEndProp;\n if (rangeBeginProp && rangeEndProp && arr.length >= 2) {\n mergedParams[rangeBeginProp] = arr[0];\n mergedParams[rangeEndProp] = arr[1];\n }\n }\n });\n\n // 删除排序字段\n delete mergedParams.orderByColumn;\n delete mergedParams.isAsc;\n\n // 执行自定义保存前处理\n if (dataSource.beforeSave) {\n const result = await dataSource.beforeSave(mergedParams);\n if (result === false) return false;\n if (result && typeof result === 'object') Object.assign(saveParams, result);\n else Object.assign(saveParams, mergedParams);\n } else {\n Object.assign(saveParams, mergedParams);\n }\n return true;\n };\n\n return {\n formData,\n initFormData,\n prepareSaveData\n };\n}\n","<template>\n <el-dialog\n :title=\"props.action.title || props.action.text\"\n v-model=\"dialogVisible\"\n @close=\"handleClose\"\n destroy-on-close\n append-to-bod\n :width=\"props.action.width || (formInline ? 750 : 550)\"\n class=\"curdEditDialog\"\n >\n <el-form\n :model=\"formData\"\n ref=\"curdFormRef\"\n :label-width=\"props.action.labelWidth || props.dataSource.formLabelWidth || 'auto'\"\n :label-position=\"props.action.labelPosition || 'right'\"\n class=\"p-l-3 p-r-3\"\n >\n <el-row :gutter=\"20\">\n <el-col :span=\"(item.form.inline === false) ? 24 : formInline ? 12 : 24\" v-for=\"item in formObjs\" :key=\"item.prop\" class=\"el-p\">\n <el-form-item\n :prop=\"item.form.prop || item.prop\"\n :label=\"$t(item.label)\"\n :name=\"item.form.prop || item.prop\"\n :rules=\"item.form.rules\"\n :required=\"item.form.required\"\n >\n <el-input-number\n v-if=\"item.form.type === 'number'\"\n v-model=\"formData[item.form.prop || item.prop]\"\n style=\"width: 100%\"\n :min=\"item.form.min\"\n :max=\"item.form.max\"\n :placeholder=\"$t('請輸入') + $t(item.label)\"\n />\n <el-select\n v-else-if=\"item.form.type === 'select'\"\n :multiple=\"item.form.multiple\"\n style=\"width: 100%\"\n v-model=\"formData[item.form.prop || item.prop]\"\n :placeholder=\"$t('請選擇') + $t(item.label)\"\n :filterable=\"item.form.filterable ?? true\"\n >\n <el-option\n v-for=\"option in item.form.data\"\n :key=\"option[item.form.value] || option.value\"\n :label=\"option[item.form.label] || option.label\"\n :value=\"option[item.form.value] || option.value\"\n ></el-option>\n </el-select>\n <el-select\n v-else-if=\"item.form.type === 'selectDict'\"\n :multiple=\"item.form.multiple\"\n style=\"width: 100%\"\n v-model=\"formData[item.form.prop || item.prop]\"\n :placeholder=\"$t('請選擇') + $t(item.label)\"\n :filterable=\"item.form.filterable ?? true\"\n >\n <el-option\n v-for=\"option in item.form.data\"\n :key=\"option[item.form.value] || option.value\"\n :label=\"option[item.form.label] || option.label\"\n :value=\"option[item.form.value] || option.value\"\n ></el-option>\n </el-select>\n <el-select\n v-else-if=\"item.form.type === 'selectCommonDict'\"\n :multiple=\"item.form.multiple\"\n style=\"width: 100%\"\n v-model=\"formData[item.form.prop || item.prop]\"\n :placeholder=\"$t('請選擇') + $t(item.label)\"\n :filterable=\"item.form.filterable ?? true\"\n >\n <el-option v-for=\"option in item.form.data\" :key=\"option.value\" :label=\"option.label\" :value=\"option.value\"></el-option>\n </el-select>\n <el-select\n v-else-if=\"item.form.type === 'selectApi'\"\n :multiple=\"item.form.multiple\"\n style=\"width: 100%\"\n v-model=\"formData[item.form.prop || item.prop]\"\n :placeholder=\"$t('請選擇') + $t(item.label)\"\n :filterable=\"item.form.filterable ?? true\"\n >\n <el-option\n v-for=\"option in item.form.data\"\n :key=\"option[item.form.value]\"\n :label=\"option[item.form.label]\"\n :value=\"option[item.form.value]\"\n ></el-option>\n </el-select>\n <el-date-picker\n v-else-if=\"item.form.type === 'date'\"\n format=\"YYYY-MM-DD\"\n value-format=\"YYYY-MM-DD\"\n v-model=\"formData[item.form.prop || item.prop]\"\n type=\"date\"\n :placeholder=\"$t('請選擇日期')\"\n style=\"width: 100%\"\n />\n <el-date-picker\n v-else-if=\"item.form.type === 'datetime'\"\n format=\"YYYY-MM-DD HH:mm:ss\"\n value-format=\"YYYY-MM-DD HH:mm:ss\"\n v-model=\"formData[item.form.prop || item.prop]\"\n type=\"datetime\"\n :placeholder=\"$t('請選擇日期時間')\"\n style=\"width: 100%;\"\n />\n <el-date-picker\n v-else-if=\"item.form.type === 'daterange'\"\n v-model=\"formData[item.form.prop || item.prop]\"\n type=\"daterange\"\n format=\"YYYY-MM-DD\"\n value-format=\"YYYY-MM-DD\"\n :range-separator=\"$t('至')\"\n :start-placeholder=\"$t('開始日期')\"\n :end-placeholder=\"$t('結束日期')\"\n style=\"width: 100%\"\n />\n <el-date-picker\n v-else-if=\"item.form.type === 'timerange'\"\n v-model=\"formData[item.form.prop || item.prop]\"\n type=\"datetimerange\"\n format=\"YYYY-MM-DD HH:mm\"\n value-format=\"YYYY-MM-DD HH:mm:ss\"\n :range-separator=\"$t('至')\"\n :start-placeholder=\"$t('開始時間')\"\n :end-placeholder=\"$t('結束時間')\"\n style=\"width: 100%\"\n />\n <image-upload v-else-if=\"item.form.type === 'image'\" :limit=\"item.form.limit || 1\" v-model=\"formData[item.form.prop || item.prop]\" />\n <file-upload\n v-else-if=\"item.form.type === 'file'\"\n :limit=\"item.form.limit || 1\"\n v-model=\"formData[item.form.prop || item.prop]\"\n :fileType=\"item.form.fileType\"\n :fileSize=\"item.form.fileSize\"\n />\n <el-input\n v-else-if=\"item.form.type === 'password'\"\n auto-complete=\"new-password\"\n v-model=\"formData[item.form.prop || item.prop]\"\n type=\"password\"\n :placeholder=\"$t('請輸入') + $t(item.label)\"\n />\n <el-checkbox-group v-else-if=\"item.form.type === 'checkboxGroup'\" class=\"checkbox-group\" v-model=\"formData[item.form.prop || item.prop]\">\n <el-checkbox\n v-for=\"checkbox in item.form.list\"\n :key=\"checkbox[item.form.value]\"\n :label=\"checkbox[item.form.label]\"\n :value=\"checkbox[item.form.value]\"\n ></el-checkbox>\n </el-checkbox-group>\n <el-checkbox-group\n v-else-if=\"item.form.type === 'checkboxCommonDict'\"\n class=\"checkbox-group\"\n v-model=\"formData[item.form.prop || item.prop]\"\n >\n <el-checkbox v-for=\"ckb in item.form.data\" :label=\"ckb.value\" :key=\"ckb.value\" :value=\"ckb.value\"\n >{{\n ckb.label }}\n </el-checkbox>\n </el-checkbox-group>\n <el-switch\n v-else-if=\"item.form.type === 'switch'\"\n v-model=\"formData[item.form.prop || item.prop]\"\n :active-value=\"item.form.activeValue || '0'\"\n :inactive-value=\"item.form.inactiveValue || '1'\"\n >\n </el-switch>\n <el-radio-group v-else-if=\"item.form.type === 'radioDict'\" v-model=\"formData[item.form.prop || item.prop]\">\n <el-radio v-for=\"radio in item.form.data\" :key=\"radio.value\" :value=\"radio.value\">\n {{ radio.label\n }}</el-radio\n >\n </el-radio-group>\n <el-tree-select\n v-else-if=\"item.form.type === 'treeSelectApi'\"\n v-model=\"formData[item.form.prop || item.prop]\"\n :data=\"item.form.data\"\n :props=\"item.form.props\"\n :value-key=\"item.form.valueKey\"\n :default-expanded-keys=\"getTreeDefaultExpandedKeys(item.form)\"\n :filterable=\"item.form.filterable ?? true\"\n :placeholder=\"$t('請選擇') + $t(item.label)\"\n check-strictly\n />\n <el-radio-group v-else-if=\"item.form.type === 'radioCommonDict'\" v-model=\"formData[item.form.prop || item.prop]\">\n <el-radio v-for=\"radio in item.form.data\" :key=\"radio.value\" :label=\"radio.value\">\n {{ radio.label\n }}</el-radio\n >\n </el-radio-group>\n <el-radio-group v-else-if=\"item.form.type === 'radioLocalDict'\" v-model=\"formData[item.form.prop || item.prop]\">\n <el-radio v-for=\"radio in localDicts[item.form.dictType]\" :key=\"radio.value\" :label=\"radio.value\">\n {{\n radio.label }}</el-radio\n >\n </el-radio-group>\n <el-checkbox\n v-else-if=\"item.form.type === 'checkbox'\"\n :label=\"$t(item.label)\"\n v-model=\"formData[item.form.prop || item.prop]\"\n ></el-checkbox>\n <div v-else-if=\"item.form.type === 'color'\">\n <el-color-picker v-model=\"formData[item.form.prop || item.prop]\"></el-color-picker>\n <span class=\"color-picker-tips\">{{ $t('需点击选择器中的确定按钮') }}</span>\n </div>\n <el-input-number\n v-else-if=\"item.form.type === 'inputNumber'\"\n :min=\"item.form.min || 0\"\n v-model=\"formData[item.form.prop || item.prop]\"\n :step=\"1\"\n />\n <Editor\n v-else-if=\"item.form.type === 'richtext'\"\n v-model=\"formData[item.form.prop || item.prop]\"\n :height=\"item.form.max || 320\"\n :min-height=\"item.form.min || 220\"\n />\n <template v-else>\n <translate-input\n v-if=\"item.form.translate === true\"\n ref=\"translateInputRef\"\n :key=\"formData[dataSource.rowKey]\"\n v-model=\"formData[item.form.prop || item.prop]\"\n :disabled=\"item.form.disabled || false\"\n :type=\"item.form.type\"\n :placeholder=\"$t('請輸入') + $t(item.label)\"\n :rows=\"item.form.rows || 3\"\n :show-word-limit=\"item.form.showWordLimit || false\"\n :maxlength=\"item.form.limit || -1\"\n ></translate-input>\n <el-input\n v-else\n v-model=\"formData[item.form.prop || item.prop]\"\n :disabled=\"item.form.disabled || false\"\n :type=\"item.form.type\"\n :placeholder=\"$t('請輸入') + $t(item.label)\"\n :rows=\"item.form.rows || 3\"\n :show-word-limit=\"item.form.showWordLimit || false\"\n :maxlength=\"item.form.limit || -1\"\n >\n <template #suffix v-if=\"item.form.suffix\">\n {{ item.form.suffix }}\n </template>\n <template #prefix v-if=\"item.form.prefix\">\n {{ item.form.prefix }}\n </template>\n </el-input>\n </template>\n </el-form-item>\n </el-col>\n </el-row>\n </el-form>\n\n <template #footer>\n <div>\n <el-button @click=\"handleClose(false)\">{{ $t('關閉') }}</el-button>\n <el-button type=\"primary\" @click=\"handleSave\">{{ $t('存儲') }}</el-button>\n </div>\n </template>\n </el-dialog>\n</template>\n\n<script setup lang=\"ts\">\nimport { addTranslationBatch } from \"@/api/app/translation\";\nimport TranslateInput from '@/components/TranslateInput/index.vue';\nimport Editor from '@/components/Editor/index.vue';\nimport { useFormData } from './composables/useFormData';\nimport { initializeFormRules, filterEditableColumns, sortColumns } from './utils';\nimport { LOCAL_DICTS } from './constants';\nimport type { DataSourceConfig } from './types';\nimport { useI18n } from 'vue-i18n';\nconst { t } = useI18n();\n\nconst { proxy } = getCurrentInstance() as ComponentInternalInstance;\n\nconst props = defineProps<{\n dataSource: DataSourceConfig;\n editData: any;\n action: any;\n modelValue: boolean;\n}>()\n\nconst dialogVisible = ref<boolean>(false)\n\nconst translateInputRef = ref<any>();\nconst emits = defineEmits(['close', 'actionEvent']);\n\nconst curdFormRef = ref<any>();\nconst localDicts = ref(LOCAL_DICTS);\n\n// 使用 composable 处理表单数据\nconst { formData, initFormData: initFormDataComposable, prepareSaveData } = useFormData(\n props.dataSource,\n props.editData\n);\n\n/**\n * 生成编辑界面的组件\n */\nconst formObjs = computed(() => {\n const keyId = props.editData[props.dataSource.rowKey];\n const hasKeyId = !!keyId;\n const filteredColumns = filterEditableColumns(props.dataSource.columns, hasKeyId, hasKeyId);\n return sortColumns(filteredColumns, 'form.order');\n});\n\nconst formInline = computed(() => props.dataSource.formInline || false);\n\nconst getTreeDefaultExpandedKeys = (form: any) => {\n if (Array.isArray(form.defaultExpandedKeys)) {\n return form.defaultExpandedKeys;\n }\n if (form.autoExpandFirstLevel === false) {\n return [];\n }\n const data = form.data || [];\n const propsConfig = form.props || { value: 'id' };\n const valueKey = form.valueKey || propsConfig.value || 'id';\n return Array.isArray(data) ? data.map((item: any) => item[valueKey]).filter((v: any) => v !== undefined) : [];\n};\n\n/**\n * 初始化校驗規則\n */\nconst initFormRules = () => initializeFormRules(props.dataSource.columns)\n\n/**\n * 表单提交\n */\nconst handleSave = () => {\n curdFormRef.value?.validate(async (valid: boolean) => {\n if (!valid) return false;\n try {\n // 使用副本,避免污染 dataSource.queryParams 導致查詢時帶上編輯參數\n const saveParams = { ...(props.dataSource.queryParams || {}) };\n const result = await prepareSaveData(saveParams);\n if (result === false) return false;\n // 执行添加或更新\n if (props.editData[props.dataSource.rowKey]) {\n await props.dataSource.updateApi?.(saveParams);\n } else {\n await props.dataSource.addApi?.(saveParams);\n }\n await saveTranslation();\n handleClose(true);\n proxy?.$modal.msgSuccess(t('數據存儲成功'));\n curdFormRef.value?.resetFields();\n } catch (error) {\n console.error('Save failed:', error);\n }\n return false;\n });\n};\n\n/**\n * 保存翻譯\n */\nconst saveTranslation = async () => {\n const translateInputRefs = translateInputRef.value\n if (translateInputRefs != null) {\n const list = [];\n for (let i = 0; i < translateInputRefs.length; i++) {\n const result = translateInputRefs[i].getTranslate();\n list.push(result);\n }\n await addTranslationBatch(list);\n }\n}\n\n/**\n * 关闭弹窗\n */\nconst handleClose = (bRefTable = false) => {\n curdFormRef.value?.resetFields();\n emits('close', bRefTable);\n};\n\nwatch(() => props.modelValue, (newVal) => {\n if (newVal) {\n initFormRules();\n initFormDataComposable(props.editData);\n }\n dialogVisible.value = newVal;\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.color-picker-tips {\n margin-left: 10px;\n color: #666;\n}\n\n:deep(.el-p .el-input__wrapper) {\n width: 100% !important;\n}\n</style>\n<style lang=\"scss\">\n.curdEditDialog .el-dialog__footer {\n padding-top: 0px !important;\n padding-right: 12px;\n}\n</style>\n","<template>\r\n <el-card v-if=\"searchColumns.length > 0 && showCard\" class=\"curd-search-card\" shadow=\"hover\"\r\n style=\"margin-bottom: 12px !important;\"\r\n :body-style=\"{ paddingTop: '18px !important', paddingBottom: computedPaddingBottom }\">\r\n <el-form :model=\"searchForm\" :inline=\"true\" :label-width=\"props.dataSource.searchLabelWidth || 68\">\r\n <el-row :gutter=\"20\">\r\n <el-col :span=\"columnSpan\" v-for=\"item in searchColumns\" :key=\"item.search.prop\">\r\n <el-form-item :label=\"$t(item.label)\" :name=\"item.search.prop\" style=\"width: 100%;\"\r\n :label-width=\"item.search.labelWidth\">\r\n <el-date-picker v-if=\"item.search.type === 'daterange'\" v-model=\"searchForm[item.search.prop]\"\r\n type=\"daterange\" format=\"YYYY-MM-DD\" value-format=\"YYYY-MM-DD\" :range-separator=\"$t('至')\"\r\n :start-placeholder=\"$t('開始日期')\" :end-placeholder=\"$t('結束日期')\" style=\"width: 100%;\" clearable\r\n @change=\"handleQuery\">\r\n </el-date-picker>\r\n <el-date-picker v-else-if=\"item.search.type === 'timerange'\" v-model=\"searchForm[item.search.prop]\"\r\n type=\"datetimerange\" format=\"YYYY-MM-DD HH:mm\" value-format=\"YYYY-MM-DD HH:mm:ss\"\r\n :range-separator=\"$t('至')\" :start-placeholder=\"$t('開始時間')\" :end-placeholder=\"$t('結束時間')\"\r\n style=\"width: 100%;\" clearable @change=\"handleQuery\">\r\n </el-date-picker>\r\n <el-select v-else-if=\"item.search.type === 'selectApi'\" v-model=\"searchForm[item.search.prop]\"\r\n :placeholder=\"$t('請選擇') + $t(item.label) + $t('查詢')\" style=\"width: 100%;\" clearable @change=\"handleQuery\"\r\n :filterable=\"item.search.filterable ?? true\">\r\n <el-option v-for=\"option in item.search.data\" :key=\"option[item.search.value]\"\r\n :label=\"option[item.search.label]\" :value=\"option[item.search.value]\"></el-option>\r\n </el-select>\r\n <el-select v-else-if=\"item.search.type === 'selectData'\" v-model=\"searchForm[item.search.prop]\"\r\n :placeholder=\"$t('請選擇') + $t(item.label) + $t('查詢')\" style=\"width: 100%;\" clearable @change=\"handleQuery\"\r\n :filterable=\"item.search.filterable ?? true\">\r\n <el-option v-for=\"option in item.search.data\" :key=\"option[item.search.value]\"\r\n :label=\"option[item.search.label]\" :value=\"option[item.search.value]\"></el-option>\r\n </el-select>\r\n\r\n <el-select v-else-if=\"item.search.type === 'selectDict'\" v-model=\"searchForm[item.search.prop]\"\r\n :placeholder=\"$t('請選擇') + $t(item.label) + $t('查詢')\" style=\"width: 100%;\" clearable @change=\"handleQuery\"\r\n :filterable=\"item.search.filterable ?? true\">\r\n <el-option v-for=\"option in item.search.data || []\" :key=\"option[item.search.value] || option.value\"\r\n :label=\"option[item.search.label] || option.label\" :value=\"option[item.search.value] || option.value\" />\r\n </el-select>\r\n <el-select v-else-if=\"item.search.type === 'selectCommonDict'\" v-model=\"searchForm[item.search.prop]\"\r\n :placeholder=\"$t('請選擇') + $t(item.label) + $t('查詢')\" style=\"width: 100%;\" clearable @change=\"handleQuery\"\r\n :filterable=\"item.search.filterable ?? true\">\r\n <el-option v-for=\"option in item.search.data || []\" :key=\"option[item.search.value] || option.value\"\r\n :label=\"option[item.search.label] || option.label\" :value=\"option[item.search.value] || option.value\" />\r\n </el-select>\r\n <el-tree-select v-else-if=\"item.search.type === 'treeSelectApi'\" v-model=\"searchForm[item.search.prop]\"\r\n :data=\"item.search.data\" :props=\"item.search.props\" :value-key=\"item.search.valueKey\"\r\n :default-expanded-keys=\"item.search.defaultExpandedKeys\"\r\n :placeholder=\"$t('請選擇') + $t(item.label) + $t('查詢')\" style=\"width: 100%;\" check-strictly clearable\r\n @change=\"handleQuery\" />\r\n <el-input v-else-if=\"item.search.type === 'number'\" type=\"number\" v-model=\"searchForm[item.search.prop]\"\r\n :placeholder=\"$t('請輸入') + $t(item.label) + $t('查詢')\" style=\"width: 100%;\" clearable />\r\n <el-input v-else type=\"text\" v-model=\"searchForm[item.search.prop]\" style=\"width: 100%;\"\r\n :placeholder=\"$t('請輸入') + $t(item.label) + $t('查詢')\" @keyup.enter=\"handleQuery\" clearable />\r\n </el-form-item>\r\n </el-col>\r\n <el-col :span=\"actionColSpan\">\r\n <el-button @click=\"handleQuery\" plain>{{ $t('查詢') }}</el-button>\r\n <el-button @click=\"handleClear\" plain>{{ $t('清空') }}</el-button>\r\n <el-button v-if=\"createAction\" v-hasPermi=\"createAction.permission || null\" :type=\"createAction.cssType\"\r\n :icon=\"createAction.icon\" plain @click=\"emitToolbarAction(createAction)\">{{ $t(createAction.text || '新增')\r\n }}</el-button>\r\n <el-button v-if=\"props.dataSource.showBatchDeleteButton\" type=\"danger\" icon=\"Delete\" plain\r\n :disabled=\"selectedCount === 0\" @click=\"emits('actionEvent', { action: 'batchDelete' })\"\r\n v-hasPermi=\"batchDeleteAction?.permission || null\">{{ $t('批量刪除') }}</el-button>\r\n <el-button v-if=\"props.dataSource.exportUrl\" type=\"primary\" icon=\"Download\" plain @click=\"handleExport\">{{\r\n props.dataSource.exportButtonName || $t('導出') }}</el-button>\r\n <el-button v-for=\"item in toolbarsWithoutCreate\" :key=\"item.text\" @click=\"emitToolbarAction(item)\"\r\n v-hasPermi=\"item.permission || null\" :type=\"item.cssType\" :icon=\"item.icon\" plain>{{ $t(item.text)\r\n }}</el-button>\r\n </el-col>\r\n </el-row>\r\n </el-form>\r\n </el-card>\r\n <div v-else-if=\"searchColumns.length > 0 && !showCard\" class=\"curd-search-card\"\r\n style=\"margin-bottom: 0px !important;\">\r\n <el-form :model=\"searchForm\" :inline=\"true\" :label-width=\"props.dataSource.searchLabelWidth || 68\">\r\n <el-row :gutter=\"20\">\r\n <el-col :span=\"columnSpan\" v-for=\"item in searchColumns\" :key=\"item.search.prop\">\r\n <el-form-item :label=\"item.label\" :name=\"item.search.prop\" style=\"width: 100%;\"\r\n :label-width=\"item.search.labelWidth\">\r\n <el-date-picker v-if=\"item.search.type === 'daterange'\" v-model=\"searchForm[item.search.prop]\"\r\n type=\"daterange\" format=\"YYYY-MM-DD\" value-format=\"YYYY-MM-DD\" :range-separator=\"$t('至')\"\r\n :start-placeholder=\"$t('開始日期')\" :end-placeholder=\"$t('結束日期')\" style=\"width: 100%;\" clearable\r\n @change=\"handleQuery\">\r\n </el-date-picker>\r\n <el-date-picker v-else-if=\"item.search.type === 'timerange'\" v-model=\"searchForm[item.search.prop]\"\r\n type=\"datetimerange\" format=\"YYYY-MM-DD HH:mm\" value-format=\"YYYY-MM-DD HH:mm:ss\"\r\n :range-separator=\"$t('至')\" :start-placeholder=\"$t('開始時間')\" :end-placeholder=\"$t('結束時間')\"\r\n style=\"width: 100%;\" clearable @change=\"handleQuery\">\r\n </el-date-picker>\r\n <el-select v-else-if=\"item.search.type === 'selectApi'\" v-model=\"searchForm[item.search.prop]\"\r\n :placeholder=\"$t('請選擇' + item.label + '查詢')\" style=\"width: 100%;\" clearable @change=\"handleQuery\"\r\n :filterable=\"item.search.filterable ?? true\">\r\n <el-option v-for=\"option in item.search.data\" :key=\"option[item.search.value]\"\r\n :label=\"option[item.search.label]\" :value=\"option[item.search.value]\"></el-option>\r\n </el-select>\r\n <el-select v-else-if=\"item.search.type === 'selectData'\" v-model=\"searchForm[item.search.prop]\"\r\n :placeholder=\"$t('請選擇' + item.label + '查詢')\" style=\"width: 100%;\" clearable @change=\"handleQuery\"\r\n :filterable=\"item.search.filterable ?? true\">\r\n <el-option v-for=\"option in item.search.data\" :key=\"option[item.search.value]\"\r\n :label=\"option[item.search.label]\" :value=\"option[item.search.value]\"></el-option>\r\n </el-select>\r\n\r\n <el-select v-else-if=\"item.search.type === 'selectDict'\" v-model=\"searchForm[item.search.prop]\"\r\n :placeholder=\"$t('請選擇' + item.label + '查詢')\" style=\"width: 100%;\" clearable @change=\"handleQuery\"\r\n :filterable=\"item.search.filterable ?? true\">\r\n <el-option v-for=\"option in item.search.data || []\" :key=\"option[item.search.value] || option.value\"\r\n :label=\"option[item.search.label] || option.label\" :value=\"option[item.search.value] || option.value\" />\r\n </el-select>\r\n <el-select v-else-if=\"item.search.type === 'selectCommonDict'\" v-model=\"searchForm[item.search.prop]\"\r\n :placeholder=\"$t('請選擇' + item.label + '查詢')\" style=\"width: 100%;\" clearable @change=\"handleQuery\"\r\n :filterable=\"item.search.filterable ?? true\">\r\n <el-option v-for=\"option in item.search.data || []\" :key=\"option[item.search.value] || option.value\"\r\n :label=\"option[item.search.label] || option.label\" :value=\"option[item.search.value] || option.value\" />\r\n </el-select>\r\n <el-tree-select v-else-if=\"item.search.type === 'treeSelectApi'\" v-model=\"searchForm[item.search.prop]\"\r\n :data=\"item.search.data\" :props=\"item.search.props\" :value-key=\"item.search.valueKey\"\r\n :default-expanded-keys=\"item.search.defaultExpandedKeys\" :placeholder=\"$t('請選擇' + item.label + '查詢')\"\r\n style=\"width: 100%;\" check-strictly clearable @change=\"handleQuery\" />\r\n <el-input v-else-if=\"item.search.type === 'number'\" type=\"number\" v-model=\"searchForm[item.search.prop]\"\r\n :placeholder=\"$t('請輸入' + item.label + '查詢')\" style=\"width: 100%;\" clearable />\r\n <el-input v-else type=\"text\" v-model=\"searchForm[item.search.prop]\" style=\"width: 100%;\"\r\n :placeholder=\"$t('請輸入' + item.label + '查詢')\" @keyup.enter=\"handleQuery\" clearable />\r\n </el-form-item>\r\n </el-col>\r\n <el-col :span=\"actionColSpan\">\r\n <el-button @click=\"handleQuery\" plain>{{ $t('查詢') }}</el-button>\r\n <el-button @click=\"handleClear\" plain>{{ $t('清空') }}</el-button>\r\n <el-button v-if=\"createAction\" v-hasPermi=\"createAction.permission || null\" :type=\"createAction.cssType\"\r\n :icon=\"createAction.icon\" plain @click=\"emitToolbarAction(createAction)\">{{ $t(createAction.text || '新增')\r\n }}</el-button>\r\n <el-button v-if=\"props.dataSource.showBatchDeleteButton\" type=\"danger\" icon=\"Delete\" plain\r\n :disabled=\"selectedCount === 0\" @click=\"emits('actionEvent', { action: 'batchDelete' })\"\r\n v-hasPermi=\"batchDeleteAction?.permission || null\">{{ $t('批量刪除') }}</el-button>\r\n <el-button v-if=\"props.dataSource.exportUrl\" type=\"primary\" icon=\"Download\" plain @click=\"handleExport\">{{\r\n $t('導出') }}</el-button>\r\n <el-button v-for=\"item in toolbarsWithoutCreate\" :key=\"item.text\" @click=\"emitToolbarAction(item)\"\r\n v-hasPermi=\"item.permission || null\" :type=\"item.cssType\" :icon=\"item.icon\" plain>{{ $t(item.text)\r\n }}</el-button>\r\n </el-col>\r\n </el-row>\r\n </el-form>\r\n </div>\r\n <div v-else-if=\"toolbars.length > 0\" class=\"curd-search-card\">\r\n <el-button v-if=\"createAction\" v-hasPermi=\"createAction.permission || null\" :type=\"createAction.cssType\"\r\n :icon=\"createAction.icon\" plain @click=\"emitToolbarAction(createAction)\">{{ createAction.text || '新增'\r\n }}</el-button>\r\n <el-button v-if=\"props.dataSource.showBatchDeleteButton\" type=\"danger\" icon=\"Delete\" plain\r\n :disabled=\"selectedCount === 0\" @click=\"emits('actionEvent', { action: 'batchDelete' })\"\r\n v-hasPermi=\"batchDeleteAction?.permission || null\">批量刪除</el-button>\r\n <el-button v-if=\"props.dataSource.exportUrl\" type=\"primary\" icon=\"Download\" plain @click=\"handleExport\">{{\r\n props.dataSource.exportButtonName || '導出' }}</el-button>\r\n <el-button v-for=\"item in toolbarsWithoutCreate\" :key=\"item.text\" @click=\"emitToolbarAction(item)\"\r\n v-hasPermi=\"item.permission || null\" :type=\"item.cssType\" :icon=\"item.icon\" plain>{{ $t(item.text)\r\n }}</el-button>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { useDictData } from './composables/useDictData';\r\nimport { sortColumns, deleteEmptyParams, formatDateRangeParams } from './utils';\r\nimport type { DataSourceConfig, ColumnConfig } from './types';\r\nimport dayjs from \"dayjs\"\r\nimport { useI18n } from 'vue-i18n';\r\nconst { t } = useI18n();\r\n\r\n\r\nconst { proxy } = getCurrentInstance() as ComponentInternalInstance;\r\nconst emits = defineEmits(['actionEvent']);\r\n\r\nconst props = withDefaults(defineProps<{\r\n dataSource: DataSourceConfig;\r\n /** 表格勾選數量,為 0 時批量刪除按鈕禁用 */\r\n selectedCount?: number;\r\n}>(), {\r\n selectedCount: 0\r\n});\r\n\r\n/** 頂部操作按鈕(來自 curdToolbar:position===top、create、back) */\r\nconst toolbars = computed(() => {\r\n const actions = props.dataSource.actions;\r\n if (actions == null) return [];\r\n const list = actions.filter((item: any) => item.position === 'top' || item.action === 'create' || item.action === 'back');\r\n return list.map((item: any) => {\r\n const t = { ...item };\r\n if (t.action === 'create') {\r\n t.cssType = 'primary';\r\n t.icon = 'Plus';\r\n } else if (t.action === 'back') {\r\n t.icon = 'ArrowLeft';\r\n }\r\n return t;\r\n });\r\n});\r\n\r\n/** 新增按鈕(單獨放批量刪除前) */\r\nconst createAction = computed(() => toolbars.value.find((item: any) => item.action === 'create') ?? null);\r\n///批量刪除按鈕\r\nconst batchDeleteAction = computed(() => props.dataSource.actions?.find((item: any) => item.action === 'delete') ?? null);\r\n/** 除新增外的其他頂部按鈕(放導出後面) */\r\nconst toolbarsWithoutCreate = computed(() => toolbars.value.filter((item: any) => item.action !== 'create'));\r\n\r\nconst emitToolbarAction = (item: any) => {\r\n emits('actionEvent', item);\r\n};\r\n\r\n/** 每行顯示的列數,默認 4 */\r\nconst columnsPerRow = computed(() => props.dataSource.searchColumnsPerRow ?? 4);\r\n\r\n/** 每列所佔的 span */\r\nconst columnSpan = computed(() => Math.floor(24 / columnsPerRow.value));\r\n\r\n/** 是否顯示卡片容器 */\r\nconst showCard = computed(() => props.dataSource.showSearchCard ?? true);\r\n\r\n/** 操作按鈕列所佔 span:優先使用配置值,否則根據最後一行的剩餘空間自動計算 */\r\nconst actionColSpan = computed(() => {\r\n // 如果配置了固定值,優先使用\r\n if (props.dataSource.searchActionColSpan != null) {\r\n return props.dataSource.searchActionColSpan;\r\n }\r\n // 否則自動計算\r\n const remainder = searchColumns.value.length % columnsPerRow.value;\r\n const lastRowColumns = remainder === 0 ? columnsPerRow.value : remainder;\r\n const rest = 24 - lastRowColumns * columnSpan.value;\r\n return rest <= 0 ? 24 : rest;\r\n});\r\n\r\n/** 計算底部內邊距:當最後一行不滿時為 0,否則為 15px */\r\nconst computedPaddingBottom = computed(() => {\r\n const remainder = searchColumns.value.length % columnsPerRow.value;\r\n return remainder === 0 ? '15px !important' : '0px !important';\r\n});\r\n\r\nconst { initSearchDicts } = useDictData();\r\n\r\nconst searchForm = ref<any>({});\r\nconst searchColumns = ref<any>([]);\r\n/**\r\n * 執行查詢\r\n */\r\nconst handleQuery = () => {\r\n emits(\"actionEvent\", { action: 'filter' })\r\n}\r\n\r\nconst handleExport = async () => {\r\n try {\r\n let queryParams = getQueryParams();\r\n queryParams = { ...(props.dataSource.queryParams || {}), ...queryParams };\r\n if (props.dataSource.beforeExport) {\r\n const result = await props.dataSource.beforeExport(queryParams);\r\n if (result === false) return;\r\n }\r\n const dateStr = dayjs().format(\"YYYYMMDDHHmmss\");\r\n let fileName = props.dataSource.exportFileName ? `${props.dataSource.exportFileName}` : `export`;\r\n fileName += `_${dateStr}.xlsx`;\r\n proxy?.download(props.dataSource.exportUrl, queryParams, fileName);\r\n } catch (error) {\r\n console.error('Export failed:', error);\r\n proxy?.$modal.msgError(t('導出失敗'));\r\n }\r\n}\r\n\r\n/**\r\n * 獲取查詢參數\r\n * @returns {Object} 查詢參數對象\r\n */\r\nconst getQueryParams = (): Record<string, any> => {\r\n // 直接使用 searchForm.value,避免创建多余的 params 对象\r\n const queryParams: Record<string, any> = { ...searchForm.value };\r\n\r\n // 处理日期/时间范围参数\r\n searchColumns.value.forEach((item: ColumnConfig) => {\r\n if (item.search?.type === 'daterange' || item.search?.type === 'timerange') {\r\n const dataTemp = queryParams[item.search.prop];\r\n if (dataTemp && dataTemp[0] && dataTemp[1]) {\r\n formatDateRangeParams(queryParams, dataTemp, item.search.prop, proxy);\r\n }\r\n delete queryParams[item.search.prop];\r\n }\r\n });\r\n return deleteEmptyParams(queryParams);\r\n}\r\n\r\n/**\r\n * 操作清空查詢條件\r\n */\r\nconst handleClear = async () => {\r\n await initData();\r\n emits(\"actionEvent\", { action: 'filter' }, {})\r\n}\r\n\r\n/**\r\n * 初始化数据\r\n */\r\nconst initData = async () => {\r\n // 先同步設置 searchColumns,讓搜索框立即顯示\r\n const searchColumnsTemp = props.dataSource.columns.filter((item: ColumnConfig) => item.search);\r\n searchColumns.value = sortColumns(\r\n searchColumnsTemp.map((col, i) => {\r\n if (!col.search.order) {\r\n col.search.order = i + 100;\r\n }\r\n return col;\r\n }),\r\n 'search.order'\r\n );\r\n\r\n // 異步初始化表單數據和字典(不阻塞搜索框顯示)\r\n await nextTick();\r\n const searchFormTemp: Record<string, any> = {};\r\n for (let i = 0; i < searchColumnsTemp.length; i++) {\r\n const column = searchColumnsTemp[i];\r\n if (!column.search) continue;\r\n\r\n const searchProp = column.search.prop || ''\r\n const searchType = column.search.type || ''\r\n\r\n // 根据类型初始化\r\n if (searchType === 'daterange' || searchType === 'timerange') {\r\n searchFormTemp[searchProp] = column.search.default ? column.search.default.split(',') : [];\r\n } else if (searchType === 'selectApi' || searchType === 'treeSelectApi' || searchType === 'selectDict') {\r\n await initSearchDicts(column);\r\n searchFormTemp[searchProp] = column.search.default;\r\n } else {\r\n searchFormTemp[searchProp] = column.search.default;\r\n }\r\n }\r\n searchForm.value = searchFormTemp;\r\n}\r\n\r\nonMounted(() => initData());\r\n\r\ndefineExpose({ initData, getQueryParams })\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n.curd-search-card {\r\n margin-bottom: 12px !important;\r\n\r\n /* 與表格內容字體、顏色保持一致 */\r\n :deep(.el-form-item__label) {\r\n font-weight: normal;\r\n font-size: 14px;\r\n color: var(--el-text-color-regular);\r\n }\r\n}\r\n\r\n.search {\r\n cursor: pointer;\r\n padding: 12px 14px 0px;\r\n border-radius: 4px;\r\n}\r\n</style>\r\n","<template>\r\n <div :class=\"{ 'p-3': showPadding }\">\r\n <curdSearch :dataSource=\"props.dataSource\" :selectedCount=\"selectedCount\" ref=\"curdSearchRef\" @actionEvent=\"actionEvent\"> </curdSearch>\r\n <el-card v-if=\"showCard\" :body-style=\"{ paddingBottom: props.dataSource.paging === false ? '20px' : '0px' }\" shadow=\"hover\">\r\n <curd-table\r\n ref=\"curdTableRef\"\r\n @actionEvent=\"actionEvent\"\r\n @selectionChange=\"onSelectionChange\"\r\n :dataSource=\"props.dataSource\"\r\n :maxHeight=\"legacyMaxHeight\"\r\n >\r\n <template v-for=\"name in columnSlotNames\" :key=\"name\" #[name]=\"scope\">\r\n <slot :name=\"name\" v-bind=\"scope\" />\r\n </template>\r\n </curd-table>\r\n <curd-edit @close=\"closeEdit\" v-model=\"editDialogVisible\" :dataSource=\"props.dataSource\" :action=\"action\" :editData=\"editData\"></curd-edit>\r\n </el-card>\r\n <div v-else>\r\n <curd-table\r\n ref=\"curdTableRef\"\r\n @actionEvent=\"actionEvent\"\r\n @selectionChange=\"onSelectionChange\"\r\n :dataSource=\"props.dataSource\"\r\n :maxHeight=\"legacyMaxHeight\"\r\n >\r\n <template v-for=\"name in columnSlotNames\" :key=\"name\" #[name]=\"scope\">\r\n <slot :name=\"name\" v-bind=\"scope\" />\r\n </template>\r\n </curd-table>\r\n <curd-edit @close=\"closeEdit\" v-model=\"editDialogVisible\" :dataSource=\"props.dataSource\" :action=\"action\" :editData=\"editData\"></curd-edit>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport curdTable from '@/components/CURD/curdTable.vue';\r\nimport curdEdit from '@/components/CURD/curdEdit.vue';\r\nimport curdSearch from '@/components/CURD/curdSearch.vue';\r\nimport { initializeColumns } from './utils';\r\nimport type { DataSourceConfig, ActionEvent } from './types';\r\n\r\nconst { proxy } = getCurrentInstance() as ComponentInternalInstance;\r\n\r\nconst emits = defineEmits<{\r\n actionEvent: [ev: ActionEvent, data: any];\r\n beforeQuery: [params: any];\r\n}>();\r\n\r\nconst props = withDefaults(defineProps<{\r\n dataSource: DataSourceConfig;\r\n showCard?: boolean;\r\n showToolbar?: boolean;\r\n showPadding?: boolean;\r\n /** @deprecated 請使用 dataSource.maxHeight */\r\n maxHeight?: number | string,\r\n}>(), {\r\n showCard: true,\r\n showToolbar: true,\r\n showPadding: true,\r\n});\r\n\r\n/** 僅在頁面仍傳 :maxHeight 時作為兜底,dataSource.maxHeight 優先 */\r\nconst legacyMaxHeight = computed(() =>\r\n props.dataSource.maxHeight != null ? undefined : props.maxHeight\r\n);\r\n\r\nconst editDialogVisible = ref(false);\r\nconst editData = ref<any>({});\r\nconst action = ref<any>({});\r\nconst curdTableRef = ref<any>();\r\nconst curdSearchRef = ref<any>();\r\n/** 表格勾選數量,為 0 時批量刪除按鈕禁用 */\r\nconst selectedCount = ref(0);\r\n\r\nconst columnSlotNames = computed(() => {\r\n const names = (props.dataSource.columns || []).map((col) => col.slot).filter((name): name is string => !!name);\r\n return Array.from(new Set(names));\r\n});\r\n\r\nconst onSelectionChange = (val: any[]) => {\r\n selectedCount.value = Array.isArray(val) ? val.length : 0;\r\n};\r\n\r\n\r\nconst closeEdit = (refresh = false) => {\r\n editDialogVisible.value = false;\r\n editData.value = {}\r\n if (refresh === true) refreshTable();\r\n}\r\n\r\n/**\r\n * 刷新表格數據\r\n */\r\nconst refreshTable = () => curdTableRef.value?.refreshTable()\r\n\r\n/**\r\n * 初始化表格\r\n */\r\nconst initTable = async () => await curdTableRef.value?.initData()\r\n\r\n/**\r\n * 刷新查詢\r\n */\r\nconst initSearch = () => curdSearchRef.value?.initData()\r\n\r\n/**\r\n * 操作事件\r\n */\r\nconst actionEvent = async (ev: ActionEvent, data: any) => {\r\n let processedData = data;\r\n if (props.dataSource.beforeAction) {\r\n processedData = await props.dataSource.beforeAction(ev, data);\r\n }\r\n execActionEvent(ev, processedData);\r\n}\r\n\r\n/**\r\n * 执行 action 事件\r\n */\r\nconst execActionEvent = (ev: ActionEvent, data: any) => {\r\n // 如果 action 是函数,直接执行\r\n if (typeof ev.action === 'function') {\r\n ev.action(data);\r\n return;\r\n }\r\n\r\n // 根据 action 类型执行不同操作\r\n const actionHandlers: Record<string, () => void> = {\r\n update: () => handleUpdate(ev, data),\r\n delete: () => handleDelete(ev, data),\r\n batchDelete: () => handleBatchDelete(),\r\n create: () => handleCreate(ev, data),\r\n view: () => handleView(ev, data),\r\n filter: () => handleFilter(),\r\n back: () => handleBack(),\r\n };\r\n\r\n const handler = actionHandlers[ev.action as string];\r\n if (handler) {\r\n handler();\r\n } else {\r\n emits('actionEvent', ev, data);\r\n }\r\n};\r\n\r\n/**\r\n * 处理更新\r\n */\r\nconst handleUpdate = async (ev: ActionEvent, data: any) => {\r\n if (props.dataSource.beforeUpdate) {\r\n const result = await props.dataSource.beforeUpdate(data);\r\n if (result === false) return;\r\n }\r\n editData.value = data;\r\n action.value = ev;\r\n editDialogVisible.value = true;\r\n};\r\n\r\n/**\r\n * 处理删除\r\n */\r\nconst handleDelete = async (ev: ActionEvent, data: any) => {\r\n try {\r\n if (props.dataSource.beforeDelete) {\r\n const result = await props.dataSource.beforeDelete(data);\r\n if (result === false) return;\r\n }\r\n const deleteId = data[props.dataSource.rowKey];\r\n if (!deleteId) {\r\n proxy?.$modal.msgError('rowKey参数不正确');\r\n return;\r\n }\r\n const confirmText = ev.confirm || ev.confirmText || '確認刪除此數據嗎?';\r\n await proxy?.$modal.confirm(confirmText);\r\n await props.dataSource.deleteApi?.(deleteId);\r\n proxy?.$modal.msgSuccess('刪除成功');\r\n curdTableRef.value?.refreshTable();\r\n } catch (error) {\r\n if (error !== 'cancel') {\r\n console.error('Delete failed:', error);\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * 处理批量删除(deleteApi 支持傳入 id 數組一次刪除)\r\n */\r\nconst handleBatchDelete = async () => {\r\n if (!props.dataSource.deleteApi) {\r\n proxy?.$modal.msgError('deleteApi 方法未設置');\r\n return;\r\n }\r\n const selection = curdTableRef.value?.getSelection?.() ?? [];\r\n if (!selection.length) {\r\n proxy?.$modal.msgWarning('請先勾選要刪除的數據');\r\n return;\r\n }\r\n await proxy?.$modal.confirm(`確認刪除選中的 ${selection.length} 條數據嗎?`);\r\n const deleteIds: any[] = [];\r\n for (const row of selection) {\r\n if (props.dataSource.beforeDelete) {\r\n const result = await props.dataSource.beforeDelete(row);\r\n if (result === false) continue;\r\n }\r\n const id = row[props.dataSource.rowKey];\r\n if (id != null) deleteIds.push(id);\r\n }\r\n if (deleteIds.length) {\r\n await props.dataSource.deleteApi?.(deleteIds);\r\n proxy?.$modal.msgSuccess('刪除成功');\r\n curdTableRef.value?.refreshTable();\r\n }\r\n};\r\n\r\n/**\r\n * 处理创建\r\n */\r\nconst handleCreate = async (ev: ActionEvent, data: any) => {\r\n if (props.dataSource.beforeCreate) {\r\n const result = await props.dataSource.beforeCreate(data || {});\r\n if (result === false) return;\r\n }\r\n editData.value = data || {};\r\n action.value = ev;\r\n editDialogVisible.value = true;\r\n};\r\n\r\n/**\r\n * 提供外部調用,打開與「新增」按鈕一致的新增彈窗\r\n * 支持傳入配置對象控制標題、寬度等 action 參數\r\n */\r\nconst openCreate = async (options: any = {}) => {\r\n // 兼容舊用法:openCreate(data)\r\n const isConfigObject = options && (\r\n Object.prototype.hasOwnProperty.call(options, 'data') ||\r\n Object.prototype.hasOwnProperty.call(options, 'title') ||\r\n Object.prototype.hasOwnProperty.call(options, 'text') ||\r\n Object.prototype.hasOwnProperty.call(options, 'width') ||\r\n Object.prototype.hasOwnProperty.call(options, 'labelWidth') ||\r\n Object.prototype.hasOwnProperty.call(options, 'labelPosition')\r\n );\r\n\r\n const data = isConfigObject ? (options.data || {}) : (options || {});\r\n const createAction: ActionEvent = {\r\n action: 'create',\r\n ...(isConfigObject ? {\r\n title: options.title,\r\n text: options.text,\r\n width: options.width,\r\n labelWidth: options.labelWidth,\r\n labelPosition: options.labelPosition,\r\n } : {})\r\n } as ActionEvent;\r\n\r\n await handleCreate(createAction, data);\r\n};\r\n\r\n/**\r\n * 处理查看\r\n */\r\nconst handleView = (ev: ActionEvent, data: any) => {\r\n let url = ev.url;\r\n const pm = ev.pm;\r\n if (pm && data[pm]) {\r\n const separator = url.includes('?') ? '&' : '?';\r\n url += `${separator}${pm}=${data[pm]}`;\r\n }\r\n proxy?.$router.push(url);\r\n};\r\n\r\n\r\n/**\r\n * 处理过滤\r\n */\r\nconst handleFilter = () => {\r\n const queryParams = curdSearchRef.value?.getQueryParams();\r\n curdTableRef.value?.refreshTable(queryParams || {});\r\n};\r\n\r\n/**\r\n * 獲取查詢參數\r\n */\r\nconst getQueryParams = () => {\r\n return curdSearchRef.value?.getQueryParams();\r\n}\r\n\r\n/**\r\n * 獲取已選中行\r\n */\r\nconst getSelection = () => curdTableRef.value?.getSelection()\r\n\r\n/**\r\n * 返回\r\n */\r\nconst handleBack = () => proxy?.$router.back();\r\n\r\nonMounted(() => initializeColumns(props.dataSource.columns));\r\n\r\ndefineExpose({ refreshTable, initSearch, initTable, openCreate, getQueryParams, getSelection })\r\n</script>\r\n\r\n<style scoped lang=\"scss\"></style>\r\n","/**\n * 可配置的权限指令(库内自包含版本)\n * 默认全部放行;消费者通过 setPermissionChecker 或 install 选项注入真实校验逻辑。\n */\nimport type { Directive, DirectiveBinding } from 'vue';\n\ntype PermChecker = (value: string | string[]) => boolean;\n\nlet _hasPermiChecker: PermChecker = () => true;\nlet _hasRolesChecker: PermChecker = () => true;\n\nexport const setPermissionChecker = (opts: { hasPermi?: PermChecker; hasRoles?: PermChecker }) => {\n if (opts.hasPermi) _hasPermiChecker = opts.hasPermi;\n if (opts.hasRoles) _hasRolesChecker = opts.hasRoles;\n};\n\nexport const hasPermi: Directive = {\n mounted(el: HTMLElement, binding: DirectiveBinding) {\n const { value } = binding;\n if (value == null) return;\n if (Array.isArray(value) && value.length > 0) {\n if (!_hasPermiChecker(value)) {\n el.parentNode?.removeChild(el);\n }\n }\n }\n};\n\nexport const hasRoles: Directive = {\n mounted(el: HTMLElement, binding: DirectiveBinding) {\n const { value } = binding;\n if (value == null) return;\n if (Array.isArray(value) && value.length > 0) {\n if (!_hasRolesChecker(value)) {\n el.parentNode?.removeChild(el);\n }\n }\n }\n};\n","export const errorCode: any = {\r\n '401': '认证失败,无法访问系统资源',\r\n '403': '当前操作没有权限',\r\n '404': '访问资源不存在',\r\n default: '系统未知错误,请反馈给管理员'\r\n};\r\nexport default errorCode;\r\n","/**\n * 通用下载方法(库内自包含版本)\n * 通过 getRequest() 获取 axios 实例,避免直接依赖业务侧的 request.ts\n */\nimport FileSaver from 'file-saver';\nimport { getRequest } from '@/utils/http';\nimport { blobValidate, tansParams } from '@/utils/ruoyi';\nimport { errorCode } from '@/utils/errorCode';\n\nexport function download(url: string, params: any, fileName: string) {\n const loadingInstance = ElLoading.service({\n text: '正在下載數據,請稍後',\n background: 'rgba(0, 0, 0, 0.7)'\n });\n return getRequest()\n .post(url, params, {\n transformRequest: [(p: any) => tansParams(p)],\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n responseType: 'blob'\n })\n .then(async (resp: any) => {\n const isBlob = blobValidate(resp);\n if (isBlob) {\n const blob = new Blob([resp]);\n FileSaver.saveAs(blob, fileName);\n } else {\n const resText = await new Blob([resp]).text();\n const rspObj = JSON.parse(resText);\n const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default'];\n ElMessage.error(errMsg);\n }\n loadingInstance.close();\n })\n .catch((r: any) => {\n console.error(r);\n ElMessage.error('下载文件出现错误,请联系管理员!');\n loadingInstance.close();\n });\n}\n","import { MessageBoxData } from 'element-plus';\r\nimport { LoadingInstance } from 'element-plus/es/components/loading/src/loading';\r\nlet loadingInstance: LoadingInstance;\r\nexport default {\r\n // 消息提示\r\n msg(content: any) {\r\n ElMessage.info(content);\r\n },\r\n // 错误消息\r\n msgError(content: any) {\r\n ElMessage.error(content);\r\n },\r\n // 成功消息\r\n msgSuccess(content: any) {\r\n ElMessage.success(content);\r\n },\r\n // 警告消息\r\n msgWarning(content: any) {\r\n ElMessage.warning(content);\r\n },\r\n // 弹出提示\r\n alert(content: any) {\r\n ElMessageBox.alert(content, '系統提示');\r\n },\r\n // 错误提示\r\n alertError(content: any) {\r\n ElMessageBox.alert(content, '系統提示', { type: 'error' });\r\n },\r\n // 成功提示\r\n alertSuccess(content: any) {\r\n ElMessageBox.alert(content, '系統提示', { type: 'success' });\r\n },\r\n // 警告提示\r\n alertWarning(content: any) {\r\n ElMessageBox.alert(content, '系統提示', { type: 'warning' });\r\n },\r\n // 通知提示\r\n notify(content: any) {\r\n ElNotification.info(content);\r\n },\r\n // 错误通知\r\n notifyError(content: any) {\r\n ElNotification.error(content);\r\n },\r\n // 成功通知\r\n notifySuccess(content: any) {\r\n ElNotification.success(content);\r\n },\r\n // 警告通知\r\n notifyWarning(content: any) {\r\n ElNotification.warning(content);\r\n },\r\n // 确认窗体\r\n confirm(content: any): Promise<MessageBoxData> {\r\n return ElMessageBox.confirm(content, '系統提示', {\r\n confirmButtonText: '確認',\r\n cancelButtonText: '取消',\r\n type: 'warning'\r\n });\r\n },\r\n // 提交内容\r\n prompt(content: any, title: any, inputType: any = '') {\r\n return ElMessageBox.prompt(content, title || '系統提示', {\r\n confirmButtonText: '確認',\r\n cancelButtonText: '取消',\r\n type: 'warning',\r\n inputType: inputType,\r\n inputPlaceholder: content\r\n });\r\n },\r\n\r\n // 打开遮罩层\r\n loading(content: string) {\r\n loadingInstance = ElLoading.service({\r\n lock: true,\r\n text: content,\r\n background: 'rgba(0, 0, 0, 0.7)'\r\n });\r\n },\r\n // 关闭遮罩层\r\n closeLoading() {\r\n loadingInstance.close();\r\n }\r\n};\r\n","import { getRequest } from '@/utils/http';\r\nimport { AxiosPromise } from 'axios';\r\nimport { DictDataForm, DictDataQuery, DictDataVO } from './types';\r\n// 根据字典类型查询字典数据信息\r\nexport function getDicts(dictType: string): AxiosPromise<DictDataVO[]> {\r\n return getRequest()({\r\n url: '/system/dict/data/type/' + dictType,\r\n method: 'get'\r\n });\r\n}\r\n\r\n// 查询字典数据列表\r\nexport function listData(query: DictDataQuery): AxiosPromise<DictDataVO[]> {\r\n return getRequest()({\r\n url: '/system/dict/data/list',\r\n method: 'get',\r\n params: query\r\n });\r\n}\r\n\r\n// 查询字典数据详细\r\nexport function getData(dictCode: string | number): AxiosPromise<DictDataVO> {\r\n return getRequest()({\r\n url: '/system/dict/data/' + dictCode,\r\n method: 'get'\r\n });\r\n}\r\n\r\n// 新增字典数据\r\nexport function addData(data: DictDataForm) {\r\n return getRequest()({\r\n url: '/system/dict/data',\r\n method: 'post',\r\n data: data\r\n });\r\n}\r\n\r\n// 修改字典数据\r\nexport function updateData(data: DictDataForm) {\r\n return getRequest()({\r\n url: '/system/dict/data',\r\n method: 'put',\r\n data: data\r\n });\r\n}\r\n\r\n// 删除字典数据\r\nexport function delData(dictCode: string | number | Array<string | number>) {\r\n return getRequest()({\r\n url: '/system/dict/data/' + dictCode,\r\n method: 'delete'\r\n });\r\n}\r\n","import { getRequest } from '@/utils/http';\r\nimport { AxiosPromise } from 'axios';\r\nimport { CommonDictDataForm, CommonDictDataQuery, CommonDictDataVO } from './types';\r\n// 根据字典类型查询字典数据信息\r\nexport function getDicts(dictType: string): AxiosPromise<CommonDictDataVO[]> {\r\n return getRequest()({\r\n url: '/system/common/dict/data/type/' + dictType,\r\n method: 'get'\r\n });\r\n}\r\n\r\n// 查询字典数据列表\r\nexport function listData(query: CommonDictDataQuery): AxiosPromise<CommonDictDataVO[]> {\r\n return getRequest()({\r\n url: '/system/common/dict/data/list',\r\n method: 'get',\r\n params: query\r\n });\r\n}\r\n\r\n// 查询字典数据详细\r\nexport function getData(dictCode: string | number): AxiosPromise<CommonDictDataVO> {\r\n return getRequest()({\r\n url: '/system/common/dict/data/' + dictCode,\r\n method: 'get'\r\n });\r\n}\r\n\r\n// 新增字典数据\r\nexport function addData(data: CommonDictDataForm) {\r\n return getRequest()({\r\n url: '/system/common/dict/data',\r\n method: 'post',\r\n data: data\r\n });\r\n}\r\n\r\n// 修改字典数据\r\nexport function updateData(data: CommonDictDataForm) {\r\n return getRequest()({\r\n url: '/system/common/dict/data',\r\n method: 'put',\r\n data: data\r\n });\r\n}\r\n\r\n// 删除字典数据\r\nexport function delData(dictCode: string | number | Array<string | number>) {\r\n return getRequest()({\r\n url: '/system/common/dict/data/' + dictCode,\r\n method: 'delete'\r\n });\r\n}\r\n","import { defineStore } from 'pinia';\r\n\r\nexport const useDictStore = defineStore('dict', () => {\r\n const dict = ref<Map<string, DictDataOption[]>>(new Map());\r\n\r\n /**\r\n * 获取字典\r\n * @param _key 字典key\r\n */\r\n const getDict = (_key: string): DictDataOption[] | null => {\r\n if (!_key) {\r\n return null;\r\n }\r\n return dict.value.get(_key) || null;\r\n };\r\n\r\n /**\r\n * 设置字典\r\n * @param _key 字典key\r\n * @param _value 字典value\r\n */\r\n const setDict = (_key: string, _value: DictDataOption[]) => {\r\n if (!_key) {\r\n return false;\r\n }\r\n try {\r\n dict.value.set(_key, _value);\r\n return true;\r\n } catch (e) {\r\n console.error('Error in setDict:', e);\r\n return false;\r\n }\r\n };\r\n\r\n /**\r\n * 删除字典\r\n * @param _key\r\n */\r\n const removeDict = (_key: string): boolean => {\r\n if (!_key) {\r\n return false;\r\n }\r\n try {\r\n return dict.value.delete(_key);\r\n } catch (e) {\r\n console.error('Error in removeDict:', e);\r\n return false;\r\n }\r\n };\r\n\r\n /**\r\n * 清空字典\r\n */\r\n const cleanDict = (): void => {\r\n dict.value.clear();\r\n };\r\n\r\n return {\r\n dict,\r\n getDict,\r\n setDict,\r\n removeDict,\r\n cleanDict\r\n };\r\n});\r\n","import { getDicts } from '@/api/system/dict/data';\r\nimport { getDicts as getCommonDicts } from '@/api/system/commonDict/data';\r\nimport { useDictStore } from '@/store/modules/dict';\r\n/**\r\n * 获取字典数据\r\n */\r\nexport const useDict = (...args: string[]): { [key: string]: DictDataOption[] } => {\r\n const res = ref<{\r\n [key: string]: DictDataOption[];\r\n }>({});\r\n return (() => {\r\n args.forEach(async (dictType) => {\r\n res.value[dictType] = [];\r\n const dicts = useDictStore().getDict(dictType);\r\n if (dicts) {\r\n res.value[dictType] = dicts;\r\n } else {\r\n await getDicts(dictType).then((resp) => {\r\n res.value[dictType] = resp.data.map(\r\n (p): DictDataOption => ({ label: p.dictLabel, value: p.dictValue, elTagType: p.listClass, elTagClass: p.cssClass })\r\n );\r\n useDictStore().setDict(dictType, res.value[dictType]);\r\n });\r\n }\r\n });\r\n return res.value;\r\n })();\r\n};\r\n\r\nexport const useOneDict = async (dictType: string) => {\r\n const dicts = useDictStore().getDict(dictType);\r\n if (dicts && dicts.length > 0) return dicts;\r\n else {\r\n const result = await getDicts(dictType);\r\n const data = result.data.map(\r\n (p): DictDataOption => ({ label: p.dictLabel, value: p.dictValue, elTagType: p.listClass, elTagClass: p.cssClass, remark: p.remark })\r\n );\r\n useDictStore().setDict(dictType, data);\r\n return data;\r\n }\r\n};\r\n\r\n/**\r\n * 获取公共字典数据\r\n */\r\nexport const useCommonDict = (...args: string[]): { [key: string]: DictDataOption[] } => {\r\n const res = ref<{\r\n [key: string]: DictDataOption[];\r\n }>({});\r\n return (() => {\r\n args.forEach(async (dictType) => {\r\n res.value[dictType] = [];\r\n const dicts = useDictStore().getDict(dictType);\r\n if (dicts && dicts.length > 0) {\r\n res.value[dictType] = dicts;\r\n } else {\r\n await getCommonDicts(dictType).then((resp) => {\r\n res.value[dictType] = resp.data.map(\r\n (p): DictDataOption => ({ label: p.dictLabel, value: p.dictValue, elTagType: p.listClass, elTagClass: p.cssClass, remark: p.remark })\r\n );\r\n useDictStore().setDict(dictType, res.value[dictType]);\r\n });\r\n }\r\n });\r\n return res.value;\r\n })();\r\n};\r\n\r\nexport const useOneCommonDict = async (dictType: string) => {\r\n const dicts = useDictStore().getDict(dictType);\r\n if (dicts && dicts.length > 0) return dicts;\r\n else {\r\n const result = await getCommonDicts(dictType);\r\n const data = result.data.map((p): DictDataOption => ({ label: p.dictLabel, value: p.dictValue, elTagType: p.listClass, elTagClass: p.cssClass }));\r\n useDictStore().setDict(dictType, data);\r\n return data;\r\n }\r\n};\r\n","/**\r\n * v-copyText 复制文本内容\r\n * Copyright (c) 2022 ruoyi\r\n */\r\nimport { DirectiveBinding } from 'vue';\r\n\r\nexport default {\r\n beforeMount(el: any, { value, arg }: DirectiveBinding) {\r\n if (arg === 'callback') {\r\n el.$copyCallback = value;\r\n } else {\r\n el.$copyValue = value;\r\n const handler = () => {\r\n copyTextToClipboard(el.$copyValue);\r\n if (el.$copyCallback) {\r\n el.$copyCallback(el.$copyValue);\r\n }\r\n };\r\n el.addEventListener('click', handler);\r\n el.$destroyCopy = () => el.removeEventListener('click', handler);\r\n }\r\n }\r\n};\r\n\r\nfunction copyTextToClipboard(input: string, { target = document.body } = {}) {\r\n const element = document.createElement('textarea');\r\n const previouslyFocusedElement = document.activeElement as HTMLInputElement;\r\n element.value = input;\r\n // Prevent keyboard from showing on mobile\r\n element.setAttribute('readonly', '');\r\n\r\n element.style.contain = 'strict';\r\n element.style.position = 'absolute';\r\n element.style.left = '-9999px';\r\n element.style.fontSize = '12pt'; // Prevent zooming on iOS\r\n\r\n const selection = document.getSelection();\r\n let originalRange;\r\n if (selection) {\r\n originalRange = selection?.rangeCount > 0 && selection.getRangeAt(0);\r\n }\r\n target.append(element);\r\n element.select();\r\n\r\n // Explicit selection workaround for iOS\r\n element.selectionStart = 0;\r\n element.selectionEnd = input.length;\r\n\r\n let isSuccess = false;\r\n try {\r\n isSuccess = document.execCommand('copy');\r\n } catch (err) {\r\n console.error(err);\r\n }\r\n element.remove();\r\n\r\n if (originalRange) {\r\n selection?.removeAllRanges();\r\n selection?.addRange(originalRange);\r\n }\r\n\r\n // Get the focus back on the previously focused element, if any\r\n if (previouslyFocusedElement) {\r\n previouslyFocusedElement.focus();\r\n }\r\n return isSuccess;\r\n}\r\n","/**\n * vue3-curd 安装插件\n *\n * 用法:\n * import Vue3Curd from 'vue3-curd'\n * app.use(Vue3Curd, {\n * request, // 业务侧 axios 实例(携带鉴权/拦截器)\n * globalHeaders, // 返回鉴权请求头的函数\n * baseUrl, // 后端 API 基础地址\n * hasPermi, hasRoles // 权限校验函数\n * })\n */\nimport type { App } from 'vue';\nimport type { AxiosInstance } from 'axios';\n\nimport Curd from '@/components/CURD/index.vue';\nimport DictTag from '@/components/DictTag/index.vue';\nimport SvgIcon from '@/components/SvgIcon/index.vue';\nimport OssImagePreview from '@/components/OssImagePreview/index.vue';\nimport OssFile from '@/components/ossFile/index.vue';\nimport Editor from '@/components/Editor/index.vue';\nimport TranslateInput from '@/components/TranslateInput/index.vue';\nimport ImageUpload from '@/components/ImageUpload/index.vue';\nimport FileUpload from '@/components/FileUpload/index.vue';\n\nimport modal from '@/plugins/modal';\nimport { download } from '@/utils/download';\nimport { useDict, useOneDict, useCommonDict, useOneCommonDict } from '@/utils/dict';\nimport { parseTime, addDateRange, handleTree, selectDictLabel, selectDictLabels } from '@/utils/ruoyi';\nimport { setRequest, setGlobalHeaders, setBaseUrl } from '@/utils/http';\nimport { setPermissionChecker, hasPermi, hasRoles } from '@/directive/curd-perm';\nimport copyText from '@/directive/common/copyText';\n\nexport interface Vue3CurdOptions {\n /** 业务侧 axios 实例(携带鉴权/拦截器),不传则使用库内置最小实例 */\n request?: AxiosInstance;\n /** 返回鉴权请求头的函数,用于上传组件 */\n globalHeaders?: () => Record<string, string>;\n /** 后端 API 基础地址,用于上传/下载等 */\n baseUrl?: string;\n /** 按钮权限校验:v-hasPermi */\n hasPermi?: (value: string | string[]) => boolean;\n /** 角色权限校验:v-hasRoles */\n hasRoles?: (value: string | string[]) => boolean;\n}\n\nconst components: Record<string, any> = {\n Curd,\n DictTag,\n SvgIcon,\n OssImagePreview,\n OssFile,\n Editor,\n TranslateInput,\n ImageUpload,\n FileUpload\n};\n\nexport const install = (app: App, options: Vue3CurdOptions = {}) => {\n if (options.baseUrl != null) setBaseUrl(options.baseUrl);\n if (options.request) setRequest(options.request);\n if (options.globalHeaders) setGlobalHeaders(options.globalHeaders);\n if (options.hasPermi || options.hasRoles) setPermissionChecker(options);\n\n for (const [name, comp] of Object.entries(components)) {\n app.component(name, comp);\n }\n\n const gp = app.config.globalProperties as any;\n gp.$modal = modal;\n gp.download = download;\n gp.useDict = useDict;\n gp.useOneDict = useOneDict;\n gp.useOneCommonDict = useOneCommonDict;\n gp.useCommonDict = useCommonDict;\n gp.parseTime = parseTime;\n gp.addDateRange = addDateRange;\n gp.handleTree = handleTree;\n gp.selectDictLabel = selectDictLabel;\n gp.selectDictLabels = selectDictLabels;\n\n app.directive('hasPermi', hasPermi);\n app.directive('hasRoles', hasRoles);\n app.directive('copyText', copyText);\n};\n\nexport default { install };\n"],"names":["propTypes","createTypes","props","__props","iconName","computed","svgClass","tagData","pValue","data","res","valueArray","_request","_globalHeaders","_baseUrl","createDefaultRequest","instance","axios","err","getRequest","setRequest","getGlobalHeaders","setGlobalHeaders","fn","getBaseUrl","setBaseUrl","url","listByIds","ossId","delOss","src","ref","realSrc","realSrcList","real_src_list","srcList","item","realWidth","width","height","watch","value","list","realHeight","LOCAL_DICTS","FIXED_WIDTH_FIELDS","PAGE_SIZES","DEFAULT_PAGE_OPTIONS","PAGING_LAYOUT","MIN_TOOLBAR_WIDTH","TOOLBAR_BUTTON_WIDTH","deleteEmptyParams","params","result","key","calculateColumnWidth","column","fixedField","field","getDeepProperty","obj","prefix","suffix","initializeColumns","columns","initializeFormRules","rules","sortColumns","b","getNestedValue","path","keys","k","aOrder","bOrder","filterEditableColumns","isUpdate","hasKeyId","isButtonVisible","action","isButtonDisabled","formatDateRangeParams","queryParams","dateRange","propName","proxy","useTableData","dataSource","getCurrentInstance","listData","loading","multipleSelection","filterParams","defaultSort","pageOptions","listSize","pagingLayout","executeQuery","_a","getQueryParams","resData","isSinglePage","error","beforeQueryParams","refreshTable","handlePageChange","pageNum","handleSizeChange","pageSize","handleSortChange","handleSelectionChange","val","initQueryParams","useDictData","initColumnDicts","toRaw","initSearchDicts","initSelectApi","initTreeSelectApi","valueKey","emits","__emit","tableMaxHeight","curdTableRef","initData","localDicts","getRowKeyValue","row","columnToolbars","isRowSelectable","columnToolbarWidth","customWidth","buttonVisible","buttonDisabled","calColumnWidth","calColumnAlign","align","fromDbDicts","dictData","fromLocalDicts","fromData","getTagType","deepJsonProp","onSelectionChange","normalizedSelection","onRowClick","event","_b","actionEvent","ev","getSelection","applyDefaultSelectedRows","nextTick","table","keyStr","applyDefaultDisabledSelectedRows","initTableData","onMounted","__expose","emit","number","uploadList","baseUrl","uploadFileUrl","headers","fileList","showTip","fileUploadRef","temp","oss","handleBeforeUpload","file","fileName","fileExt","handleExceed","handleUploadError","handleUploadSuccess","uploadedSuccessfully","handleDelete","index","listToString","f","getFileName","name","separator","strs","dialogImageUrl","dialogVisible","uploadImgUrl","imageUploadRef","fileAccept","type","itemData","isImg","fileExtension","compressAccurately","handlePictureCardPreview","i","getTranslationByName","addTranslationBatch","useTranslationStore","defineStore","translation","_key","_value","useTranslation","dataVo","languages","hasInit","updateModelValue","text","zhHkOnBlur","getTranslateData","languageTemp","changeLanguage","lang","getTranslate","element","setInputFocus","newVal","upload","reactive","quillEditorRef","uploadRef","options","Quill","styles","style","content","v","quill","length","parseTime","time","pattern","format","date","formatObj","addDateRange","search","selectDictLabel","datas","actions","selectDictLabels","currentSeparator","match","handleTree","id","parentId","children","config","childrenListMap","tree","d","parentObj","tansParams","part","subPart","blobValidate","deepClone","source","targetObj","useFormData","editData","formData","getFormComponentDefaultValue","form","initFormData","currentEditData","formDataTemp","dataToUse","formProp","clonedEditData","initUpdateFormData","formType","prop","_c","rangeBeginProp","_d","rangeEndProp","_e","saveParams","mergedParams","arr","t","useI18n","translateInputRef","curdFormRef","initFormDataComposable","prepareSaveData","formObjs","filteredColumns","formInline","getTreeDefaultExpandedKeys","propsConfig","initFormRules","handleSave","valid","saveTranslation","handleClose","translateInputRefs","bRefTable","toolbars","createAction","batchDeleteAction","toolbarsWithoutCreate","emitToolbarAction","columnsPerRow","columnSpan","showCard","actionColSpan","remainder","searchColumns","rest","computedPaddingBottom","searchForm","handleQuery","handleExport","dateStr","dayjs","dataTemp","handleClear","searchColumnsTemp","col","searchFormTemp","searchProp","searchType","legacyMaxHeight","editDialogVisible","curdSearchRef","selectedCount","columnSlotNames","names","closeEdit","refresh","initTable","initSearch","processedData","execActionEvent","handler","handleUpdate","handleBatchDelete","handleCreate","handleView","handleFilter","handleBack","deleteId","confirmText","selection","deleteIds","openCreate","isConfigObject","pm","_hasPermiChecker","_hasRolesChecker","setPermissionChecker","opts","hasPermi","el","binding","hasRoles","errorCode","download","loadingInstance","ElLoading","p","resp","blob","FileSaver","resText","rspObj","errMsg","ElMessage","r","modal","ElMessageBox","ElNotification","title","inputType","getDicts","dictType","useDictStore","dict","e","useDict","args","dicts","useOneDict","useCommonDict","getCommonDicts","useOneCommonDict","copyText","arg","copyTextToClipboard","input","target","previouslyFocusedElement","originalRange","isSuccess","components","Curd","DictTag","SvgIcon","OssImagePreview","OssFile","Editor","TranslateInput","ImageUpload","FileUpload","install","app","comp","gp","plugin"],"mappings":"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"}
1
+ {"version":3,"file":"vue3-curd.es.js","sources":["../src/utils/propTypes.ts","../src/components/SvgIcon/index.vue","../src/components/DictTag/index.vue","../src/utils/http.ts","../src/api/system/oss/index.ts","../src/components/OssImagePreview/index.vue","../src/components/ossFile/index.vue","../src/components/CURD/constants.ts","../src/components/CURD/utils.ts","../src/components/CURD/composables/useTableData.ts","../src/components/CURD/composables/useDictData.ts","../src/components/CURD/curdTable.vue","../src/components/FileUpload/index.vue","../src/components/ImageUpload/index.vue","../src/api/app/translation/index.ts","../src/store/modules/translation.ts","../src/utils/translation.ts","../src/components/TranslateInput/index.vue","../src/components/Editor/index.vue","../src/utils/ruoyi.ts","../src/utils/index.ts","../src/components/CURD/composables/useFormData.ts","../src/components/CURD/curdEdit.vue","../src/components/CURD/curdSearch.vue","../src/components/CURD/index.vue","../src/directive/curd-perm.ts","../src/utils/errorCode.ts","../src/utils/download.ts","../src/plugins/modal.ts","../src/api/system/dict/data/index.ts","../src/api/system/commonDict/data/index.ts","../src/store/modules/dict.ts","../src/utils/dict.ts","../src/directive/common/copyText.ts","../src/plugin.ts"],"sourcesContent":["import { CSSProperties } from 'vue';\r\nimport VueTypes, { createTypes, toValidableType, VueTypeValidableDef, VueTypesInterface } from 'vue-types';\r\n\r\ntype PropTypes = VueTypesInterface & {\r\n readonly style: VueTypeValidableDef<CSSProperties>;\r\n readonly fieldOption: VueTypeValidableDef<Array<FieldOption>>;\r\n};\r\n\r\nconst propTypes = createTypes({\r\n func: undefined,\r\n bool: undefined,\r\n string: undefined,\r\n number: undefined,\r\n object: undefined,\r\n integer: undefined\r\n}) as PropTypes;\r\n\r\nexport default class ProjectTypes extends VueTypes {\r\n static get style() {\r\n return toValidableType('style', {\r\n type: [String, Object],\r\n default: undefined\r\n });\r\n }\r\n}\r\nexport { propTypes };\r\n","<template>\r\n <svg :class=\"svgClass\" aria-hidden=\"true\">\r\n <use :xlink:href=\"iconName\" :fill=\"color\" />\r\n </svg>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { propTypes } from '@/utils/propTypes';\r\n\r\nconst props = defineProps({\r\n iconClass: propTypes.string.isRequired,\r\n className: propTypes.string.def(''),\r\n color: propTypes.string.def('')\r\n});\r\nconst iconName = computed(() => `#icon-${props.iconClass}`);\r\nconst svgClass = computed(() => {\r\n if (props.className) {\r\n return `svg-icon ${props.className}`;\r\n }\r\n return 'svg-icon';\r\n});\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.sub-el-icon,\r\n.nav-icon {\r\n display: inline-block;\r\n font-size: 15px;\r\n margin-right: 12px;\r\n position: relative;\r\n}\r\n\r\n.svg-icon {\r\n width: 1em;\r\n height: 1em;\r\n position: relative;\r\n fill: currentColor;\r\n vertical-align: -2px;\r\n}\r\n</style>\r\n","<template>\r\n <template v-if=\"Array.isArray(tagData)\">\r\n <template v-for=\"item in tagData\">\r\n <el-tag :disable-transitions=\"true\" :key=\"item.value\" :type=\"item.elTagType || 'primary'\" v-if=\"item.label\">\r\n {{ item.label }}\r\n </el-tag>\r\n </template>\r\n </template>\r\n <template v-else>\r\n <el-tag :disable-transitions=\"true\" :key=\"tagData.value\" :type=\"tagData.elTagType || 'primary'\" v-if=\"tagData.label\">\r\n {{ tagData.label }}\r\n </el-tag>\r\n </template>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\n\r\nconst props = defineProps({\r\n options: {\r\n type: Array as PropType<DictDataOption[]>,\r\n default: null,\r\n },\r\n value: {\r\n type: [Number, String, Array, undefined] as PropType<number | string | Array<number | string> | undefined>,\r\n default: \"\"\r\n },\r\n});\r\n\r\n\r\nconst tagData = computed(() => {\r\n const pValue = props.value\r\n if (pValue === '' || pValue === null || typeof pValue === \"undefined\") {\r\n return \"\"\r\n }\r\n if (Array.isArray(pValue)) {\r\n const data = <any>[]\r\n props.options.map((res: any) => {\r\n if (pValue.indexOf(res.value) > -1 || pValue.indexOf(res.label) > -1) {\r\n data.push(res)\r\n }\r\n })\r\n return data;\r\n }\r\n else if (typeof pValue === 'string') {\r\n let valueArray = <any>[];\r\n if (pValue.indexOf(\",\") > -1) {\r\n valueArray = pValue.split(',');\r\n }\r\n else if (pValue.indexOf(\"、\") > -1) {\r\n valueArray = pValue.split('、');\r\n }\r\n if (valueArray.length > 0) {\r\n const data = <any>[]\r\n props.options.map((res: any) => {\r\n if (valueArray.indexOf(res.value) > -1 || valueArray.indexOf(res.label) > -1) {\r\n data.push(res)\r\n }\r\n })\r\n return data;\r\n }\r\n }\r\n return props.options.find((res: any) => res.value == pValue || res.label == pValue) || pValue;\r\n\r\n});\r\n</script>\r\n\r\n<style scoped>\r\n.el-tag+.el-tag {\r\n margin-left: 5px;\r\n margin-bottom: 3px;\r\n margin-top: 3px;\r\n}\r\n</style>\r\n","/**\r\n * HTTP 访问器:将 axios 实例、鉴权请求头、baseURL 等应用级耦合点\r\n * 从库内部代码中解耦,由消费者通过 install 插件选项注入。\r\n *\r\n * 默认提供一个仅返回 res.data 的最小 axios 实例,\r\n * 业务侧应通过 setRequest 注入自身携带鉴权/拦截器的实例。\r\n */\r\nimport axios, { type AxiosInstance } from 'axios';\r\n\r\nlet _request: AxiosInstance | null = null;\r\nlet _globalHeaders: () => Record<string, string> = () => ({});\r\nlet _baseUrl = '';\r\n\r\nconst createDefaultRequest = (): AxiosInstance => {\r\n const instance = axios.create({ baseURL: _baseUrl, timeout: 50000 });\r\n instance.interceptors.response.use(\r\n (res) => res.data,\r\n (err) => Promise.reject(err)\r\n );\r\n return instance;\r\n};\r\n\r\nexport const getRequest = (): AxiosInstance => {\r\n if (!_request) _request = createDefaultRequest();\r\n return _request;\r\n};\r\n\r\nexport const setRequest = (instance: AxiosInstance) => {\r\n _request = instance;\r\n};\r\n\r\nexport const getGlobalHeaders = () => _globalHeaders();\r\n\r\nexport const setGlobalHeaders = (fn: () => Record<string, string>) => {\r\n _globalHeaders = fn;\r\n};\r\n\r\nexport const getBaseUrl = () => _baseUrl;\r\n\r\nexport const setBaseUrl = (url: string) => {\r\n _baseUrl = url || '';\r\n};\r\n","import { getRequest } from '@/utils/http';\r\nimport { OssQuery, OssVO } from './types';\r\nimport { AxiosPromise } from 'axios';\r\n\r\n// 查询OSS对象存储列表\r\nexport function listOss(query: OssQuery): AxiosPromise<OssVO[]> {\r\n return getRequest()({\r\n url: '/resource/oss/list',\r\n method: 'get',\r\n params: query\r\n });\r\n}\r\n\r\n// 查询OSS对象基于id串\r\nexport function listByIds(ossId: string | number): AxiosPromise<OssVO[]> {\r\n return getRequest()({\r\n url: '/resource/oss/listByIds/' + ossId,\r\n method: 'get'\r\n });\r\n}\r\n\r\n// 删除OSS对象存储\r\nexport function delOss(ossId: string | number | Array<string | number>) {\r\n return getRequest()({\r\n url: '/resource/oss/' + ossId,\r\n method: 'delete'\r\n });\r\n}\r\n\r\n/**\r\n * 上传文件\r\n * @param formData\r\n * @returns\r\n */\r\nexport function uploadOss(fileData: any, fileName: any) {\r\n const formData = new FormData();\r\n if (fileName) formData.append('file', fileData, fileName);\r\n else formData.append('file', fileData);\r\n return getRequest()({\r\n url: '/resource/oss/upload',\r\n method: 'post',\r\n data: formData\r\n });\r\n}\r\n","<template>\r\n <div>\r\n <el-image\r\n v-if=\"realSrc\"\r\n :src=\"`${realSrc}`\"\r\n fit=\"scale-down\"\r\n loading=\"lazy\"\r\n :style=\"`width:${realWidth};height:${realWidth};`\"\r\n :preview-src-list=\"realSrcList\"\r\n preview-teleported\r\n >\r\n <template #error>\r\n <div class=\"image-slot\">\r\n <el-icon><PictureFilled /></el-icon>\r\n </div>\r\n </template>\r\n </el-image>\r\n <div v-else>\r\n <div class=\"image-slot\" :style=\"`width:${realWidth};height:${realWidth};`\">\r\n <el-icon><PictureFilled /></el-icon>\r\n </div>\r\n </div>\r\n </div>\r\n</template>\r\n<script setup lang=\"ts\">\r\nimport { PictureFilled } from '@element-plus/icons-vue';\r\nimport { listByIds } from \"@/api/system/oss\";\r\nconst props = defineProps({\r\n modelValue: [String, Object, Array, Number],\r\n width: {\r\n type: [Number, String],\r\n default: \"\",\r\n },\r\n height: {\r\n type: [Number, String],\r\n default: \"\",\r\n },\r\n})\r\n\r\nconst src = ref(\"\")\r\n\r\nconst realSrc = computed(() => {\r\n if (!src.value) return;\r\n const real_src = src.value.split(\",\")[0];\r\n return real_src;\r\n})\r\n\r\nconst realSrcList = computed(() => {\r\n if (!src.value) return;\r\n const real_src_list = src.value.split(\",\");\r\n const srcList = [] as any;\r\n real_src_list.forEach((item) => {\r\n return srcList.push(item);\r\n })\r\n return srcList;\r\n})\r\n\r\nconst realWidth = computed(() => {\r\n const width = props.width\r\n return typeof width == \"string\" ? width : `${width}px`;\r\n})\r\n\r\nconst realHeight = computed(() => {\r\n const height = props.height\r\n return typeof height == \"string\" ? height : `${height}px`;\r\n})\r\n\r\nwatch(() => props.modelValue, async (value) => {\r\n if (value) {\r\n let list;\r\n if (Array.isArray(value)) list = value;\r\n else {\r\n const res = await listByIds(value as any)\r\n list = res.data;\r\n }\r\n src.value = list.map(item => { return item.url; }).join(\",\");\r\n } else src.value = \"\";\r\n}, { immediate: true, deep: true })\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.el-image {\r\n border-radius: 2px;\r\n\r\n :deep(.el-image__inner) {\r\n transition: all 0.3s;\r\n cursor: pointer;\r\n }\r\n\r\n :deep(.image-slot) {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n width: 100%;\r\n height: 100%;\r\n color: #909399;\r\n font-size: 30px;\r\n }\r\n}\r\n\r\n.image-slot {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n width: 110px;\r\n height: 100px;\r\n color: #909399;\r\n font-size: 30px;\r\n border-radius: 5px;\r\n\r\n}\r\n</style>\r\n","<template>\r\n <span>\r\n <template v-if=\"['.jpg', '.png', '.jpeg'].indexOf(data.fileSuffix) > -1\">\r\n <el-image :src=\"data.url\" fit=\"cover\" :preview-src-list=\"[data.url]\" :style=\"`width:${realWidth};height:${realHeight};`\" preview-teleported>\r\n <template #error>\r\n <div class=\"image-slot\">\r\n <el-icon>\r\n <PictureFilled />\r\n </el-icon>\r\n </div>\r\n </template>\r\n </el-image>\r\n </template>\r\n <template v-else-if=\"['.xls', '.xlsx', '.csv'].indexOf(data.fileSuffix) > -1\">\r\n <svg-icon icon-class=\"excel\" :style=\"`width:${realWidth};height:${realHeight};`\" />\r\n </template>\r\n <template v-else-if=\"['.doc', '.docx'].indexOf(data.fileSuffix) > -1\">\r\n <svg-icon icon-class=\"word\" :style=\"`width:${realWidth};height:${realHeight};`\" />\r\n </template>\r\n <template v-else-if=\"['.pdf'].indexOf(data.fileSuffix) > -1\">\r\n <svg-icon icon-class=\"pdf\" :style=\"`width:${realWidth};height:${realHeight};`\" />\r\n </template>\r\n <template v-else-if=\"['.tet'].indexOf(data.fileSuffix) > -1\">\r\n <svg-icon icon-class=\"tet\" :style=\"`width:${realWidth};height:${realHeight};`\" />\r\n </template>\r\n <template v-else-if=\"['.zip'].indexOf(data.fileSuffix) > -1\">\r\n <svg-icon icon-class=\"zip\" :style=\"`width:${realWidth};height:${realHeight};`\" />\r\n </template>\r\n <template v-else>\r\n <svg-icon icon-class=\"unknown\" :style=\"`width:${realWidth};height:${realHeight};`\" />\r\n </template>\r\n </span>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { PictureFilled } from '@element-plus/icons-vue';\r\nimport { OssVO } from '@/api/system/oss/types';\r\n\r\nconst props = defineProps({\r\n data: {\r\n type: Object,\r\n default: () => <OssVO>{}\r\n },\r\n width: {\r\n type: [Number, String],\r\n default: 60\r\n },\r\n height: {\r\n type: [Number, String],\r\n default: 60\r\n },\r\n});\r\nconst realWidth = computed(() =>\r\n typeof props.width == \"string\" ? props.width : `${props.width}px`\r\n);\r\n\r\nconst realHeight = computed(() =>\r\n typeof props.height == \"string\" ? props.height : `${props.height}px`\r\n);\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.el-image {\r\n border-radius: 4px;\r\n\r\n :deep(.el-image__inner) {\r\n transition: all 0.3s;\r\n cursor: pointer;\r\n\r\n &:hover {\r\n transform: scale(1.2);\r\n }\r\n }\r\n\r\n :deep(.image-slot) {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n width: 100%;\r\n height: 100%;\r\n color: #909399;\r\n font-size: 30px;\r\n }\r\n}\r\n</style>\r\n","/**\n * CURD组件常量定义\n */\n\nimport type { DictItem } from './types';\n\n// 本地字典\nexport const LOCAL_DICTS: Record<string, DictItem[]> = {\n bool: [\n { label: '是', value: '1' },\n { label: '否', value: '0' },\n { label: '是', value: 'True' },\n { label: '否', value: 'False' }\n ]\n};\n\n// 固定宽度字段配置\nexport const FIXED_WIDTH_FIELDS = [\n { prop: 'createTime', width: '165px' },\n { prop: 'createBy', width: '100px' },\n { prop: 'orderNum', width: '80px' },\n { prop: 'sort', width: '80px' },\n { prop: 'code', width: '120px' },\n { prop: 'updateTime', width: '165px' }\n];\n\n// 分页大小选项\nexport const PAGE_SIZES = [10, 15, 20, 30, 50, 100];\n\n// 默认分页配置\nexport const DEFAULT_PAGE_OPTIONS = {\n pageSize: 15,\n pageNum: 1,\n total: 0,\n orderByColumn: '',\n isAsc: ''\n};\n\n// 分页布局\nexport const PAGING_LAYOUT = {\n FULL: 'total, sizes, prev, pager, next',\n SIMPLE: 'total'\n};\n\n// 操作按钮最小宽度\nexport const MIN_TOOLBAR_WIDTH = 30;\nexport const TOOLBAR_BUTTON_WIDTH = 28;\n","/**\n * CURD组件工具函数\n */\n\nimport type { ColumnConfig, FormConfig } from './types';\nimport { FIXED_WIDTH_FIELDS } from './constants';\n\n/**\n * 删除空参数\n * @param params 参数对象\n * @returns 过滤后的参数对象\n */\nexport function deleteEmptyParams(params: any): Record<string, any> {\n if (!params) return {};\n\n const result: Record<string, any> = {};\n for (const key in params) {\n const value = params[key];\n if (value !== '' && value !== null && value !== undefined && (!Array.isArray(value) || value.length > 0)) {\n result[key] = value;\n }\n }\n return result;\n}\n\n/**\n * 计算列宽度\n * @param column 列配置\n * @returns 列宽度\n */\nexport function calculateColumnWidth(column: ColumnConfig): string {\n if (column.width) return String(column.width);\n\n const fixedField = FIXED_WIDTH_FIELDS.find((field) => field.prop === column.prop);\n if (fixedField) return fixedField.width;\n\n if (column.prop.includes('time')) return '100px';\n\n return 'auto';\n}\n\n/**\n * 深度获取对象属性值\n * @param data 数据对象\n * @param column 列配置\n * @returns 属性值\n */\nexport function getDeepProperty(data: any, column: ColumnConfig): any {\n if (!column.prop) return '';\n\n let result: any;\n if (column.prop.includes('.')) {\n const keys = column.prop.split('.');\n result = keys.reduce((obj, key) => obj?.[key], data);\n } else {\n result = data[column.prop];\n }\n\n if (result !== null && result !== undefined) {\n const prefix = column.prefix || '';\n const suffix = column.suffix || '';\n return `${prefix}${result}${suffix}`;\n }\n\n return '';\n}\n\n/**\n * 从字典数据中查找标签\n * @param dictData 字典数据\n * @param value 值\n * @returns 标签\n */\nexport function findDictLabel(dictData: any[], value: any): string {\n if (!dictData || !Array.isArray(dictData)) return '';\n\n const item = dictData.find((dict) => dict.value == value);\n return item?.label || '';\n}\n\n/**\n * 从字典数据中查找标签类型\n * @param dictData 字典数据\n * @param value 值\n * @returns 标签类型\n */\nexport function findDictTagType(dictData: any[], value: any): string {\n if (!dictData || !Array.isArray(dictData)) return 'info';\n\n const item = dictData.find((dict) => dict.value == value);\n return item?.tagType || 'info';\n}\n\n/**\n * 初始化列配置\n * @param columns 列配置数组\n */\nexport function initializeColumns(columns: ColumnConfig[]): void {\n columns.forEach((column) => {\n // 初始化表单配置\n if (!column.form) {\n column.form = { order: 100 };\n }\n if (column.form.order == null) {\n column.form.order = 100;\n }\n if (!column.form.type && column.type) {\n column.form.type = column.type as any;\n }\n if (!column.form.prop && column.prop) {\n column.form.prop = column.prop;\n }\n\n // 同步字典类型\n if (column.search && !column.search.dictType && column.dictType) {\n column.search.dictType = column.dictType;\n }\n if (!column.form.dictType && column.dictType) {\n column.form.dictType = column.dictType;\n }\n\n // 同步开关值\n if (!column.form.inactiveValue && column.inactiveValue) {\n column.form.inactiveValue = column.inactiveValue;\n }\n if (!column.form.activeValue && column.activeValue) {\n column.form.activeValue = column.activeValue;\n }\n\n // 复选框特殊处理\n if (column.form.type === 'checkbox') {\n column.label = '';\n }\n\n if (column.search) {\n column.search.prop = column.search.prop ?? column.prop;\n }\n });\n}\n\n/**\n * 初始化表单验证规则\n * @param columns 列配置数组\n */\nexport function initializeFormRules(columns: ColumnConfig[]): void {\n columns.forEach((column) => {\n if (!column.form) return;\n\n const rules = column.form.rules || [];\n if (column.form.required) {\n rules.push({\n required: true,\n message: `${column.label}必填`,\n trigger: 'blur'\n });\n }\n column.form.rules = rules.length > 0 ? rules : undefined;\n });\n}\n\n/**\n * 排序列配置\n * @param columns 列配置数组\n * @param key 排序键(支持嵌套属性,如 'form.order')\n * @returns 排序后的列配置数组\n */\nexport function sortColumns<T extends { [key: string]: any }>(columns: T[], key: string = 'order'): T[] {\n return [...columns].sort((a, b) => {\n // 支持嵌套属性访问\n const getNestedValue = (obj: any, path: string): number => {\n const keys = path.split('.');\n let value = obj;\n for (const k of keys) {\n value = value?.[k];\n }\n return value ?? 100;\n };\n\n const aOrder = getNestedValue(a, key);\n const bOrder = getNestedValue(b, key);\n return aOrder - bOrder;\n });\n}\n\n/**\n * 过滤可编辑的列\n * @param columns 列配置数组\n * @param isUpdate 是否为更新操作\n * @param hasKeyId 是否有主键ID\n * @returns 过滤后的列配置数组\n */\nexport function filterEditableColumns(columns: ColumnConfig[], isUpdate: boolean, hasKeyId: boolean): ColumnConfig[] {\n return columns.filter((column) => {\n if (column.canEdit === false) return false;\n if (hasKeyId && column.canUpdate === false) return false;\n if (!hasKeyId && column.canCreate === false) return false;\n return true;\n });\n}\n\n/**\n * 检查按钮是否可见\n * @param data 行数据\n * @param action 操作配置\n * @returns 是否可见\n */\nexport function isButtonVisible(data: any, action: any): boolean {\n if (!action.show) return true;\n for (const key in action.show) {\n if (action.show[key].includes(data[key])) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * 检查按钮是否禁用\n * @param data 行数据\n * @param action 操作配置\n * @returns 是否可见\n */\nexport function isButtonDisabled(data: any, action: any): boolean {\n if (!action.disable && !action.enable) return false;\n for (const key in action.disable) {\n if (action.disable[key].includes(data[key])) {\n return true;\n }\n }\n for (const key in action.enable) {\n if (!action.enable[key].includes(data[key])) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * 格式化日期范围参数\n * @param queryParams 查询参数\n * @param dateRange 日期范围\n * @param propName 属性名\n * @param proxy Vue实例代理\n */\nexport function formatDateRangeParams(queryParams: any, dateRange: any[], propName: string, proxy: any): void {\n if (dateRange && dateRange[0] && dateRange[1]) {\n queryParams[propName] = `${dateRange[0]},${dateRange[1]}`;\n proxy?.addDateRange(queryParams, dateRange, propName);\n }\n}\n","/**\n * 表格数据处理 Composable\n */\n\nimport { ref, getCurrentInstance, type ComponentInternalInstance } from 'vue';\nimport type { DataSourceConfig, PageOptions } from '../types';\nimport { DEFAULT_PAGE_OPTIONS, PAGING_LAYOUT, PAGE_SIZES } from '../constants';\nimport { deleteEmptyParams } from '../utils';\n\nexport function useTableData(dataSource: DataSourceConfig) {\n const { proxy } = getCurrentInstance() as ComponentInternalInstance;\n\n const listData = ref<any[]>([]);\n const loading = ref(false);\n const multipleSelection = ref<any[]>([]);\n const filterParams = ref<Record<string, any>>({});\n const defaultSort = ref<any>();\n\n const pageOptions = ref<PageOptions>({\n ...DEFAULT_PAGE_OPTIONS,\n pageSize: dataSource.initialPageSize ?? DEFAULT_PAGE_OPTIONS.pageSize\n });\n const listSize = ref(PAGE_SIZES);\n const pagingLayout = ref(PAGING_LAYOUT.FULL);\n\n /**\n * 执行查询\n */\n const executeQuery = async (): Promise<void> => {\n if (!dataSource.listApi) {\n if (dataSource.data) {\n listData.value = Array.isArray(dataSource.data) ? dataSource.data : dataSource.data?.value || [];\n } else {\n listData.value = [];\n }\n return;\n }\n\n loading.value = true;\n try {\n const queryParams = await getQueryParams(null);\n const res = await dataSource.listApi(queryParams);\n // 执行查询后回调\n if (dataSource.afterQuery) {\n await dataSource.afterQuery(res);\n }\n\n let resData = res.rows || res.data;\n // 树形数据处理\n if (dataSource.treeProps) {\n resData = proxy?.handleTree(res.data, dataSource.rowKey);\n }\n listData.value = resData;\n pageOptions.value.total = res.total;\n // 动态调整分页布局\n const isSinglePage = res.total / pageOptions.value.pageSize <= 1;\n pagingLayout.value = isSinglePage ? PAGING_LAYOUT.SIMPLE : PAGING_LAYOUT.FULL;\n proxy?.$forceUpdate();\n } catch (error) {\n console.error('Query failed:', error);\n } finally {\n loading.value = false;\n }\n };\n\n /**\n * 获取查询参数\n */\n const getQueryParams = async (params: any): Promise<Record<string, any>> => {\n if (params) {\n filterParams.value = deleteEmptyParams(params);\n }\n let queryParams = dataSource.queryParams || {};\n\n if (dataSource.beforeQuery) {\n const beforeQueryParams = await dataSource.beforeQuery({});\n queryParams = {\n ...pageOptions.value,\n ...queryParams,\n ...filterParams.value,\n ...beforeQueryParams\n };\n } else {\n queryParams = {\n ...pageOptions.value,\n ...queryParams,\n ...filterParams.value\n };\n }\n\n return queryParams;\n };\n\n /**\n * 刷新表格\n */\n const refreshTable = (params?: any): void => {\n if (params) {\n filterParams.value = deleteEmptyParams(params);\n }\n executeQuery();\n };\n\n /**\n * 页码变化\n */\n const handlePageChange = (pageNum: number): void => {\n if (pageOptions.value.pageNum !== pageNum) {\n pageOptions.value.pageNum = pageNum;\n executeQuery();\n }\n };\n\n /**\n * 页大小变化\n */\n const handleSizeChange = (pageSize: number): void => {\n if (pageOptions.value.pageSize !== pageSize) {\n pageOptions.value.pageSize = pageSize;\n executeQuery();\n }\n };\n\n /**\n * 排序变化\n */\n const handleSortChange = (data: any): void => {\n if (data.order) {\n pageOptions.value.orderByColumn = data.prop;\n pageOptions.value.isAsc = data.order;\n }\n executeQuery();\n };\n\n /**\n * 选择变化\n */\n const handleSelectionChange = (val: any[]): void => {\n multipleSelection.value = val;\n if (dataSource.selectionChange) {\n dataSource.selectionChange(val);\n }\n };\n\n /**\n * 初始化查询参数\n */\n const initQueryParams = () => {\n const queryParams = dataSource.queryParams || {};\n // 初始化排序\n if (queryParams.orderByColumn) {\n pageOptions.value.orderByColumn = queryParams.orderByColumn;\n defaultSort.value = {\n prop: queryParams.orderByColumn,\n order: queryParams.isAsc === 'asc' ? 'ascending' : 'descending'\n };\n }\n\n if (queryParams.isAsc) {\n pageOptions.value.isAsc = queryParams.isAsc;\n }\n };\n\n /**\n * 初始化数据\n */\n const initData = async (): Promise<void> => {\n initQueryParams();\n await executeQuery();\n };\n\n return {\n listData,\n loading,\n multipleSelection,\n pageOptions,\n listSize,\n pagingLayout,\n defaultSort,\n executeQuery,\n getQueryParams,\n refreshTable,\n handlePageChange,\n handleSizeChange,\n handleSortChange,\n handleSelectionChange,\n initData\n };\n}\n","/**\n * 字典数据处理 Composable\n */\n\nimport { getCurrentInstance, toRaw, type ComponentInternalInstance } from 'vue';\nimport type { ColumnConfig } from '../types';\n\nexport function useDictData() {\n const { proxy } = getCurrentInstance() as ComponentInternalInstance;\n\n /**\n * 初始化列的字典数据\n */\n const initColumnDicts = async (columns: ColumnConfig[]): Promise<void> => {\n for (const column of columns) {\n if (column.type === 'dictTag') {\n const data = await proxy?.useOneDict(column.dictType);\n column.data = toRaw(data);\n } else if (column.type === 'commonDictTag') {\n const data = await proxy?.useOneCommonDict(column.dictType);\n column.data = toRaw(data);\n }\n }\n };\n\n /**\n * 初始化搜索字段的字典数据\n */\n const initSearchDicts = async (column: ColumnConfig): Promise<void> => {\n if (!column.search) return;\n\n const searchType = column.search.type;\n\n switch (searchType) {\n case 'selectApi':\n if (column.search.api) {\n await initSelectApi(column);\n } else {\n console.error(`${column.prop}中search对象未设置api参数`);\n }\n break;\n\n case 'treeSelectApi':\n if (column.search.api) {\n await initTreeSelectApi(column);\n } else {\n console.error(`${column.prop}中search对象未设置api参数`);\n }\n break;\n\n case 'selectCommonDict':\n column.search.data = await proxy?.useOneCommonDict(column.search.dictType);\n break;\n\n case 'selectDict':\n column.search.data = await proxy?.useOneDict(column.search.dictType);\n break;\n }\n };\n\n /**\n * 初始化API下拉列表\n */\n const initSelectApi = async (column: ColumnConfig): Promise<void> => {\n if (!column.search?.api) return;\n\n const params = {\n pageNum: 1,\n pageSize: 1000,\n filedName: column.prop,\n ...(column.search.apiParams || {})\n };\n\n try {\n const result = await column.search.api(params);\n column.search.data = result.data || result.rows;\n } catch (error) {\n console.error(`Failed to load API data for ${column.prop}:`, error);\n }\n };\n\n /**\n * 初始化树形选择API\n */\n const initTreeSelectApi = async (column: ColumnConfig): Promise<void> => {\n if (!column.search?.api) return;\n\n try {\n const result = await column.search.api({ pageNum: 1, pageSize: 1000 });\n column.search.data = result.data || result.rows || result;\n\n // 设置默认展开第一级节点\n if (column.search.data && Array.isArray(column.search.data)) {\n const props = column.search.props || { value: 'id', label: 'label', children: 'children' };\n const valueKey = props.value || 'id';\n column.search.defaultExpandedKeys = column.search.data.map((item: any) => item[valueKey]);\n }\n } catch (error) {\n console.error(`Failed to load tree API data for ${column.prop}:`, error);\n }\n };\n\n return {\n initColumnDicts,\n initSearchDicts,\n initSelectApi\n };\n}\n","<template>\r\n <div>\r\n <el-table ref=\"curdTableRef\" v-loading=\"loading\" :data=\"listData\" :row-key=\"props.dataSource.rowKey\"\r\n :element-loading-text=\"$t('數據加載中...')\" stripe :tree-props=\"props.dataSource.treeProps\" :default-sort=\"defaultSort\"\r\n @selection-change=\"onSelectionChange\" @sort-change=\"handleSortChange\" @row-click=\"onRowClick\"\r\n :default-expand-all=\"props.dataSource.treeDefaultExpandAll ?? false\" :max-height=\"tableMaxHeight\"\r\n highlight-current-row>\r\n <el-table-column v-if=\"props.dataSource.showSelection\" type=\"selection\" width=\"60\" :selectable=\"isRowSelectable\"\r\n align=\"center\"></el-table-column>\r\n <el-table-column v-if=\"props.dataSource.showIndex\" type=\"index\" width=\"70\" align=\"center\"\r\n label=\"序號\"></el-table-column>\r\n <template v-for=\"item in props.dataSource.columns\" :key=\"item.prop || item.key\">\r\n <el-table-column v-if=\"item.width !== 0\" :label=\"$t(item.label)\" :width=\"calColumnWidth(item)\"\r\n :min-width=\"item.minWidth\" :prop=\"item.prop\" :align=\"calColumnAlign(item)\"\r\n :sortable=\"item.sort ? 'custom' : false\" :show-overflow-tooltip=\"item.showOverflowTooltip || true\"\r\n :fixed=\"item.fixed\">\r\n <template v-slot=\"{ row }\">\r\n <slot v-if=\"item.slot\" :name=\"item.slot\" :row=\"row\" :column=\"item\" :value=\"row[item.prop]\" />\r\n <ossImagePreview v-else-if=\"item.type === 'image' && row[item.prop]\" v-model=\"row[item.prop]\"\r\n class=\"image-preview\" :width=\"24\" :height=\"24\" />\r\n <view v-else-if=\"item.type === 'networkImage'\" class=\"image-style\">\r\n <el-image :src=\"row[item.prop]\" fit=\"cover\" style=\"height: 24px;\"> </el-image>\r\n </view>\r\n <view v-else-if=\"item.type === 'imageUrl'\" class=\"image-style\">\r\n <el-image :src=\"row[item.prop]\" fit=\"cover\" style=\"height: 24px;\"> </el-image>\r\n </view>\r\n <template v-else-if=\"item.type === 'date'\"> {{ parseTime(row[item.prop], '{y}-{m}-{d}') }}</template>\r\n <template v-else-if=\"item.type === 'timestamp'\"> {{ parseTime(row[item.prop]) }}</template>\r\n <el-checkbox v-else-if=\"item.type === 'bool'\" v-model=\"row[item.prop]\" @click.stop.prevent> </el-checkbox>\r\n <el-switch v-else-if=\"item.type === 'switch'\" v-model=\"row[item.prop]\"\r\n :active-value=\"item.activeValue || '0'\" :inactive-value=\"item.inactiveValue || '1'\" size=\"small\"\r\n :before-change=\"() => { return false }\">\r\n </el-switch>\r\n <div v-else-if=\"item.type === 'color'\" class=\"color-cell\">\r\n <div class=\"color-cell-item\" :style=\"{ backgroundColor: row[item.prop] || '#ffffff' }\"></div>\r\n </div>\r\n <template v-else-if=\"item.type === 'fromDict'\">{{ fromDbDicts(item, row) }}</template>\r\n <el-tag v-else-if=\"item.type === 'fromDataTag'\" :type=\"getTagType(item, row)\"\r\n :effect=\"item.tagEffect ?? 'light'\" class=\"el-column-tag\">{{ fromData(item, row)\r\n }}</el-tag>\r\n <el-tag v-else-if=\"item.type === 'localDict'\" :type=\"(item.tagType as any) || 'info'\"\r\n :effect=\"item.tagEffect || 'light'\" class=\"el-column-tag\">{{ fromLocalDicts(item,\r\n row)\r\n }}</el-tag>\r\n <el-tag v-else-if=\"item.type === 'tag'\" :type=\"(item.tagType as any) || 'info'\"\r\n :effect=\"item.tagEffect || 'light'\" class=\"el-column-tag\" plain>{{\r\n row[item.prop] }}</el-tag>\r\n <el-tag v-else-if=\"item.type === 'tagArrayObj'\" :type=\"(item.tagType as any) || 'info'\"\r\n :effect=\"item.tagEffect || 'light'\" class=\"el-column-tag\" v-for=\"(tag) in row[item.prop]\"\r\n :key=\"tag[item.displayField]\" plain>\r\n {{ tag[item.displayField] }}\r\n </el-tag>\r\n <el-tag v-else-if=\"item.type === 'tags'\" :type=\"(item.tagType as any) || 'info'\"\r\n :effect=\"item.tagEffect || 'light'\" class=\"el-column-tag\" v-for=\"tag in row[item.prop]\"\r\n :key=\"tag[item.tagLabel]\" plain>\r\n {{ tag[item.tagLabel] }}\r\n </el-tag>\r\n <dict-tag v-else-if=\"item.type === 'dictTag' || item.type === 'commonDictTag'\" :options=\"item.data || []\"\r\n :value=\"row[item.prop]\" />\r\n <svg-icon v-else-if=\"item.type === 'svg'\" :icon-class=\"row.icon\" />\r\n <oss-file v-else-if=\"item.type === 'ossFile'\" :data=\"row\" :width=\"32\" :height=\"32\" />\r\n <template v-else> {{ deepJsonProp(row, item) }} </template>\r\n </template>\r\n </el-table-column>\r\n </template>\r\n <el-table-column v-if=\"columnToolbars.length > 0\" align=\"center\" fixed=\"right\" :label=\"$t('操作')\"\r\n :width=\"columnToolbarWidth\">\r\n <template v-slot=\"{ row }\">\r\n <el-tooltip :content=\"$t(ac.text)\" placement=\"top\" v-for=\"ac in columnToolbars\" :key=\"(ac.action as any)\">\r\n <template v-if=\"buttonVisible(row, ac)\">\r\n <el-button link type=\"primary\" icon=\"Edit\" v-if=\"ac.action === 'update'\" @click=\"actionEvent(ac, row)\"\r\n :disabled=\"buttonDisabled(row, ac)\" v-hasPermi=\"ac.permission || null\" />\r\n <el-button link type=\"primary\" icon=\"Delete\" v-else-if=\"ac.action === 'delete'\"\r\n @click=\"actionEvent(ac, row)\" :disabled=\"buttonDisabled(row, ac)\" v-hasPermi=\"ac.permission || null\" />\r\n <el-button link type=\"primary\" icon=\"View\" v-else-if=\"ac.action === 'view' || ac.action === 'viewDetail'\"\r\n @click=\"actionEvent(ac, row)\" :disabled=\"buttonDisabled(row, ac)\" v-hasPermi=\"ac.permission || null\" />\r\n <el-button link type=\"primary\" v-else :icon=\"ac.icon\" @click=\"actionEvent(ac, row)\"\r\n :disabled=\"buttonDisabled(row, ac)\" v-hasPermi=\"ac.permission || null\" />\r\n </template>\r\n </el-tooltip>\r\n </template>\r\n </el-table-column>\r\n <template #empty>\r\n <div style=\"margin: 20px 0px;\">{{ $t('未查詢到數據') }}</div>\r\n </template>\r\n </el-table>\r\n <el-pagination v-if=\"props.dataSource.paging !== false\" class=\"paging\" background :layout=\"pagingLayout\"\r\n @size-change=\"handleSizeChange\" @current-change=\"handlePageChange\" :page-size=\"pageOptions.pageSize\"\r\n v-model:current-page=\"pageOptions.pageNum\" :total=\"pageOptions.total\" :page-sizes=\"listSize\">\r\n </el-pagination>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport ossImagePreview from \"@/components/OssImagePreview/index.vue\";\r\nimport OssFile from \"@/components/ossFile/index.vue\";\r\nimport { useTableData } from './composables/useTableData';\r\nimport { useDictData } from './composables/useDictData';\r\nimport { calculateColumnWidth, getDeepProperty, isButtonDisabled, isButtonVisible } from './utils';\r\nimport { LOCAL_DICTS, MIN_TOOLBAR_WIDTH, TOOLBAR_BUTTON_WIDTH } from './constants';\r\nimport type { DataSourceConfig, ActionConfig } from './types';\r\n\r\nconst { proxy } = getCurrentInstance() as ComponentInternalInstance;\r\n\r\nconst emits = defineEmits([\"actionEvent\", \"selectionChange\", \"rowClick\"]);\r\nconst props = withDefaults(defineProps<{\r\n dataSource: DataSourceConfig;\r\n /** @deprecated 請使用 dataSource.maxHeight */\r\n maxHeight?: number | string;\r\n}>(), {})\r\n\r\n/** 優先 dataSource.maxHeight,兼容舊版單獨傳 maxHeight */\r\nconst tableMaxHeight = computed(() => props.dataSource.maxHeight ?? props.maxHeight);\r\n\r\nconst curdTableRef = ref<any>();\r\n\r\n// 使用 composables\r\nconst {\r\n listData,\r\n loading,\r\n multipleSelection,\r\n pageOptions,\r\n listSize,\r\n pagingLayout,\r\n defaultSort,\r\n refreshTable,\r\n handlePageChange,\r\n handleSizeChange,\r\n handleSortChange,\r\n handleSelectionChange,\r\n initData,\r\n getQueryParams,\r\n} = useTableData(props.dataSource);\r\n\r\nconst { initColumnDicts } = useDictData();\r\n\r\nconst localDicts = ref(LOCAL_DICTS);\r\n\r\n\r\n\r\nconst getRowKeyValue = (row: any) => row?.[props.dataSource.rowKey];\r\n\r\nconst columnToolbars = computed(() => {\r\n if (!props.dataSource.actions) return [];\r\n return props.dataSource.actions.filter((item: ActionConfig) =>\r\n item.action !== 'create' &&\r\n item.action !== 'back' &&\r\n (!item.position || item.position === 'table')\r\n );\r\n});\r\n\r\nconst isRowSelectable = (row: any) => {\r\n return !props.dataSource.disabledCheckedRowKeys?.includes(getRowKeyValue(row));\r\n}\r\n\r\nconst columnToolbarWidth = computed(() => {\r\n const customWidth = props.dataSource.actionColumnWidth;\r\n if (customWidth !== undefined && customWidth !== null && customWidth !== '') {\r\n return typeof customWidth === 'number' ? `${customWidth}px` : customWidth;\r\n }\r\n let width = columnToolbars.value.length * TOOLBAR_BUTTON_WIDTH;\r\n if (width < MIN_TOOLBAR_WIDTH) width = MIN_TOOLBAR_WIDTH;\r\n return `${width + 40}px`;\r\n});\r\n\r\n/**\r\n * 操作按鈕隱藏顯示\r\n */\r\nconst buttonVisible = (data: any, action: any) => {\r\n return isButtonVisible(data, action);\r\n}\r\n\r\n/**\r\n * 操作按鈕隱藏顯示\r\n */\r\nconst buttonDisabled = (data: any, action: any) => {\r\n return isButtonDisabled(data, action);\r\n}\r\n\r\n/**\r\n * 自定義宽度\r\n */\r\nconst calColumnWidth = (item: any) => {\r\n return calculateColumnWidth(item);\r\n}\r\n\r\n/**\r\n *\r\n * @param item\r\n */\r\nconst calColumnAlign = (item: any) => {\r\n const align = item.align || props.dataSource.align\r\n if (align) return align;\r\n else if ((item.label?.indexOf('時間') > -1 ? 'center' : '')) return 'center';\r\n else if (item.type == 'dictTag' || item.type == 'image' || item.type == 'switch') return 'center';\r\n else return 'left'\r\n}\r\n\r\n/**\r\n * 单元格内容从字典表\r\n * @param {*} item\r\n * @param {*} row\r\n */\r\nconst fromDbDicts = (item: any, row: any) => {\r\n const dictData = proxy?.useDict(item.dictType)[item.dictType];\r\n if (dictData != null) {\r\n const data = row[item.prop];\r\n const res = dictData.filter((res) => res.value == data);\r\n if (res.length == 0) return \"\";\r\n else return res[0].label;\r\n } else console.error(item.dictType + \" 字典不存在\");\r\n}\r\n\r\n/**\r\n * 单元格内容从本地字典表\r\n * @param {*} item\r\n * @param {*} row\r\n */\r\nconst fromLocalDicts = (item: any, row: any) => {\r\n const dictData = localDicts.value[item.dictType];\r\n if (dictData != null) {\r\n const data = row[item.prop];\r\n const res = dictData.find((res: any) => res.value == data);\r\n if (res == null) return \"\";\r\n else return res.label;\r\n } else console.error(item.dictType + \" 字典不存在\");\r\n}\r\n\r\n/**\r\n * 從data數據中獲取數據\r\n * @param item\r\n * @param row\r\n */\r\nconst fromData = (item: any, row: any) => {\r\n if (item.data != null) {\r\n const data = row[item.prop];\r\n const res = item.data.find((res: any) => res.value == data);\r\n if (res == null) return \"\";\r\n else return res.label;\r\n } else console.error(\"未配置data屬性\");\r\n}\r\n\r\nconst getTagType = (item: any, row: any) => {\r\n if (item.data != null) {\r\n const data = row[item.prop];\r\n const res = item.data.find((res: any) => res.value == data);\r\n if (res == null) return \"info\";\r\n else return res.tagType;\r\n } else console.error(\"未配置data屬性\");\r\n}\r\n\r\n/**\r\n * 支持深度属性绑定\r\n */\r\nconst deepJsonProp = (data: any, item: any) => {\r\n return getDeepProperty(data, item);\r\n}\r\n\r\n/**\r\n * 选中变化时通知父组件\r\n */\r\nconst onSelectionChange = (val: any[]) => {\r\n const normalizedSelection = Array.isArray(val) ? val : [];\r\n handleSelectionChange(normalizedSelection);\r\n emits(\"selectionChange\", normalizedSelection);\r\n};\r\n\r\n/**\r\n * 行點擊事件\r\n */\r\nconst onRowClick = (row: any, column: any, event: Event) => props.dataSource.rowClick?.(row, column, event);\r\n\r\n/**\r\n * 操作栏按钮点击事件\r\n * @param {*} ev\r\n * @param {*} data\r\n */\r\nconst actionEvent = (ev: object, data: any) => emits(\"actionEvent\", ev, data)\r\n\r\n/**\r\n * 获取选中项\r\n */\r\nconst getSelection = () => {\r\n return multipleSelection.value;\r\n}\r\n\r\n/**\r\n * 初始化選擇行\r\n */\r\nconst applyDefaultSelectedRows = async () => {\r\n if (!props.dataSource.showSelection) return;\r\n if (!props.dataSource.checkedRowKeys?.length) return;\r\n await nextTick();\r\n\r\n const table = curdTableRef.value;\r\n if (!table?.toggleRowSelection) return;\r\n\r\n for (const row of listData.value) {\r\n const key = getRowKeyValue(row);\r\n if (key === undefined || key === null) continue;\r\n const keyStr = String(key);\r\n if (props.dataSource.checkedRowKeys?.includes(keyStr)) {\r\n table.toggleRowSelection(row, true);\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * 初始化禁用選擇行\r\n */\r\nconst applyDefaultDisabledSelectedRows = async () => {\r\n if (!props.dataSource.showSelection) return;\r\n if (!props.dataSource.disabledCheckedRowKeys?.length) return;\r\n await nextTick();\r\n\r\n const table = curdTableRef.value;\r\n if (!table?.toggleRowSelection) return;\r\n\r\n for (const row of listData.value) {\r\n const key = getRowKeyValue(row);\r\n if (key === undefined || key === null) continue;\r\n const keyStr = String(key);\r\n if (props.dataSource.disabledCheckedRowKeys?.includes(keyStr)) {\r\n table.toggleRowSelection(row, true);\r\n }\r\n }\r\n};\r\n\r\n\r\n\r\nconst initTableData = async () => {\r\n await initColumnDicts(props.dataSource.columns);\r\n await initData();\r\n await applyDefaultDisabledSelectedRows();\r\n await applyDefaultSelectedRows();\r\n}\r\n\r\nonMounted(() => initTableData());\r\ndefineExpose({ refreshTable, getSelection, initData: initTableData, getQueryParams })\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n.paging {\r\n padding: 12px 15px 15px 15px;\r\n font-size: 14px;\r\n // background-color: white;\r\n}\r\n\r\n.image-preview {\r\n display: flex;\r\n justify-content: center;\r\n}\r\n\r\n.el-switch .is-disabled {\r\n opacity: 1;\r\n}\r\n\r\n.image-style {\r\n display: flex;\r\n justify-content: center;\r\n}\r\n\r\n.tc-text {\r\n display: flex;\r\n text-overflow: ellipsis;\r\n line-clamp: 2;\r\n -webkit-line-clamp: 2;\r\n overflow: hidden;\r\n -webkit-box-orient: vertical;\r\n}\r\n\r\n.el-column-tag {\r\n margin-right: 2px;\r\n}\r\n\r\n.color-cell {\r\n display: flex;\r\n justify-content: center;\r\n\r\n &-item {\r\n width: 24px;\r\n height: 24px;\r\n border-radius: 2px;\r\n }\r\n}\r\n</style>\r\n","<template>\r\n <div class=\"upload-file\">\r\n <el-upload\r\n multiple\r\n :action=\"uploadFileUrl\"\r\n :before-upload=\"handleBeforeUpload\"\r\n :file-list=\"fileList\"\r\n :limit=\"limit\"\r\n :on-error=\"handleUploadError\"\r\n :on-exceed=\"handleExceed\"\r\n :on-success=\"handleUploadSuccess\"\r\n :show-file-list=\"false\"\r\n :headers=\"headers\"\r\n class=\"upload-file-uploader\"\r\n ref=\"fileUploadRef\"\r\n >\r\n <!-- 上传按钮 -->\r\n <el-button type=\"primary\">选取文件</el-button>\r\n </el-upload>\r\n <!-- 上传提示 -->\r\n <div class=\"el-upload__tip\" v-if=\"showTip\">\r\n 請上傳\r\n <template v-if=\"fileSize\">\r\n 大小不超過 <b style=\"color: #f56c6c\">{{ fileSize }}MB</b>\r\n </template>\r\n <template v-if=\"fileType\">\r\n 格式為 <b style=\"color: #f56c6c\">{{ fileType.join(\"/\") }}</b>\r\n </template>\r\n 的文件\r\n </div>\r\n <!-- 文件列表 -->\r\n <transition-group v-if=\"showFileList\" class=\"upload-file-list el-upload-list el-upload-list--text\" name=\"el-fade-in-linear\" tag=\"ul\">\r\n <li :key=\"file.uid\" class=\"el-upload-list__item ele-upload-list__item-content\" v-for=\"(file, index) in fileList\">\r\n <el-link :href=\"`${file.url}`\" :underline=\"false\" target=\"_blank\">\r\n <span class=\"el-icon-document\"> {{ getFileName(file.name) }} </span>\r\n </el-link>\r\n <div class=\"ele-upload-list__item-content-action\">\r\n <el-link :underline=\"false\" @click=\"handleDelete(index)\" type=\"danger\">删除</el-link>\r\n </div>\r\n </li>\r\n </transition-group>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { listByIds, delOss } from \"@/api/system/oss\";\r\nimport { propTypes } from '@/utils/propTypes';\r\nimport { getGlobalHeaders, getBaseUrl } from \"@/utils/http\";\r\n\r\nconst props = defineProps({\r\n modelValue: [String, Object, Array],\r\n // 数量限制\r\n limit: propTypes.number.def(5),\r\n // 大小限制(MB)\r\n fileSize: propTypes.number.def(5),\r\n // 文件类型, 例如['png', 'jpg', 'jpeg']\r\n fileType: propTypes.array.def([\"doc\", \"xls\", \"ppt\", \"txt\", \"pdf\"]),\r\n // 是否显示提示\r\n showTip: propTypes.bool.def(true),\r\n // 是否显示文件列表\r\n showFileList: propTypes.bool.def(true),\r\n});\r\n\r\nconst { proxy } = getCurrentInstance() as ComponentInternalInstance;\r\nconst emit = defineEmits(['update:modelValue']);\r\nconst number = ref(0);\r\nconst uploadList = ref<any[]>([]);\r\n\r\nconst baseUrl = getBaseUrl();\r\nconst uploadFileUrl = ref(baseUrl + \"/resource/oss/upload\"); // 上传文件服务器地址\r\nconst headers = ref(getGlobalHeaders());\r\n\r\nconst fileList = ref<any[]>([]);\r\nconst showTip = computed(\r\n () => props.showTip && (props.fileType || props.fileSize)\r\n);\r\n\r\nconst fileUploadRef = ref<ElUploadInstance>();\r\n\r\nwatch(() => props.modelValue, async val => {\r\n if (val) {\r\n if (!props.showFileList) return;\r\n let temp = 1;\r\n // 首先将值转为数组\r\n let list = [];\r\n if (Array.isArray(val)) {\r\n list = val;\r\n } else {\r\n const res = await listByIds(val as string)\r\n list = res.data.map((oss) => {\r\n const data = { name: oss.originalName, url: oss.url, ossId: oss.ossId };\r\n return data;\r\n });\r\n }\r\n // 然后将数组转为对象数组\r\n fileList.value = list.map(item => {\r\n item = { name: item.name, url: item.url, ossId: item.ossId };\r\n item.uid = item.uid || new Date().getTime() + temp++;\r\n return item;\r\n });\r\n } else {\r\n fileList.value = [];\r\n return [];\r\n }\r\n}, { deep: true, immediate: true });\r\n\r\n// 上传前校检格式和大小\r\nconst handleBeforeUpload = (file: any) => {\r\n // 校检文件类型\r\n if (props.fileType.length) {\r\n const fileName = file.name.split('.');\r\n const fileExt = fileName[fileName.length - 1];\r\n const isTypeOk = props.fileType.indexOf(fileExt) >= 0;\r\n if (!isTypeOk) {\r\n proxy?.$modal.msgError(`文件格式不正確, 請上傳${props.fileType.join(\"/\")}格式文件!`);\r\n return false;\r\n }\r\n }\r\n // 校检文件大小\r\n if (props.fileSize) {\r\n const isLt = file.size / 1024 / 1024 < props.fileSize;\r\n if (!isLt) {\r\n proxy?.$modal.msgError(`上傳文件大小不超過 ${props.fileSize} MB!`);\r\n return false;\r\n }\r\n }\r\n proxy?.$modal.loading(\"正在上傳文件,請稍等...\");\r\n number.value++;\r\n return true;\r\n}\r\n\r\n// 文件个数超出\r\nconst handleExceed = () => {\r\n proxy?.$modal.msgError(`上傳文件數量不能超過 ${props.limit} 个!`);\r\n}\r\n\r\n// 上传失败\r\nconst handleUploadError = () => {\r\n proxy?.$modal.msgError(\"上傳文件失敗\");\r\n}\r\n\r\n// 上传成功回调\r\nconst handleUploadSuccess = (res: any, file: UploadFile) => {\r\n if (res.code === 200) {\r\n uploadList.value.push({ name: res.data.fileName, url: res.data.url, ossId: res.data.ossId });\r\n uploadedSuccessfully();\r\n } else {\r\n number.value--;\r\n proxy?.$modal.closeLoading();\r\n proxy?.$modal.msgError(res.msg);\r\n fileUploadRef.value?.handleRemove(file);\r\n uploadedSuccessfully();\r\n }\r\n}\r\n\r\n// 删除文件\r\nconst handleDelete = (index: number) => {\r\n const ossId = fileList.value[index].ossId;\r\n delOss(ossId);\r\n fileList.value.splice(index, 1);\r\n emit(\"update:modelValue\", listToString(fileList.value));\r\n}\r\n\r\n// 上传结束处理\r\nconst uploadedSuccessfully = () => {\r\n if (number.value > 0 && uploadList.value.length === number.value) {\r\n fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value);\r\n uploadList.value = [];\r\n number.value = 0;\r\n emit(\"update:modelValue\", listToString(fileList.value));\r\n proxy?.$modal.closeLoading();\r\n }\r\n}\r\n\r\n// 获取文件名称\r\nconst getFileName = (name: string) => {\r\n // 如果是url那么取最后的名字 如果不是直接返回\r\n if (name.lastIndexOf(\"/\") > -1) {\r\n return name.slice(name.lastIndexOf(\"/\") + 1);\r\n } else {\r\n return name;\r\n }\r\n}\r\n\r\n// 对象转成指定字符串分隔\r\nconst listToString = (list: any[], separator?: string) => {\r\n let strs = \"\";\r\n separator = separator || \",\";\r\n list.forEach(item => {\r\n if (item.ossId) {\r\n strs += item.ossId + separator;\r\n }\r\n })\r\n return strs != \"\" ? strs.substring(0, strs.length - 1) : \"\";\r\n}\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n.upload-file-uploader {\r\n margin-bottom: 5px;\r\n}\r\n\r\n.upload-file-list .el-upload-list__item {\r\n border: 1px solid #e4e7ed;\r\n line-height: 2;\r\n margin-bottom: 10px;\r\n position: relative;\r\n padding-left: 10px;\r\n}\r\n\r\n.upload-file-list .ele-upload-list__item-content {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n color: inherit;\r\n}\r\n\r\n.ele-upload-list__item-content-action .el-link {\r\n margin-right: 10px;\r\n}\r\n</style>\r\n","<template>\r\n <div>\r\n <el-upload\r\n ref=\"imageUploadRef\"\r\n multiple\r\n :action=\"uploadImgUrl\"\r\n list-type=\"picture-card\"\r\n :on-success=\"handleUploadSuccess\"\r\n :before-upload=\"handleBeforeUpload\"\r\n :limit=\"limit\"\r\n :accept=\"fileAccept\"\r\n :on-error=\"handleUploadError\"\r\n :on-exceed=\"handleExceed\"\r\n :before-remove=\"handleDelete\"\r\n :show-file-list=\"true\"\r\n :headers=\"headers\"\r\n :file-list=\"fileList\"\r\n :on-preview=\"handlePictureCardPreview\"\r\n :class=\"{ hide: fileList.length >= limit }\"\r\n >\r\n <el-icon class=\"avatar-uploader-icon\">\r\n <plus />\r\n </el-icon>\r\n </el-upload>\r\n <!-- 上傳提示 -->\r\n <div v-if=\"showTip\" class=\"el-upload__tip\">\r\n 請上傳\r\n <template v-if=\"fileSize\">\r\n 大小不超過 <b style=\"color: #f56c6c\">{{ fileSize }}MB</b>\r\n </template>\r\n <template v-if=\"fileType\">\r\n <template v-if=\"fileSize\">、</template>格式為 <b style=\"color: #f56c6c\">{{ fileType.join('/') }}</b>\r\n </template>\r\n <template v-if=\"imageSize\">\r\n <template v-if=\"fileSize || fileType\">。</template>建議尺寸 <b style=\"color: #f56c6c\">{{ imageSize }}</b>\r\n </template>\r\n 的文件\r\n </div>\r\n\r\n <el-dialog v-model=\"dialogVisible\" title=\"預覽\" width=\"800px\" append-to-body>\r\n <img :src=\"dialogImageUrl\" style=\"display: block; max-width: 100%; margin: 0 auto\" />\r\n </el-dialog>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { listByIds, delOss } from '@/api/system/oss';\r\nimport { OssVO } from '@/api/system/oss/types';\r\nimport { propTypes } from '@/utils/propTypes';\r\nimport { getGlobalHeaders, getBaseUrl } from '@/utils/http';\r\nimport { compressAccurately } from 'image-conversion';\r\n\r\nconst props = defineProps({\r\n modelValue: {\r\n type: [String, Object, Array],\r\n default: () => []\r\n },\r\n // 圖片數量限制\r\n limit: propTypes.number.def(5),\r\n // 大小限制(MB)\r\n fileSize: propTypes.number.def(5),\r\n // 文件類型, 例如['png', 'jpg', 'jpeg']\r\n fileType: propTypes.array.def(['png', 'jpg', 'jpeg']),\r\n // 是否顯示提示\r\n isShowTip: {\r\n type: Boolean,\r\n default: true\r\n },\r\n // 建議圖片尺寸,如 \"200×200px\"\r\n imageSize: propTypes.string.def(''),\r\n // 是否支援壓縮,預設否\r\n compressSupport: {\r\n type: Boolean,\r\n default: false\r\n },\r\n // 壓縮目標大小,單位KB。預設300KB以上文件才壓縮,並壓縮至300KB以內\r\n compressTargetSize: propTypes.number.def(300),\r\n // 自訂高度\r\n height: propTypes.string.def('148px')\r\n});\r\n\r\nconst { proxy } = getCurrentInstance() as ComponentInternalInstance;\r\nconst emit = defineEmits(['update:modelValue']);\r\nconst number = ref(0);\r\nconst uploadList = ref<any[]>([]);\r\nconst dialogImageUrl = ref('');\r\nconst dialogVisible = ref(false);\r\n\r\nconst baseUrl = getBaseUrl();\r\nconst uploadImgUrl = ref(baseUrl + '/resource/oss/upload'); // 上傳的圖片伺服器地址\r\nconst headers = ref(getGlobalHeaders());\r\n\r\nconst fileList = ref<any[]>([]);\r\nconst showTip = computed(() => props.isShowTip && (props.fileType || props.fileSize || props.imageSize));\r\n\r\nconst imageUploadRef = ref<ElUploadInstance>();\r\n\r\n// 監聽 fileType 變化,更新 fileAccept\r\nconst fileAccept = computed(() => props.fileType.map((type) => `.${type}`).join(','));\r\n\r\nwatch(\r\n () => props.modelValue,\r\n async (val: string) => {\r\n if (val) {\r\n // 首先將值轉為數組\r\n let list: OssVO[] = [];\r\n if (Array.isArray(val)) {\r\n list = val as OssVO[];\r\n } else {\r\n const res = await listByIds(val);\r\n list = res.data;\r\n }\r\n // 然後將數組轉為對象數組\r\n fileList.value = list.map((item) => {\r\n // 字符串回顯處理 如果此處存的是url可直接回顯 如果存的是id需要調用接口查出來\r\n let itemData;\r\n if (typeof item === 'string') {\r\n itemData = { name: item, url: item };\r\n } else {\r\n // 此處name使用ossId 防止刪除出現重名\r\n itemData = { name: item.ossId, url: item.url, ossId: item.ossId };\r\n }\r\n return itemData;\r\n });\r\n } else {\r\n fileList.value = [];\r\n return [];\r\n }\r\n },\r\n { deep: true, immediate: true }\r\n);\r\n\r\n/** 上傳前loading加載 */\r\nconst handleBeforeUpload = (file: any) => {\r\n let isImg = false;\r\n if (props.fileType.length) {\r\n let fileExtension = '';\r\n if (file.name.lastIndexOf('.') > -1) {\r\n fileExtension = file.name.slice(file.name.lastIndexOf('.') + 1);\r\n }\r\n isImg = props.fileType.some((type: any) => {\r\n if (file.type.indexOf(type) > -1) return true;\r\n if (fileExtension && fileExtension.indexOf(type) > -1) return true;\r\n return false;\r\n });\r\n } else {\r\n isImg = file.type.indexOf('image') > -1;\r\n }\r\n if (!isImg) {\r\n proxy?.$modal.msgError(`文件格式不正確, 請上傳${props.fileType.join('/')}圖片格式文件!`);\r\n return false;\r\n }\r\n if (file.name.includes(',')) {\r\n proxy?.$modal.msgError('文件名不正確,不能包含英文逗號!');\r\n return false;\r\n }\r\n if (props.fileSize) {\r\n const isLt = file.size / 1024 / 1024 < props.fileSize;\r\n if (!isLt) {\r\n proxy?.$modal.msgError(`上傳圖片大小不能超過 ${props.fileSize} MB!`);\r\n return false;\r\n }\r\n }\r\n\r\n //壓縮圖片,開啟壓縮並且大於指定的壓縮大小時才壓縮\r\n if (props.compressSupport && file.size / 1024 > props.compressTargetSize) {\r\n proxy?.$modal.loading('正在上傳圖片,請稍候...');\r\n number.value++;\r\n return compressAccurately(file, props.compressTargetSize);\r\n } else {\r\n proxy?.$modal.loading('正在上傳圖片,請稍候...');\r\n number.value++;\r\n }\r\n};\r\n\r\n// 文件個數超出\r\nconst handleExceed = () => {\r\n proxy?.$modal.msgError(`上傳文件數量不能超過 ${props.limit} 個!`);\r\n};\r\n\r\n// 上傳成功回調\r\nconst handleUploadSuccess = (res: any, file: UploadFile) => {\r\n if (res.code === 200) {\r\n uploadList.value.push({ name: res.data.fileName, url: res.data.url, ossId: res.data.ossId });\r\n uploadedSuccessfully();\r\n } else {\r\n number.value--;\r\n proxy?.$modal.closeLoading();\r\n proxy?.$modal.msgError(res.msg);\r\n imageUploadRef.value?.handleRemove(file);\r\n uploadedSuccessfully();\r\n }\r\n};\r\n\r\n// 刪除圖片\r\nconst handleDelete = (file: UploadFile): boolean => {\r\n const index = fileList.value.map((f) => f.name).indexOf(file.name);\r\n if (index > -1 && uploadList.value.length === number.value) {\r\n const ossId = fileList.value[index].ossId;\r\n delOss(ossId);\r\n fileList.value.splice(index, 1);\r\n emit('update:modelValue', listToString(fileList.value));\r\n return false;\r\n }\r\n return true;\r\n};\r\n\r\n// 上傳結束處理\r\nconst uploadedSuccessfully = () => {\r\n if (number.value > 0 && uploadList.value.length === number.value) {\r\n fileList.value = fileList.value.filter((f) => f.url !== undefined).concat(uploadList.value);\r\n uploadList.value = [];\r\n number.value = 0;\r\n emit('update:modelValue', listToString(fileList.value));\r\n proxy?.$modal.closeLoading();\r\n }\r\n};\r\n\r\n// 上傳失敗\r\nconst handleUploadError = () => {\r\n proxy?.$modal.msgError('上傳圖片失敗');\r\n proxy?.$modal.closeLoading();\r\n};\r\n\r\n// 預覽\r\nconst handlePictureCardPreview = (file: any) => {\r\n dialogImageUrl.value = file.url;\r\n dialogVisible.value = true;\r\n};\r\n\r\n// 對象轉成指定字符串分隔\r\nconst listToString = (list: any[], separator?: string) => {\r\n let strs = '';\r\n separator = separator || ',';\r\n for (const i in list) {\r\n if (undefined !== list[i].ossId && list[i].url.indexOf('blob:') !== 0) {\r\n strs += list[i].ossId + separator;\r\n }\r\n }\r\n return strs != '' ? strs.substring(0, strs.length - 1) : '';\r\n};\r\n</script>\r\n\r\n<style lang=\"scss\" scoped>\r\n// .el-upload--picture-card 控制加號部分\r\n:deep(.hide .el-upload--picture-card) {\r\n display: none;\r\n}\r\n\r\n// 自定义高度样式\r\n:deep(.el-upload--picture-card) {\r\n width: v-bind('props.height');\r\n height: v-bind('props.height');\r\n line-height: v-bind('props.height');\r\n}\r\n\r\n:deep(.el-upload-list__item) {\r\n width: v-bind('props.height');\r\n height: v-bind('props.height');\r\n}\r\n</style>\r\n","import { getRequest } from '@/utils/http';\r\nimport { AxiosPromise } from 'axios';\r\nimport { TranslationVO, TranslationForm, TranslationQuery } from '@/api/app/translation/types';\r\n\r\n/**\r\n * 查询語言翻譯列表\r\n * @param query\r\n * @returns {*}\r\n */\r\n\r\nexport const listTranslation = (query?: TranslationQuery): AxiosPromise<TranslationVO[]> => {\r\n return getRequest()({\r\n url: '/app/translation/list',\r\n method: 'get',\r\n params: query\r\n });\r\n};\r\n\r\n/**\r\n * 查询語言翻譯详细\r\n * @param translationId\r\n */\r\nexport const getTranslation = (translationId: string | number): AxiosPromise<TranslationVO> => {\r\n return getRequest()({\r\n url: '/app/translation/' + translationId,\r\n method: 'get'\r\n });\r\n};\r\n\r\n/**\r\n * 查询語言翻譯根據名稱\r\n * @param translationId\r\n */\r\nexport const getTranslationByName = (name: string): AxiosPromise<TranslationVO> => {\r\n return getRequest()({\r\n url: '/app/translation/getByName',\r\n method: 'post',\r\n headers: {\r\n repeatSubmit: false\r\n },\r\n data: { name: name }\r\n });\r\n};\r\n\r\n/**\r\n * 新增語言翻譯\r\n * @param data\r\n */\r\nexport const addTranslation = (data: TranslationForm) => {\r\n return getRequest()({\r\n url: '/app/translation',\r\n method: 'post',\r\n data: data\r\n });\r\n};\r\n\r\n/**\r\n * 新增語言翻譯\r\n * @param data\r\n */\r\nexport const addTranslationBatch = (data: Array<TranslationForm>) => {\r\n return getRequest()({\r\n url: '/app/translation/batchAdd',\r\n method: 'post',\r\n data: data\r\n });\r\n};\r\n\r\n/**\r\n * 修改語言翻譯\r\n * @param data\r\n */\r\nexport const updateTranslation = (data: TranslationForm) => {\r\n return getRequest()({\r\n url: '/app/translation',\r\n method: 'put',\r\n data: data\r\n });\r\n};\r\n\r\n/**\r\n * 删除語言翻譯\r\n * @param translationId\r\n */\r\nexport const delTranslation = (translationId: string | number | Array<string | number>) => {\r\n return getRequest()({\r\n url: '/app/translation/' + translationId,\r\n method: 'delete'\r\n });\r\n};\r\n","import { TranslationVO } from '@/api/app/translation/types';\r\n\r\nexport const useTranslationStore = defineStore('translation', () => {\r\n const translation = ref<\r\n Array<{\r\n key: string;\r\n value: TranslationVO;\r\n }>\r\n >([]);\r\n\r\n /**\r\n * 获取語言\r\n * @param _key 語言key\r\n */\r\n const getTranslation = (_key: string): TranslationVO | null => {\r\n if (_key == null && _key == '') {\r\n return null;\r\n }\r\n try {\r\n for (let i = 0; i < translation.value.length; i++) {\r\n if (translation.value[i].key == _key) {\r\n return translation.value[i].value;\r\n }\r\n }\r\n } catch (e) {\r\n return null;\r\n }\r\n return null;\r\n };\r\n\r\n /**\r\n * 设置語言\r\n * @param _key 語言key\r\n * @param _value 語言value\r\n */\r\n const setTranslation = (_key: string, _value: TranslationVO) => {\r\n if (_key !== null && _key !== '') {\r\n translation.value.push({\r\n key: _key,\r\n value: _value\r\n });\r\n }\r\n };\r\n\r\n /**\r\n * 清除翻譯\r\n */\r\n const clearTranslation = () => {\r\n translation.value = [];\r\n };\r\n\r\n return {\r\n translation,\r\n getTranslation,\r\n setTranslation,\r\n clearTranslation\r\n };\r\n});\r\n\r\nexport default useTranslationStore;\r\n","import { addTranslationBatch, getTranslationByName } from '@/api/app/translation';\r\nimport { TranslationVO } from '@/api/app/translation/types';\r\nimport { useTranslationStore } from '@/store/modules/translation';\r\n/**\r\n * 获取翻譯数据\r\n */\r\nexport const useTranslation = async (name: string): Promise<{ [key: string]: TranslationVO }> => {\r\n const res = ref<{\r\n [key: string]: TranslationVO;\r\n }>({});\r\n const dataVo = useTranslationStore().getTranslation(name);\r\n if (dataVo) {\r\n res.value[name] = dataVo;\r\n } else {\r\n const { data } = await getTranslationByName(name);\r\n res.value[name] = data;\r\n useTranslationStore().setTranslation(name, data);\r\n }\r\n return res.value;\r\n};\r\n\r\n/**\r\n * 保存翻譯\r\n */\r\nexport const saveTranslation = async (translateInputRefs: any) => {\r\n if (translateInputRefs != null) {\r\n const list = <any>[];\r\n for (let i = 0; i < translateInputRefs.length; i++) {\r\n const result = translateInputRefs[i].getTranslate();\r\n if (list.some((item: any) => item.name === result.name)) {\r\n continue; // 如果已经存在同名翻译,则跳过\r\n }\r\n list.push(result);\r\n }\r\n await addTranslationBatch(list);\r\n useTranslationStore().clearTranslation();\r\n }\r\n};\r\n","<template>\r\n <div style=\"width: 100%;\">\r\n <div style=\"margin-bottom: 2px;\">\r\n <el-button\r\n size=\"small\"\r\n v-for=\"item in languages\"\r\n :key=\"item.id\"\r\n :type=\"item.selected ? 'primary' : 'default'\"\r\n @click=\"changeLanguage(item)\"\r\n style=\"margin-right: -8px;\"\r\n >{{\r\n item.name }}</el-button\r\n >\r\n </div>\r\n <template v-for=\"item in languages\" :key=\"item.id\">\r\n <el-input\r\n v-show=\"item.selected\"\r\n v-model=\"item.text\"\r\n :type=\"props.type\"\r\n :placeholder=\"props.placeholder + '(' + item.name + ')'\"\r\n :disabled=\"props.disabled\"\r\n :rows=\"props.rows\"\r\n :show-word-limit=\"props.showWordLimit\"\r\n :maxlength=\"props.maxlength\"\r\n @input=\"(text: any) => updateModelValue(item, text)\"\r\n @blur=\"zhHkOnBlur(item)\"\r\n @focus=\"setInputFocus()\"\r\n >\r\n </el-input>\r\n </template>\r\n </div>\r\n</template>\r\n<script setup lang=\"ts\">\r\nimport { useTranslation } from '@/utils/translation';\r\nconst emits = defineEmits([\"update:modelValue\", \"focus\"]);\r\nconst props = defineProps(['modelValue', 'placeholder', \"disabled\", \"type\", \"rows\", \"showWordLimit\", \"maxlength\"])\r\nconst languages = ref<any>([{ name: '繁體', id: 'zhHk', selected: true, text: \"\" }, { name: '簡體', id: 'zhCn', selected: false, text: '' }, { name: '英文', id: 'enUs', selected: false, text: '' }])\r\nconst loading = ref(false);\r\nconst hasInit = ref(false);\r\nconst updateModelValue = (item: any, text: any) => {\r\n item.text = text;\r\n if (item.id == \"zhHk\") {\r\n emits(\"update:modelValue\", text);\r\n }\r\n};\r\n\r\nconst zhHkOnBlur = async (item: any) => {\r\n if (item.id == \"zhHk\") {\r\n await getTranslateData()\r\n }\r\n}\r\n\r\n/**\r\n * 獲取翻譯查詢數據\r\n */\r\nconst getTranslateData = async () => {\r\n if (loading.value) return;\r\n const name = languages.value[0].text;\r\n if (name != '') {\r\n loading.value = true\r\n const data = await useTranslation(name);\r\n loading.value = false\r\n if (data[name] != null) {\r\n for (let i = 0; i < languages.value.length; i++) {\r\n if (!languages.value[i].text) {\r\n const languageTemp = languages.value[i]\r\n languageTemp.text = data[name][languageTemp.id];\r\n }\r\n }\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 切换语言\r\n */\r\nconst changeLanguage = async (lang: any) => {\r\n languages.value.map((res: any) => res.selected = false)\r\n lang.selected = true\r\n // await getTranslateData()\r\n}\r\n\r\n/**\r\n * 獲取翻譯\r\n */\r\nconst getTranslate = () => {\r\n const data = <any>{ name: languages.value[0].text };\r\n languages.value.forEach((element: { id: string | number; text: any; }) => data[element.id] = element.text);\r\n return data;\r\n}\r\n\r\nconst setInputFocus = () => emits(\"focus\");\r\n\r\nwatch(() => props.modelValue, (newVal) => {\r\n if (newVal) {\r\n const item = languages.value.filter((res: any) => res.selected)[0]\r\n item.text = newVal;\r\n if (!hasInit.value) {\r\n getTranslateData();\r\n }\r\n hasInit.value = true;\r\n }\r\n}, {\r\n immediate: true,\r\n deep: true\r\n});\r\n\r\ndefineExpose({ getTranslate })\r\n</script>\r\n<style scoped></style>\r\n","<template>\r\n <div>\r\n <el-upload\r\n v-if=\"type === 'url'\"\r\n :action=\"upload.url\"\r\n :before-upload=\"handleBeforeUpload\"\r\n :on-success=\"handleUploadSuccess\"\r\n :on-error=\"handleUploadError\"\r\n class=\"editor-img-uploader\"\r\n name=\"file\"\r\n :show-file-list=\"false\"\r\n :headers=\"upload.headers\"\r\n >\r\n <i ref=\"uploadRef\"></i>\r\n </el-upload>\r\n </div>\r\n <div class=\"editor\">\r\n <quill-editor\r\n ref=\"quillEditorRef\"\r\n v-model:content=\"content\"\r\n content-type=\"html\"\r\n :options=\"options\"\r\n :style=\"styles\"\r\n @text-change=\"(e: any) => $emit('update:modelValue', content)\"\r\n />\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport '@vueup/vue-quill/dist/vue-quill.snow.css';\r\n\r\nimport { QuillEditor, Quill } from '@vueup/vue-quill';\r\nimport { propTypes } from '@/utils/propTypes';\r\nimport { getGlobalHeaders, getBaseUrl } from '@/utils/http';\r\n\r\ndefineEmits(['update:modelValue']);\r\n\r\nconst props = defineProps({\r\n /* 编辑器的内容 */\r\n modelValue: propTypes.string,\r\n /* 高度 */\r\n height: propTypes.number.def(400),\r\n /* 最小高度 */\r\n minHeight: propTypes.number.def(400),\r\n /* 只读 */\r\n readOnly: propTypes.bool.def(false),\r\n /* 上传文件大小限制(MB) */\r\n fileSize: propTypes.number.def(5),\r\n /* 类型(base64格式、url格式) */\r\n type: propTypes.string.def('url')\r\n});\r\n\r\nconst { proxy } = getCurrentInstance() as ComponentInternalInstance;\r\n\r\nconst upload = reactive<UploadOption>({\r\n headers: getGlobalHeaders(),\r\n url: getBaseUrl() + '/resource/oss/upload'\r\n});\r\nconst quillEditorRef = ref();\r\nconst uploadRef = ref<HTMLDivElement>();\r\n\r\nconst options = ref<any>({\r\n theme: 'snow',\r\n bounds: document.body,\r\n debug: 'warn',\r\n modules: {\r\n // 工具栏配置\r\n toolbar: {\r\n container: [\r\n ['bold', 'italic', 'underline', 'strike'], // 加粗 斜体 下划线 删除线\r\n ['blockquote', 'code-block'], // 引用 代码块\r\n [{ list: 'ordered' }, { list: 'bullet' }], // 有序、无序列表\r\n [{ indent: '-1' }, { indent: '+1' }], // 缩进\r\n [{ size: ['small', false, 'large', 'huge'] }], // 字体大小\r\n [{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题\r\n [{ color: [] }, { background: [] }], // 字体颜色、字体背景颜色\r\n [{ align: [] }], // 对齐方式\r\n ['clean'], // 清除文本格式\r\n ['link', 'image', 'video'] // 链接、图片、视频\r\n ],\r\n handlers: {\r\n image: (value: boolean) => {\r\n if (value) {\r\n // 调用element图片上传\r\n uploadRef.value.click();\r\n } else {\r\n (Quill as any).format('image', true);\r\n }\r\n }\r\n }\r\n }\r\n },\r\n placeholder: '请输入内容',\r\n readOnly: props.readOnly\r\n});\r\n\r\nconst styles = computed(() => {\r\n const style: any = {};\r\n if (props.minHeight) {\r\n style.minHeight = `${props.minHeight}px`;\r\n }\r\n if (props.height) {\r\n style.height = `${props.height}px`;\r\n }\r\n return style;\r\n});\r\n\r\nconst content = ref('');\r\nwatch(\r\n () => props.modelValue,\r\n (v: string) => {\r\n if (v !== content.value) {\r\n content.value = v || '<p></p>';\r\n }\r\n },\r\n { immediate: true }\r\n);\r\n\r\n// 图片上传成功返回图片地址\r\nconst handleUploadSuccess = (res: any) => {\r\n // 如果上传成功\r\n if (res.code === 200) {\r\n // 获取富文本实例\r\n const quill = toRaw(quillEditorRef.value).getQuill();\r\n // 获取光标位置\r\n const length = quill.selection.savedRange.index;\r\n // 插入图片,res为服务器返回的图片链接地址\r\n quill.insertEmbed(length, 'image', res.data.url);\r\n // 调整光标到最后\r\n quill.setSelection(length + 1);\r\n proxy?.$modal.closeLoading();\r\n } else {\r\n proxy?.$modal.msgError('图片插入失败');\r\n proxy?.$modal.closeLoading();\r\n }\r\n};\r\n\r\n// 图片上传前拦截\r\nconst handleBeforeUpload = (file: any) => {\r\n const type = ['image/jpeg', 'image/jpg', 'image/png', 'image/svg'];\r\n const isJPG = type.includes(file.type);\r\n //检验文件格式\r\n if (!isJPG) {\r\n proxy?.$modal.msgError(`图片格式错误!`);\r\n return false;\r\n }\r\n // 校检文件大小\r\n if (props.fileSize) {\r\n const isLt = file.size / 1024 / 1024 < props.fileSize;\r\n if (!isLt) {\r\n proxy?.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`);\r\n return false;\r\n }\r\n }\r\n proxy?.$modal.loading('正在上传文件,请稍候...');\r\n return true;\r\n};\r\n\r\n// 图片失败拦截\r\nconst handleUploadError = (err: any) => {\r\n proxy?.$modal.msgError('上传文件失败');\r\n};\r\n</script>\r\n\r\n<style>\r\n.editor-img-uploader {\r\n display: none;\r\n}\r\n\r\n.editor,\r\n.ql-toolbar {\r\n white-space: pre-wrap !important;\r\n line-height: normal !important;\r\n}\r\n\r\n.quill-img {\r\n display: none;\r\n}\r\n\r\n.ql-snow .ql-tooltip[data-mode='link']::before {\r\n content: '请输入链接地址:';\r\n}\r\n\r\n.ql-snow .ql-tooltip.ql-editing a.ql-action::after {\r\n border-right: 0;\r\n content: '保存';\r\n padding-right: 0;\r\n}\r\n\r\n.ql-snow .ql-tooltip[data-mode='video']::before {\r\n content: '请输入视频地址:';\r\n}\r\n\r\n.ql-snow .ql-picker.ql-size .ql-picker-label::before,\r\n.ql-snow .ql-picker.ql-size .ql-picker-item::before {\r\n content: '14px';\r\n}\r\n\r\n.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='small']::before,\r\n.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='small']::before {\r\n content: '10px';\r\n}\r\n\r\n.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='large']::before,\r\n.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='large']::before {\r\n content: '18px';\r\n}\r\n\r\n.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='huge']::before,\r\n.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='huge']::before {\r\n content: '32px';\r\n}\r\n\r\n.ql-snow .ql-picker.ql-header .ql-picker-label::before,\r\n.ql-snow .ql-picker.ql-header .ql-picker-item::before {\r\n content: '文本';\r\n}\r\n\r\n.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='1']::before,\r\n.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='1']::before {\r\n content: '标题1';\r\n}\r\n\r\n.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='2']::before,\r\n.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='2']::before {\r\n content: '标题2';\r\n}\r\n\r\n.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='3']::before,\r\n.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='3']::before {\r\n content: '标题3';\r\n}\r\n\r\n.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='4']::before,\r\n.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='4']::before {\r\n content: '标题4';\r\n}\r\n\r\n.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='5']::before,\r\n.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='5']::before {\r\n content: '标题5';\r\n}\r\n\r\n.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='6']::before,\r\n.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='6']::before {\r\n content: '标题6';\r\n}\r\n\r\n.ql-snow .ql-picker.ql-font .ql-picker-label::before,\r\n.ql-snow .ql-picker.ql-font .ql-picker-item::before {\r\n content: '标准字体';\r\n}\r\n\r\n.ql-snow .ql-picker.ql-font .ql-picker-label[data-value='serif']::before,\r\n.ql-snow .ql-picker.ql-font .ql-picker-item[data-value='serif']::before {\r\n content: '衬线字体';\r\n}\r\n\r\n.ql-snow .ql-picker.ql-font .ql-picker-label[data-value='monospace']::before,\r\n.ql-snow .ql-picker.ql-font .ql-picker-item[data-value='monospace']::before {\r\n content: '等宽字体';\r\n}\r\n</style>\r\n","// 日期格式化\r\nexport function parseTime(time: any, pattern?: string) {\r\n if (arguments.length === 0 || !time) {\r\n return null;\r\n }\r\n const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}';\r\n let date;\r\n if (typeof time === 'object') {\r\n date = time;\r\n } else {\r\n if (typeof time === 'string' && /^[0-9]+$/.test(time)) {\r\n time = parseInt(time);\r\n } else if (typeof time === 'string') {\r\n time = time\r\n .replace(new RegExp(/-/gm), '/')\r\n .replace('T', ' ')\r\n .replace(new RegExp(/\\.[\\d]{3}/gm), '');\r\n }\r\n if (typeof time === 'number' && time.toString().length === 10) {\r\n time = time * 1000;\r\n }\r\n date = new Date(time);\r\n }\r\n const formatObj: { [key: string]: any } = {\r\n y: date.getFullYear(),\r\n m: date.getMonth() + 1,\r\n d: date.getDate(),\r\n h: date.getHours(),\r\n i: date.getMinutes(),\r\n s: date.getSeconds(),\r\n a: date.getDay()\r\n };\r\n return format.replace(/{(y|m|d|h|i|s|a)+}/g, (result: string, key: string) => {\r\n let value = formatObj[key];\r\n // Note: getDay() returns 0 on Sunday\r\n if (key === 'a') {\r\n return ['日', '一', '二', '三', '四', '五', '六'][value];\r\n }\r\n if (result.length > 0 && value < 10) {\r\n value = '0' + value;\r\n }\r\n return value || 0;\r\n });\r\n}\r\n\r\n/**\r\n * 添加日期范围\r\n * @param params\r\n * @param dateRange\r\n * @param propName\r\n */\r\nexport const addDateRange = (params: any, dateRange: any[], propName?: string) => {\r\n const search = params;\r\n search.params = typeof search.params === 'object' && search.params !== null && !Array.isArray(search.params) ? search.params : {};\r\n dateRange = Array.isArray(dateRange) ? dateRange : [];\r\n if (dateRange[0] && dateRange[0].length == 10) {\r\n dateRange[0] = dateRange[0] + ' 00:00:00';\r\n dateRange[1] = dateRange[1] + ' 23:59:59';\r\n }\r\n if (typeof propName === 'undefined') {\r\n search.params['beginTime'] = dateRange[0];\r\n search.params['endTime'] = dateRange[1];\r\n } else {\r\n propName = propName.charAt(0).toUpperCase() + propName.slice(1);\r\n search.params['begin' + propName] = dateRange[0];\r\n search.params['end' + propName] = dateRange[1];\r\n }\r\n return search;\r\n};\r\n\r\n// 回显数据字典\r\nexport const selectDictLabel = (datas: any, value: number | string) => {\r\n if (value === undefined) {\r\n return '';\r\n }\r\n const actions: Array<string | number> = [];\r\n Object.keys(datas).some((key) => {\r\n if (datas[key].value == '' + value) {\r\n actions.push(datas[key].label);\r\n return true;\r\n }\r\n });\r\n if (actions.length === 0) {\r\n actions.push(value);\r\n }\r\n return actions.join('');\r\n};\r\n\r\n// 回显数据字典(字符串数组)\r\nexport const selectDictLabels = (datas: any, value: any, separator: any) => {\r\n if (value === undefined || value.length === 0) {\r\n return '';\r\n }\r\n if (Array.isArray(value)) {\r\n value = value.join(',');\r\n }\r\n const actions: any[] = [];\r\n const currentSeparator = undefined === separator ? ',' : separator;\r\n const temp = value.split(currentSeparator);\r\n Object.keys(value.split(currentSeparator)).some((val) => {\r\n let match = false;\r\n Object.keys(datas).some((key) => {\r\n if (datas[key].value == '' + temp[val]) {\r\n actions.push(datas[key].label + currentSeparator);\r\n match = true;\r\n }\r\n });\r\n if (!match) {\r\n actions.push(temp[val] + currentSeparator);\r\n }\r\n });\r\n return actions.join('').substring(0, actions.join('').length - 1);\r\n};\r\n\r\n// 字符串格式化(%s )\r\nexport function sprintf(str: string) {\r\n if (arguments.length !== 0) {\r\n let flag = true,\r\n i = 1;\r\n str = str.replace(/%s/g, function () {\r\n const arg = arguments[i++];\r\n if (typeof arg === 'undefined') {\r\n flag = false;\r\n return '';\r\n }\r\n return arg;\r\n });\r\n return flag ? str : '';\r\n }\r\n}\r\n\r\n// 转换字符串,undefined,null等转化为\"\"\r\nexport const parseStrEmpty = (str: any) => {\r\n if (!str || str == 'undefined' || str == 'null') {\r\n return '';\r\n }\r\n return str;\r\n};\r\n\r\n// 数据合并\r\nexport const mergeRecursive = (source: any, target: any) => {\r\n for (const p in target) {\r\n try {\r\n if (target[p].constructor == Object) {\r\n source[p] = mergeRecursive(source[p], target[p]);\r\n } else {\r\n source[p] = target[p];\r\n }\r\n } catch (e) {\r\n source[p] = target[p];\r\n }\r\n }\r\n return source;\r\n};\r\n\r\n/**\r\n * 构造树型结构数据\r\n * @param {*} data 数据源\r\n * @param {*} id id字段 默认 'id'\r\n * @param {*} parentId 父节点字段 默认 'parentId'\r\n * @param {*} children 孩子节点字段 默认 'children'\r\n */\r\nexport const handleTree = <T>(data: any[], id?: string, parentId?: string, children?: string): T[] => {\r\n const config: {\r\n id: string;\r\n parentId: string;\r\n childrenList: string;\r\n } = {\r\n id: id || 'id',\r\n parentId: parentId || 'parentId',\r\n childrenList: children || 'children'\r\n };\r\n\r\n const childrenListMap: any = {};\r\n const tree: T[] = [];\r\n for (const d of data) {\r\n const id = d[config.id];\r\n childrenListMap[id] = d;\r\n if (!d[config.childrenList]) {\r\n d[config.childrenList] = [];\r\n }\r\n }\r\n\r\n for (const d of data) {\r\n const parentId = d[config.parentId];\r\n const parentObj = childrenListMap[parentId];\r\n if (!parentObj) {\r\n tree.push(d);\r\n } else {\r\n parentObj[config.childrenList].push(d);\r\n }\r\n }\r\n return tree;\r\n};\r\n\r\n/**\r\n * 参数处理\r\n * @param {*} params 参数\r\n */\r\nexport const tansParams = (params: any) => {\r\n let result = '';\r\n for (const propName of Object.keys(params)) {\r\n const value = params[propName];\r\n const part = encodeURIComponent(propName) + '=';\r\n if (value !== null && value !== '' && typeof value !== 'undefined') {\r\n if (typeof value === 'object') {\r\n for (const key of Object.keys(value)) {\r\n if (value[key] !== null && value[key] !== '' && typeof value[key] !== 'undefined') {\r\n const params = propName + '[' + key + ']';\r\n const subPart = encodeURIComponent(params) + '=';\r\n result += subPart + encodeURIComponent(value[key]) + '&';\r\n }\r\n }\r\n } else {\r\n result += part + encodeURIComponent(value) + '&';\r\n }\r\n }\r\n }\r\n return result;\r\n};\r\n\r\n// 返回项目路径\r\nexport const getNormalPath = (p: string): string => {\r\n if (p.length === 0 || !p || p === 'undefined') {\r\n return p;\r\n }\r\n const res = p.replace('//', '/');\r\n if (res[res.length - 1] === '/') {\r\n return res.slice(0, res.length - 1);\r\n }\r\n return res;\r\n};\r\n\r\n// 验证是否为blob格式\r\nexport const blobValidate = (data: any) => {\r\n return data.type !== 'application/json';\r\n};\r\n\r\nexport default {\r\n handleTree\r\n};\r\n","import { parseTime } from '@/utils/ruoyi';\r\n\r\n/**\r\n * 表格时间格式化\r\n */\r\nexport const formatDate = (cellValue: string) => {\r\n if (cellValue == null || cellValue == '') return '';\r\n const date = new Date(cellValue);\r\n const year = date.getFullYear();\r\n const month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1;\r\n const day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate();\r\n const hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours();\r\n const minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes();\r\n const seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds();\r\n return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds;\r\n};\r\n\r\n/**\r\n * @param {number} time\r\n * @param {string} option\r\n * @returns {string}\r\n */\r\nexport const formatTime = (time: string, option: string) => {\r\n let t: number;\r\n if (('' + time).length === 10) {\r\n t = parseInt(time) * 1000;\r\n } else {\r\n t = +time;\r\n }\r\n const d: any = new Date(t);\r\n const now = Date.now();\r\n\r\n const diff = (now - d) / 1000;\r\n\r\n if (diff < 30) {\r\n return '刚刚';\r\n } else if (diff < 3600) {\r\n // less 1 hour\r\n return Math.ceil(diff / 60) + '分钟前';\r\n } else if (diff < 3600 * 24) {\r\n return Math.ceil(diff / 3600) + '小时前';\r\n } else if (diff < 3600 * 24 * 2) {\r\n return '1天前';\r\n }\r\n if (option) {\r\n return parseTime(t, option);\r\n } else {\r\n return d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分';\r\n }\r\n};\r\n\r\n/**\r\n * @param {string} url\r\n * @returns {Object}\r\n */\r\nexport const getQueryObject = (url: string) => {\r\n url = url == null ? window.location.href : url;\r\n const search = url.substring(url.lastIndexOf('?') + 1);\r\n const obj: { [key: string]: string } = {};\r\n const reg = /([^?&=]+)=([^?&=]*)/g;\r\n search.replace(reg, (rs, $1, $2) => {\r\n const name = decodeURIComponent($1);\r\n let val = decodeURIComponent($2);\r\n val = String(val);\r\n obj[name] = val;\r\n return rs;\r\n });\r\n return obj;\r\n};\r\n\r\n/**\r\n * @param {string} input value\r\n * @returns {number} output value\r\n */\r\nexport const byteLength = (str: string) => {\r\n // returns the byte length of an utf8 string\r\n let s = str.length;\r\n for (let i = str.length - 1; i >= 0; i--) {\r\n const code = str.charCodeAt(i);\r\n if (code > 0x7f && code <= 0x7ff) s++;\r\n else if (code > 0x7ff && code <= 0xffff) s += 2;\r\n if (code >= 0xdc00 && code <= 0xdfff) i--;\r\n }\r\n return s;\r\n};\r\n\r\n/**\r\n * @param {Array} actual\r\n * @returns {Array}\r\n */\r\nexport const cleanArray = (actual: Array<any>) => {\r\n const newArray: any[] = [];\r\n for (let i = 0; i < actual.length; i++) {\r\n if (actual[i]) {\r\n newArray.push(actual[i]);\r\n }\r\n }\r\n return newArray;\r\n};\r\n\r\n/**\r\n * @param {Object} json\r\n * @returns {Array}\r\n */\r\nexport const param = (json: any) => {\r\n if (!json) return '';\r\n return cleanArray(\r\n Object.keys(json).map((key) => {\r\n if (json[key] === undefined) return '';\r\n return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]);\r\n })\r\n ).join('&');\r\n};\r\n\r\n/**\r\n * @param {string} url\r\n * @returns {Object}\r\n */\r\nexport const param2Obj = (url: string) => {\r\n const search = decodeURIComponent(url.split('?')[1]).replace(/\\+/g, ' ');\r\n if (!search) {\r\n return {};\r\n }\r\n const obj: any = {};\r\n const searchArr = search.split('&');\r\n searchArr.forEach((v) => {\r\n const index = v.indexOf('=');\r\n if (index !== -1) {\r\n const name = v.substring(0, index);\r\n const val = v.substring(index + 1, v.length);\r\n obj[name] = val;\r\n }\r\n });\r\n return obj;\r\n};\r\n\r\n/**\r\n * @param {string} val\r\n * @returns {string}\r\n */\r\nexport const html2Text = (val: string) => {\r\n const div = document.createElement('div');\r\n div.innerHTML = val;\r\n return div.textContent || div.innerText;\r\n};\r\n\r\n/**\r\n * Merges two objects, giving the last one precedence\r\n * @param {Object} target\r\n * @param {(Object|Array)} source\r\n * @returns {Object}\r\n */\r\nexport const objectMerge = (target: any, source: any | any[]) => {\r\n if (typeof target !== 'object') {\r\n target = {};\r\n }\r\n if (Array.isArray(source)) {\r\n return source.slice();\r\n }\r\n Object.keys(source).forEach((property) => {\r\n const sourceProperty = source[property];\r\n if (typeof sourceProperty === 'object') {\r\n target[property] = objectMerge(target[property], sourceProperty);\r\n } else {\r\n target[property] = sourceProperty;\r\n }\r\n });\r\n return target;\r\n};\r\n\r\n/**\r\n * @param {HTMLElement} element\r\n * @param {string} className\r\n */\r\nexport const toggleClass = (element: HTMLElement, className: string) => {\r\n if (!element || !className) {\r\n return;\r\n }\r\n let classString = element.className;\r\n const nameIndex = classString.indexOf(className);\r\n if (nameIndex === -1) {\r\n classString += '' + className;\r\n } else {\r\n classString = classString.substring(0, nameIndex) + classString.substring(nameIndex + className.length);\r\n }\r\n element.className = classString;\r\n};\r\n\r\n/**\r\n * @param {string} type\r\n * @returns {Date}\r\n */\r\nexport const getTime = (type: string) => {\r\n if (type === 'start') {\r\n return new Date().getTime() - 3600 * 1000 * 24 * 90;\r\n } else {\r\n return new Date(new Date().toDateString());\r\n }\r\n};\r\n\r\n/**\r\n * @param {Function} func\r\n * @param {number} wait\r\n * @param {boolean} immediate\r\n * @return {*}\r\n */\r\nexport const debounce = (func: any, wait: number, immediate: boolean) => {\r\n let timeout: any, args: any, context: any, timestamp: any, result: any;\r\n\r\n const later = function () {\r\n // 据上一次触发时间间隔\r\n const last = +new Date() - timestamp;\r\n\r\n // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait\r\n if (last < wait && last > 0) {\r\n timeout = setTimeout(later, wait - last);\r\n } else {\r\n timeout = null;\r\n // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用\r\n if (!immediate) {\r\n result = func.apply(context, args);\r\n if (!timeout) context = args = null;\r\n }\r\n }\r\n };\r\n\r\n return (...args: any) => {\r\n context = this;\r\n timestamp = +new Date();\r\n const callNow = immediate && !timeout;\r\n // 如果延时不存在,重新设定延时\r\n if (!timeout) timeout = setTimeout(later, wait);\r\n if (callNow) {\r\n result = func.apply(context, args);\r\n context = args = null;\r\n }\r\n return result;\r\n };\r\n};\r\n\r\n/**\r\n * This is just a simple version of deep copy\r\n * Has a lot of edge cases bug\r\n * If you want to use a perfect deep copy, use lodash's _.cloneDeep\r\n * @param {Object} source\r\n * @returns {Object}\r\n */\r\nexport const deepClone = (source: any) => {\r\n if (!source && typeof source !== 'object') {\r\n throw new Error('error arguments', 'deepClone' as any);\r\n }\r\n const targetObj: any = source.constructor === Array ? [] : {};\r\n Object.keys(source).forEach((keys) => {\r\n if (source[keys] && typeof source[keys] === 'object') {\r\n targetObj[keys] = deepClone(source[keys]);\r\n } else {\r\n targetObj[keys] = source[keys];\r\n }\r\n });\r\n return targetObj;\r\n};\r\n\r\n/**\r\n * @param {Array} arr\r\n * @returns {Array}\r\n */\r\nexport const uniqueArr = (arr: any) => {\r\n return Array.from(new Set(arr));\r\n};\r\n\r\n/**\r\n * @returns {string}\r\n */\r\nexport const createUniqueString = (): string => {\r\n const timestamp = +new Date() + '';\r\n const num = (1 + Math.random()) * 65536;\r\n const randomNum = parseInt(num + '');\r\n return (+(randomNum + timestamp)).toString(32);\r\n};\r\n\r\n/**\r\n * Check if an element has a class\r\n * @param ele\r\n * @param {string} cls\r\n * @returns {boolean}\r\n */\r\nexport const hasClass = (ele: HTMLElement, cls: string): boolean => {\r\n return !!ele.className.match(new RegExp('(\\\\s|^)' + cls + '(\\\\s|$)'));\r\n};\r\n\r\n/**\r\n * Add class to element\r\n * @param ele\r\n * @param {string} cls\r\n */\r\nexport const addClass = (ele: HTMLElement, cls: string) => {\r\n if (!hasClass(ele, cls)) ele.className += ' ' + cls;\r\n};\r\n\r\n/**\r\n * Remove class from element\r\n * @param ele\r\n * @param {string} cls\r\n */\r\nexport const removeClass = (ele: HTMLElement, cls: string) => {\r\n if (hasClass(ele, cls)) {\r\n const reg = new RegExp('(\\\\s|^)' + cls + '(\\\\s|$)');\r\n ele.className = ele.className.replace(reg, ' ');\r\n }\r\n};\r\n\r\n/**\r\n * @param {string} path\r\n * @returns {Boolean}\r\n */\r\nexport const isExternal = (path: string) => {\r\n return /^(https?:|http?:|mailto:|tel:)/.test(path);\r\n};\r\n\r\nexport const guid = () => {\r\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {\r\n const r = (Math.random() * 16) | 0,\r\n v = c == 'x' ? r : (r & 0x3) | 0x8;\r\n return v.toString(16);\r\n });\r\n};\r\n\r\n//封装一个方法\r\nexport const getMonthAndDay = (dateStr: string): string => {\r\n // 方案1:优先用 Date 对象解析(兼容 ISO 标准格式)\r\n const date = new Date(dateStr);\r\n if (!isNaN(date.getTime())) {\r\n // 验证解析是否成功\r\n // 获取月份(Date.getMonth() 返回 0-11,需+1)\r\n const month = date.getMonth() + 1;\r\n // 获取日期\r\n const day = date.getDate();\r\n\r\n // 核心改动:小于10补零,使用padStart(2, '0')确保长度为2,不足补0\r\n const monthStr = month.toString().padStart(2, '0'); // 1→\"01\",12→\"12\"\r\n const dayStr = day.toString().padStart(2, '0'); // 2→\"02\",31→\"31\"\r\n\r\n return `${monthStr}-${dayStr}`;\r\n }\r\n return '';\r\n};\r\n","/**\n * 表单数据处理 Composable\n */\n\nimport { ref, getCurrentInstance, type ComponentInternalInstance } from 'vue';\nimport { deepClone } from '@/utils/index';\nimport type { ColumnConfig, DataSourceConfig, FormFieldType } from '../types';\n\nexport function useFormData(dataSource: DataSourceConfig, editData: any) {\n const { proxy } = getCurrentInstance() as ComponentInternalInstance;\n const formData = ref<any>({});\n\n /**\n * 根据列的类型获取表单组件默认值\n */\n const getFormComponentDefaultValue = async (column: ColumnConfig): Promise<any> => {\n const form = column.form;\n if (!form) return '';\n\n switch (form.type) {\n case 'checkboxGroup':\n case 'checkboxDict':\n return [];\n\n case 'treeSelectApi':\n if (form.api) {\n const res = await form.api({ pageNum: 1, pageSize: 1000, ...form.apiParams });\n form.data = res.rows || res.data;\n // 仅在未显式配置 defaultExpandedKeys 且允许自动展开时,默认展开第一级\n if (form.data && Array.isArray(form.data) && !Array.isArray(form.defaultExpandedKeys) && form.autoExpandFirstLevel !== false) {\n const props = form.props || { value: 'id', label: 'label', children: 'children' };\n const valueKey = form.valueKey || props.value || 'id';\n form.defaultExpandedKeys = form.data.map((item: any) => item[valueKey]);\n }\n }\n return '';\n case 'radioApi':\n case 'selectApi':\n if (form.api) {\n const res = await form.api({ pageNum: 1, pageSize: 1000, ...form.apiParams });\n form.data = res.rows || res.data;\n }\n return '';\n\n case 'checkboxCommonDict':\n form.data = await proxy?.useOneCommonDict(form.dictType);\n return [];\n\n case 'radioCommonDict':\n case 'selectCommonDict':\n form.data = await proxy?.useOneCommonDict(form.dictType);\n return '';\n\n case 'radioDict':\n case 'selectDict':\n form.data = await proxy?.useOneDict(form.dictType);\n return '';\n\n case 'daterange':\n case 'timerange':\n return [];\n\n default:\n return '';\n }\n };\n\n /**\n * 初始化表单数据\n */\n const initFormData = async (currentEditData?: any): Promise<void> => {\n let formDataTemp: any = {};\n const columns = dataSource.columns;\n\n // 使用傳入的 currentEditData 或原始的 editData\n const dataToUse = currentEditData !== undefined ? currentEditData : editData;\n\n // 初始化所有字段的默认值\n for (const column of columns) {\n const formProp = column.form?.prop || column.prop;\n formDataTemp[formProp] = await getFormComponentDefaultValue(column);\n }\n\n // 合併編輯數據到表單數據中\n if (dataToUse && typeof dataToUse === 'object') {\n const clonedEditData = deepClone(dataToUse);\n formDataTemp = { ...formDataTemp, ...clonedEditData };\n }\n\n // 执行自定义初始化\n if (dataSource.initEditData) {\n dataSource.initEditData(formDataTemp);\n }\n if (dataSource.beforeInitEdit) {\n dataSource.beforeInitEdit(formDataTemp);\n }\n\n // 初始化更新数据\n initUpdateFormData(formDataTemp);\n\n formData.value = formDataTemp;\n };\n\n /**\n * 初始化更新数据(处理特殊字段类型)\n */\n const initUpdateFormData = (data: any): void => {\n const columns = dataSource.columns;\n\n columns.forEach((column) => {\n const formType = column.form?.type;\n const prop = column.form?.prop || column.prop;\n\n switch (formType) {\n case 'checkboxCommonDict':\n if (Array.isArray(data[prop])) {\n data[prop] = data[prop].map((item: any) => item[column.form!.value!] || item);\n }\n break;\n\n case 'selectDict':\n case 'radioDict':\n case 'switch':\n data[prop] = data[prop] || column.form?.defaultValue;\n break;\n\n case 'daterange':\n case 'timerange':\n // 支持从 beginXxx/endXxx 或 \"start,end\" 字符串初始化\n const rangeBeginProp = (column.form as any)?.rangeBeginProp;\n const rangeEndProp = (column.form as any)?.rangeEndProp;\n if (rangeBeginProp && rangeEndProp && data[rangeBeginProp] && data[rangeEndProp]) {\n data[prop] = [data[rangeBeginProp], data[rangeEndProp]];\n } else if (typeof data[prop] === 'string' && data[prop].includes(',')) {\n data[prop] = data[prop].split(',');\n } else if (!data[prop] || !Array.isArray(data[prop])) {\n data[prop] = data[prop] || [];\n }\n break;\n }\n });\n };\n\n /**\n * 准备保存数据\n */\n const prepareSaveData = async (saveParams: any): Promise<any> => {\n const mergedParams = { ...formData.value, ...saveParams };\n\n // 展开 daterange/timerange 为 beginXxx/endXxx\n dataSource.columns.forEach((column) => {\n const formType = column.form?.type;\n const prop = column.form?.prop || column.prop;\n if ((formType === 'daterange' || formType === 'timerange') && Array.isArray(mergedParams[prop])) {\n const arr = mergedParams[prop];\n const rangeBeginProp = (column.form as any)?.rangeBeginProp;\n const rangeEndProp = (column.form as any)?.rangeEndProp;\n if (rangeBeginProp && rangeEndProp && arr.length >= 2) {\n mergedParams[rangeBeginProp] = arr[0];\n mergedParams[rangeEndProp] = arr[1];\n }\n }\n });\n\n // 删除排序字段\n delete mergedParams.orderByColumn;\n delete mergedParams.isAsc;\n\n // 执行自定义保存前处理\n if (dataSource.beforeSave) {\n const result = await dataSource.beforeSave(mergedParams);\n if (result === false) return false;\n if (result && typeof result === 'object') Object.assign(saveParams, result);\n else Object.assign(saveParams, mergedParams);\n } else {\n Object.assign(saveParams, mergedParams);\n }\n return true;\n };\n\n return {\n formData,\n initFormData,\n prepareSaveData\n };\n}\n","<template>\n <el-dialog\n :title=\"props.action.title || props.action.text\"\n v-model=\"dialogVisible\"\n @close=\"handleClose\"\n destroy-on-close\n append-to-bod\n :width=\"props.action.width || (formInline ? 750 : 550)\"\n class=\"curdEditDialog\"\n >\n <el-form\n :model=\"formData\"\n ref=\"curdFormRef\"\n :label-width=\"props.action.labelWidth || props.dataSource.formLabelWidth || 'auto'\"\n :label-position=\"props.action.labelPosition || 'right'\"\n class=\"p-l-3 p-r-3\"\n >\n <el-row :gutter=\"20\">\n <el-col :span=\"(item.form.inline === false) ? 24 : formInline ? 12 : 24\" v-for=\"item in formObjs\" :key=\"item.prop\" class=\"el-p\">\n <el-form-item\n :prop=\"item.form.prop || item.prop\"\n :label=\"$t(item.label)\"\n :name=\"item.form.prop || item.prop\"\n :rules=\"item.form.rules\"\n :required=\"item.form.required\"\n >\n <el-input-number\n v-if=\"item.form.type === 'number'\"\n v-model=\"formData[item.form.prop || item.prop]\"\n style=\"width: 100%\"\n :min=\"item.form.min\"\n :max=\"item.form.max\"\n :placeholder=\"$t('請輸入') + $t(item.label)\"\n />\n <el-select\n v-else-if=\"item.form.type === 'select'\"\n :multiple=\"item.form.multiple\"\n style=\"width: 100%\"\n v-model=\"formData[item.form.prop || item.prop]\"\n :placeholder=\"$t('請選擇') + $t(item.label)\"\n :filterable=\"item.form.filterable ?? true\"\n >\n <el-option\n v-for=\"option in item.form.data\"\n :key=\"option[item.form.value] || option.value\"\n :label=\"option[item.form.label] || option.label\"\n :value=\"option[item.form.value] || option.value\"\n ></el-option>\n </el-select>\n <el-select\n v-else-if=\"item.form.type === 'selectDict'\"\n :multiple=\"item.form.multiple\"\n style=\"width: 100%\"\n v-model=\"formData[item.form.prop || item.prop]\"\n :placeholder=\"$t('請選擇') + $t(item.label)\"\n :filterable=\"item.form.filterable ?? true\"\n >\n <el-option\n v-for=\"option in item.form.data\"\n :key=\"option[item.form.value] || option.value\"\n :label=\"option[item.form.label] || option.label\"\n :value=\"option[item.form.value] || option.value\"\n ></el-option>\n </el-select>\n <el-select\n v-else-if=\"item.form.type === 'selectCommonDict'\"\n :multiple=\"item.form.multiple\"\n style=\"width: 100%\"\n v-model=\"formData[item.form.prop || item.prop]\"\n :placeholder=\"$t('請選擇') + $t(item.label)\"\n :filterable=\"item.form.filterable ?? true\"\n >\n <el-option v-for=\"option in item.form.data\" :key=\"option.value\" :label=\"option.label\" :value=\"option.value\"></el-option>\n </el-select>\n <el-select\n v-else-if=\"item.form.type === 'selectApi'\"\n :multiple=\"item.form.multiple\"\n style=\"width: 100%\"\n v-model=\"formData[item.form.prop || item.prop]\"\n :placeholder=\"$t('請選擇') + $t(item.label)\"\n :filterable=\"item.form.filterable ?? true\"\n >\n <el-option\n v-for=\"option in item.form.data\"\n :key=\"option[item.form.value]\"\n :label=\"option[item.form.label]\"\n :value=\"option[item.form.value]\"\n ></el-option>\n </el-select>\n <el-date-picker\n v-else-if=\"item.form.type === 'date'\"\n format=\"YYYY-MM-DD\"\n value-format=\"YYYY-MM-DD\"\n v-model=\"formData[item.form.prop || item.prop]\"\n type=\"date\"\n :placeholder=\"$t('請選擇日期')\"\n style=\"width: 100%\"\n />\n <el-date-picker\n v-else-if=\"item.form.type === 'datetime'\"\n format=\"YYYY-MM-DD HH:mm:ss\"\n value-format=\"YYYY-MM-DD HH:mm:ss\"\n v-model=\"formData[item.form.prop || item.prop]\"\n type=\"datetime\"\n :placeholder=\"$t('請選擇日期時間')\"\n style=\"width: 100%;\"\n />\n <el-date-picker\n v-else-if=\"item.form.type === 'daterange'\"\n v-model=\"formData[item.form.prop || item.prop]\"\n type=\"daterange\"\n format=\"YYYY-MM-DD\"\n value-format=\"YYYY-MM-DD\"\n :range-separator=\"$t('至')\"\n :start-placeholder=\"$t('開始日期')\"\n :end-placeholder=\"$t('結束日期')\"\n style=\"width: 100%\"\n />\n <el-date-picker\n v-else-if=\"item.form.type === 'timerange'\"\n v-model=\"formData[item.form.prop || item.prop]\"\n type=\"datetimerange\"\n format=\"YYYY-MM-DD HH:mm\"\n value-format=\"YYYY-MM-DD HH:mm:ss\"\n :range-separator=\"$t('至')\"\n :start-placeholder=\"$t('開始時間')\"\n :end-placeholder=\"$t('結束時間')\"\n style=\"width: 100%\"\n />\n <image-upload v-else-if=\"item.form.type === 'image'\" :limit=\"item.form.limit || 1\" v-model=\"formData[item.form.prop || item.prop]\" />\n <file-upload\n v-else-if=\"item.form.type === 'file'\"\n :limit=\"item.form.limit || 1\"\n v-model=\"formData[item.form.prop || item.prop]\"\n :fileType=\"item.form.fileType\"\n :fileSize=\"item.form.fileSize\"\n />\n <el-input\n v-else-if=\"item.form.type === 'password'\"\n auto-complete=\"new-password\"\n v-model=\"formData[item.form.prop || item.prop]\"\n type=\"password\"\n :placeholder=\"$t('請輸入') + $t(item.label)\"\n />\n <el-checkbox-group v-else-if=\"item.form.type === 'checkboxGroup'\" class=\"checkbox-group\" v-model=\"formData[item.form.prop || item.prop]\">\n <el-checkbox\n v-for=\"checkbox in item.form.list\"\n :key=\"checkbox[item.form.value]\"\n :label=\"checkbox[item.form.label]\"\n :value=\"checkbox[item.form.value]\"\n ></el-checkbox>\n </el-checkbox-group>\n <el-checkbox-group\n v-else-if=\"item.form.type === 'checkboxCommonDict'\"\n class=\"checkbox-group\"\n v-model=\"formData[item.form.prop || item.prop]\"\n >\n <el-checkbox v-for=\"ckb in item.form.data\" :label=\"ckb.value\" :key=\"ckb.value\" :value=\"ckb.value\"\n >{{\n ckb.label }}\n </el-checkbox>\n </el-checkbox-group>\n <el-switch\n v-else-if=\"item.form.type === 'switch'\"\n v-model=\"formData[item.form.prop || item.prop]\"\n :active-value=\"item.form.activeValue || '0'\"\n :inactive-value=\"item.form.inactiveValue || '1'\"\n >\n </el-switch>\n <el-radio-group v-else-if=\"item.form.type === 'radioDict'\" v-model=\"formData[item.form.prop || item.prop]\">\n <el-radio v-for=\"radio in item.form.data\" :key=\"radio.value\" :value=\"radio.value\">\n {{ radio.label\n }}</el-radio\n >\n </el-radio-group>\n <el-tree-select\n v-else-if=\"item.form.type === 'treeSelectApi'\"\n v-model=\"formData[item.form.prop || item.prop]\"\n :data=\"item.form.data\"\n :props=\"item.form.props\"\n :value-key=\"item.form.valueKey\"\n :default-expanded-keys=\"getTreeDefaultExpandedKeys(item.form)\"\n :filterable=\"item.form.filterable ?? true\"\n :placeholder=\"$t('請選擇') + $t(item.label)\"\n check-strictly\n />\n <el-radio-group v-else-if=\"item.form.type === 'radioCommonDict'\" v-model=\"formData[item.form.prop || item.prop]\">\n <el-radio v-for=\"radio in item.form.data\" :key=\"radio.value\" :label=\"radio.value\">\n {{ radio.label\n }}</el-radio\n >\n </el-radio-group>\n <el-radio-group v-else-if=\"item.form.type === 'radioLocalDict'\" v-model=\"formData[item.form.prop || item.prop]\">\n <el-radio v-for=\"radio in localDicts[item.form.dictType]\" :key=\"radio.value\" :label=\"radio.value\">\n {{\n radio.label }}</el-radio\n >\n </el-radio-group>\n <el-checkbox\n v-else-if=\"item.form.type === 'checkbox'\"\n :label=\"$t(item.label)\"\n v-model=\"formData[item.form.prop || item.prop]\"\n ></el-checkbox>\n <div v-else-if=\"item.form.type === 'color'\">\n <el-color-picker v-model=\"formData[item.form.prop || item.prop]\"></el-color-picker>\n <span class=\"color-picker-tips\">{{ $t('需点击选择器中的确定按钮') }}</span>\n </div>\n <el-input-number\n v-else-if=\"item.form.type === 'inputNumber'\"\n :min=\"item.form.min || 0\"\n v-model=\"formData[item.form.prop || item.prop]\"\n :step=\"1\"\n />\n <Editor\n v-else-if=\"item.form.type === 'richtext'\"\n v-model=\"formData[item.form.prop || item.prop]\"\n :height=\"item.form.max || 320\"\n :min-height=\"item.form.min || 220\"\n />\n <template v-else>\n <translate-input\n v-if=\"item.form.translate === true\"\n ref=\"translateInputRef\"\n :key=\"formData[dataSource.rowKey]\"\n v-model=\"formData[item.form.prop || item.prop]\"\n :disabled=\"item.form.disabled || false\"\n :type=\"item.form.type\"\n :placeholder=\"$t('請輸入') + $t(item.label)\"\n :rows=\"item.form.rows || 3\"\n :show-word-limit=\"item.form.showWordLimit || false\"\n :maxlength=\"item.form.limit || -1\"\n ></translate-input>\n <el-input\n v-else\n v-model=\"formData[item.form.prop || item.prop]\"\n :disabled=\"item.form.disabled || false\"\n :type=\"item.form.type\"\n :placeholder=\"$t('請輸入') + $t(item.label)\"\n :rows=\"item.form.rows || 3\"\n :show-word-limit=\"item.form.showWordLimit || false\"\n :maxlength=\"item.form.limit || -1\"\n >\n <template #suffix v-if=\"item.form.suffix\">\n {{ item.form.suffix }}\n </template>\n <template #prefix v-if=\"item.form.prefix\">\n {{ item.form.prefix }}\n </template>\n </el-input>\n </template>\n </el-form-item>\n </el-col>\n </el-row>\n </el-form>\n\n <template #footer>\n <div>\n <el-button @click=\"handleClose(false)\">{{ $t('關閉') }}</el-button>\n <el-button type=\"primary\" @click=\"handleSave\">{{ $t('存儲') }}</el-button>\n </div>\n </template>\n </el-dialog>\n</template>\n\n<script setup lang=\"ts\">\nimport { addTranslationBatch } from \"@/api/app/translation\";\nimport TranslateInput from '@/components/TranslateInput/index.vue';\nimport Editor from '@/components/Editor/index.vue';\nimport { useFormData } from './composables/useFormData';\nimport { initializeFormRules, filterEditableColumns, sortColumns } from './utils';\nimport { LOCAL_DICTS } from './constants';\nimport type { DataSourceConfig } from './types';\nimport { useI18n } from 'vue-i18n';\nconst { t } = useI18n();\n\nconst { proxy } = getCurrentInstance() as ComponentInternalInstance;\n\nconst props = defineProps<{\n dataSource: DataSourceConfig;\n editData: any;\n action: any;\n modelValue: boolean;\n}>()\n\nconst dialogVisible = ref<boolean>(false)\n\nconst translateInputRef = ref<any>();\nconst emits = defineEmits(['close', 'actionEvent']);\n\nconst curdFormRef = ref<any>();\nconst localDicts = ref(LOCAL_DICTS);\n\n// 使用 composable 处理表单数据\nconst { formData, initFormData: initFormDataComposable, prepareSaveData } = useFormData(\n props.dataSource,\n props.editData\n);\n\n/**\n * 生成编辑界面的组件\n */\nconst formObjs = computed(() => {\n const keyId = props.editData[props.dataSource.rowKey];\n const hasKeyId = !!keyId;\n const filteredColumns = filterEditableColumns(props.dataSource.columns, hasKeyId, hasKeyId);\n return sortColumns(filteredColumns, 'form.order');\n});\n\nconst formInline = computed(() => props.dataSource.formInline || false);\n\nconst getTreeDefaultExpandedKeys = (form: any) => {\n if (Array.isArray(form.defaultExpandedKeys)) {\n return form.defaultExpandedKeys;\n }\n if (form.autoExpandFirstLevel === false) {\n return [];\n }\n const data = form.data || [];\n const propsConfig = form.props || { value: 'id' };\n const valueKey = form.valueKey || propsConfig.value || 'id';\n return Array.isArray(data) ? data.map((item: any) => item[valueKey]).filter((v: any) => v !== undefined) : [];\n};\n\n/**\n * 初始化校驗規則\n */\nconst initFormRules = () => initializeFormRules(props.dataSource.columns)\n\n/**\n * 表单提交\n */\nconst handleSave = () => {\n curdFormRef.value?.validate(async (valid: boolean) => {\n if (!valid) return false;\n try {\n // 使用副本,避免污染 dataSource.queryParams 導致查詢時帶上編輯參數\n const saveParams = { ...(props.dataSource.queryParams || {}) };\n const result = await prepareSaveData(saveParams);\n if (result === false) return false;\n // 执行添加或更新\n if (props.editData[props.dataSource.rowKey]) {\n await props.dataSource.updateApi?.(saveParams);\n } else {\n await props.dataSource.addApi?.(saveParams);\n }\n await saveTranslation();\n handleClose(true);\n proxy?.$modal.msgSuccess(t('數據存儲成功'));\n curdFormRef.value?.resetFields();\n } catch (error) {\n console.error('Save failed:', error);\n }\n return false;\n });\n};\n\n/**\n * 保存翻譯\n */\nconst saveTranslation = async () => {\n const translateInputRefs = translateInputRef.value\n if (translateInputRefs != null) {\n const list = [];\n for (let i = 0; i < translateInputRefs.length; i++) {\n const result = translateInputRefs[i].getTranslate();\n list.push(result);\n }\n await addTranslationBatch(list);\n }\n}\n\n/**\n * 关闭弹窗\n */\nconst handleClose = (bRefTable = false) => {\n curdFormRef.value?.resetFields();\n emits('close', bRefTable);\n};\n\nwatch(() => props.modelValue, (newVal) => {\n if (newVal) {\n initFormRules();\n initFormDataComposable(props.editData);\n }\n dialogVisible.value = newVal;\n});\n</script>\n\n<style lang=\"scss\" scoped>\n.color-picker-tips {\n margin-left: 10px;\n color: #666;\n}\n\n:deep(.el-p .el-input__wrapper) {\n width: 100% !important;\n}\n</style>\n<style lang=\"scss\">\n.curdEditDialog .el-dialog__footer {\n padding-top: 0px !important;\n padding-right: 12px;\n}\n</style>\n","<template>\r\n <el-card v-if=\"searchColumns.length > 0 && showCard\" class=\"curd-search-card\" shadow=\"hover\"\r\n style=\"margin-bottom: 12px !important;\"\r\n :body-style=\"{ paddingTop: '18px !important', paddingBottom: computedPaddingBottom }\">\r\n <el-form :model=\"searchForm\" :inline=\"true\" :label-width=\"props.dataSource.searchLabelWidth || 68\">\r\n <el-row :gutter=\"20\">\r\n <el-col :span=\"columnSpan\" v-for=\"item in searchColumns\" :key=\"item.search.prop\">\r\n <el-form-item :label=\"$t(item.label)\" :name=\"item.search.prop\" style=\"width: 100%;\"\r\n :label-width=\"item.search.labelWidth\">\r\n <el-date-picker v-if=\"item.search.type === 'daterange'\" v-model=\"searchForm[item.search.prop]\"\r\n type=\"daterange\" format=\"YYYY-MM-DD\" value-format=\"YYYY-MM-DD\" :range-separator=\"$t('至')\"\r\n :start-placeholder=\"$t('開始日期')\" :end-placeholder=\"$t('結束日期')\" style=\"width: 100%;\" clearable\r\n @change=\"handleQuery\">\r\n </el-date-picker>\r\n <el-date-picker v-else-if=\"item.search.type === 'timerange'\" v-model=\"searchForm[item.search.prop]\"\r\n type=\"datetimerange\" format=\"YYYY-MM-DD HH:mm\" value-format=\"YYYY-MM-DD HH:mm:ss\"\r\n :range-separator=\"$t('至')\" :start-placeholder=\"$t('開始時間')\" :end-placeholder=\"$t('結束時間')\"\r\n style=\"width: 100%;\" clearable @change=\"handleQuery\">\r\n </el-date-picker>\r\n <el-select v-else-if=\"item.search.type === 'selectApi'\" v-model=\"searchForm[item.search.prop]\"\r\n :placeholder=\"$t('請選擇') + $t(item.label) + $t('查詢')\" style=\"width: 100%;\" clearable @change=\"handleQuery\"\r\n :filterable=\"item.search.filterable ?? true\">\r\n <el-option v-for=\"option in item.search.data\" :key=\"option[item.search.value]\"\r\n :label=\"option[item.search.label]\" :value=\"option[item.search.value]\"></el-option>\r\n </el-select>\r\n <el-select v-else-if=\"item.search.type === 'selectData'\" v-model=\"searchForm[item.search.prop]\"\r\n :placeholder=\"$t('請選擇') + $t(item.label) + $t('查詢')\" style=\"width: 100%;\" clearable @change=\"handleQuery\"\r\n :filterable=\"item.search.filterable ?? true\">\r\n <el-option v-for=\"option in item.search.data\" :key=\"option[item.search.value]\"\r\n :label=\"option[item.search.label]\" :value=\"option[item.search.value]\"></el-option>\r\n </el-select>\r\n\r\n <el-select v-else-if=\"item.search.type === 'selectDict'\" v-model=\"searchForm[item.search.prop]\"\r\n :placeholder=\"$t('請選擇') + $t(item.label) + $t('查詢')\" style=\"width: 100%;\" clearable @change=\"handleQuery\"\r\n :filterable=\"item.search.filterable ?? true\">\r\n <el-option v-for=\"option in item.search.data || []\" :key=\"option[item.search.value] || option.value\"\r\n :label=\"option[item.search.label] || option.label\" :value=\"option[item.search.value] || option.value\" />\r\n </el-select>\r\n <el-select v-else-if=\"item.search.type === 'selectCommonDict'\" v-model=\"searchForm[item.search.prop]\"\r\n :placeholder=\"$t('請選擇') + $t(item.label) + $t('查詢')\" style=\"width: 100%;\" clearable @change=\"handleQuery\"\r\n :filterable=\"item.search.filterable ?? true\">\r\n <el-option v-for=\"option in item.search.data || []\" :key=\"option[item.search.value] || option.value\"\r\n :label=\"option[item.search.label] || option.label\" :value=\"option[item.search.value] || option.value\" />\r\n </el-select>\r\n <el-tree-select v-else-if=\"item.search.type === 'treeSelectApi'\" v-model=\"searchForm[item.search.prop]\"\r\n :data=\"item.search.data\" :props=\"item.search.props\" :value-key=\"item.search.valueKey\"\r\n :default-expanded-keys=\"item.search.defaultExpandedKeys\"\r\n :placeholder=\"$t('請選擇') + $t(item.label) + $t('查詢')\" style=\"width: 100%;\" check-strictly clearable\r\n @change=\"handleQuery\" />\r\n <el-input v-else-if=\"item.search.type === 'number'\" type=\"number\" v-model=\"searchForm[item.search.prop]\"\r\n :placeholder=\"$t('請輸入') + $t(item.label) + $t('查詢')\" style=\"width: 100%;\" clearable />\r\n <el-input v-else type=\"text\" v-model=\"searchForm[item.search.prop]\" style=\"width: 100%;\"\r\n :placeholder=\"$t('請輸入') + $t(item.label) + $t('查詢')\" @keyup.enter=\"handleQuery\" clearable />\r\n </el-form-item>\r\n </el-col>\r\n <el-col :span=\"actionColSpan\">\r\n <el-button @click=\"handleQuery\" plain>{{ $t('查詢') }}</el-button>\r\n <el-button @click=\"handleClear\" plain>{{ $t('清空') }}</el-button>\r\n <el-button v-if=\"createAction\" v-hasPermi=\"createAction.permission || null\" :type=\"createAction.cssType\"\r\n :icon=\"createAction.icon\" plain @click=\"emitToolbarAction(createAction)\">{{ $t(createAction.text || '新增')\r\n }}</el-button>\r\n <el-button v-if=\"props.dataSource.showBatchDeleteButton\" type=\"danger\" icon=\"Delete\" plain\r\n :disabled=\"selectedCount === 0\" @click=\"emits('actionEvent', { action: 'batchDelete' })\"\r\n v-hasPermi=\"batchDeleteAction?.permission || null\">{{ $t('批量刪除') }}</el-button>\r\n <el-button v-if=\"props.dataSource.exportUrl\" type=\"primary\" icon=\"Download\" plain @click=\"handleExport\">{{\r\n props.dataSource.exportButtonName || $t('導出') }}</el-button>\r\n <el-button v-for=\"item in toolbarsWithoutCreate\" :key=\"item.text\" @click=\"emitToolbarAction(item)\"\r\n v-hasPermi=\"item.permission || null\" :type=\"item.cssType\" :icon=\"item.icon\" plain>{{ $t(item.text)\r\n }}</el-button>\r\n </el-col>\r\n </el-row>\r\n </el-form>\r\n </el-card>\r\n <div v-else-if=\"searchColumns.length > 0 && !showCard\" class=\"curd-search-card\"\r\n style=\"margin-bottom: 0px !important;\">\r\n <el-form :model=\"searchForm\" :inline=\"true\" :label-width=\"props.dataSource.searchLabelWidth || 68\">\r\n <el-row :gutter=\"20\">\r\n <el-col :span=\"columnSpan\" v-for=\"item in searchColumns\" :key=\"item.search.prop\">\r\n <el-form-item :label=\"item.label\" :name=\"item.search.prop\" style=\"width: 100%;\"\r\n :label-width=\"item.search.labelWidth\">\r\n <el-date-picker v-if=\"item.search.type === 'daterange'\" v-model=\"searchForm[item.search.prop]\"\r\n type=\"daterange\" format=\"YYYY-MM-DD\" value-format=\"YYYY-MM-DD\" :range-separator=\"$t('至')\"\r\n :start-placeholder=\"$t('開始日期')\" :end-placeholder=\"$t('結束日期')\" style=\"width: 100%;\" clearable\r\n @change=\"handleQuery\">\r\n </el-date-picker>\r\n <el-date-picker v-else-if=\"item.search.type === 'timerange'\" v-model=\"searchForm[item.search.prop]\"\r\n type=\"datetimerange\" format=\"YYYY-MM-DD HH:mm\" value-format=\"YYYY-MM-DD HH:mm:ss\"\r\n :range-separator=\"$t('至')\" :start-placeholder=\"$t('開始時間')\" :end-placeholder=\"$t('結束時間')\"\r\n style=\"width: 100%;\" clearable @change=\"handleQuery\">\r\n </el-date-picker>\r\n <el-select v-else-if=\"item.search.type === 'selectApi'\" v-model=\"searchForm[item.search.prop]\"\r\n :placeholder=\"$t('請選擇' + item.label + '查詢')\" style=\"width: 100%;\" clearable @change=\"handleQuery\"\r\n :filterable=\"item.search.filterable ?? true\">\r\n <el-option v-for=\"option in item.search.data\" :key=\"option[item.search.value]\"\r\n :label=\"option[item.search.label]\" :value=\"option[item.search.value]\"></el-option>\r\n </el-select>\r\n <el-select v-else-if=\"item.search.type === 'selectData'\" v-model=\"searchForm[item.search.prop]\"\r\n :placeholder=\"$t('請選擇' + item.label + '查詢')\" style=\"width: 100%;\" clearable @change=\"handleQuery\"\r\n :filterable=\"item.search.filterable ?? true\">\r\n <el-option v-for=\"option in item.search.data\" :key=\"option[item.search.value]\"\r\n :label=\"option[item.search.label]\" :value=\"option[item.search.value]\"></el-option>\r\n </el-select>\r\n\r\n <el-select v-else-if=\"item.search.type === 'selectDict'\" v-model=\"searchForm[item.search.prop]\"\r\n :placeholder=\"$t('請選擇' + item.label + '查詢')\" style=\"width: 100%;\" clearable @change=\"handleQuery\"\r\n :filterable=\"item.search.filterable ?? true\">\r\n <el-option v-for=\"option in item.search.data || []\" :key=\"option[item.search.value] || option.value\"\r\n :label=\"option[item.search.label] || option.label\" :value=\"option[item.search.value] || option.value\" />\r\n </el-select>\r\n <el-select v-else-if=\"item.search.type === 'selectCommonDict'\" v-model=\"searchForm[item.search.prop]\"\r\n :placeholder=\"$t('請選擇' + item.label + '查詢')\" style=\"width: 100%;\" clearable @change=\"handleQuery\"\r\n :filterable=\"item.search.filterable ?? true\">\r\n <el-option v-for=\"option in item.search.data || []\" :key=\"option[item.search.value] || option.value\"\r\n :label=\"option[item.search.label] || option.label\" :value=\"option[item.search.value] || option.value\" />\r\n </el-select>\r\n <el-tree-select v-else-if=\"item.search.type === 'treeSelectApi'\" v-model=\"searchForm[item.search.prop]\"\r\n :data=\"item.search.data\" :props=\"item.search.props\" :value-key=\"item.search.valueKey\"\r\n :default-expanded-keys=\"item.search.defaultExpandedKeys\" :placeholder=\"$t('請選擇' + item.label + '查詢')\"\r\n style=\"width: 100%;\" check-strictly clearable @change=\"handleQuery\" />\r\n <el-input v-else-if=\"item.search.type === 'number'\" type=\"number\" v-model=\"searchForm[item.search.prop]\"\r\n :placeholder=\"$t('請輸入' + item.label + '查詢')\" style=\"width: 100%;\" clearable />\r\n <el-input v-else type=\"text\" v-model=\"searchForm[item.search.prop]\" style=\"width: 100%;\"\r\n :placeholder=\"$t('請輸入' + item.label + '查詢')\" @keyup.enter=\"handleQuery\" clearable />\r\n </el-form-item>\r\n </el-col>\r\n <el-col :span=\"actionColSpan\">\r\n <el-button @click=\"handleQuery\" plain>{{ $t('查詢') }}</el-button>\r\n <el-button @click=\"handleClear\" plain>{{ $t('清空') }}</el-button>\r\n <el-button v-if=\"createAction\" v-hasPermi=\"createAction.permission || null\" :type=\"createAction.cssType\"\r\n :icon=\"createAction.icon\" plain @click=\"emitToolbarAction(createAction)\">{{ $t(createAction.text || '新增')\r\n }}</el-button>\r\n <el-button v-if=\"props.dataSource.showBatchDeleteButton\" type=\"danger\" icon=\"Delete\" plain\r\n :disabled=\"selectedCount === 0\" @click=\"emits('actionEvent', { action: 'batchDelete' })\"\r\n v-hasPermi=\"batchDeleteAction?.permission || null\">{{ $t('批量刪除') }}</el-button>\r\n <el-button v-if=\"props.dataSource.exportUrl\" type=\"primary\" icon=\"Download\" plain @click=\"handleExport\">{{\r\n $t('導出') }}</el-button>\r\n <el-button v-for=\"item in toolbarsWithoutCreate\" :key=\"item.text\" @click=\"emitToolbarAction(item)\"\r\n v-hasPermi=\"item.permission || null\" :type=\"item.cssType\" :icon=\"item.icon\" plain>{{ $t(item.text)\r\n }}</el-button>\r\n </el-col>\r\n </el-row>\r\n </el-form>\r\n </div>\r\n <div v-else-if=\"toolbars.length > 0\" class=\"curd-search-card\">\r\n <el-button v-if=\"createAction\" v-hasPermi=\"createAction.permission || null\" :type=\"createAction.cssType\"\r\n :icon=\"createAction.icon\" plain @click=\"emitToolbarAction(createAction)\">{{ createAction.text || '新增'\r\n }}</el-button>\r\n <el-button v-if=\"props.dataSource.showBatchDeleteButton\" type=\"danger\" icon=\"Delete\" plain\r\n :disabled=\"selectedCount === 0\" @click=\"emits('actionEvent', { action: 'batchDelete' })\"\r\n v-hasPermi=\"batchDeleteAction?.permission || null\">批量刪除</el-button>\r\n <el-button v-if=\"props.dataSource.exportUrl\" type=\"primary\" icon=\"Download\" plain @click=\"handleExport\">{{\r\n props.dataSource.exportButtonName || '導出' }}</el-button>\r\n <el-button v-for=\"item in toolbarsWithoutCreate\" :key=\"item.text\" @click=\"emitToolbarAction(item)\"\r\n v-hasPermi=\"item.permission || null\" :type=\"item.cssType\" :icon=\"item.icon\" plain>{{ $t(item.text)\r\n }}</el-button>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { useDictData } from './composables/useDictData';\r\nimport { sortColumns, deleteEmptyParams, formatDateRangeParams } from './utils';\r\nimport type { DataSourceConfig, ColumnConfig } from './types';\r\nimport dayjs from \"dayjs\"\r\nimport { useI18n } from 'vue-i18n';\r\nconst { t } = useI18n();\r\n\r\n\r\nconst { proxy } = getCurrentInstance() as ComponentInternalInstance;\r\nconst emits = defineEmits(['actionEvent']);\r\n\r\nconst props = withDefaults(defineProps<{\r\n dataSource: DataSourceConfig;\r\n /** 表格勾選數量,為 0 時批量刪除按鈕禁用 */\r\n selectedCount?: number;\r\n}>(), {\r\n selectedCount: 0\r\n});\r\n\r\n/** 頂部操作按鈕(來自 curdToolbar:position===top、create、back) */\r\nconst toolbars = computed(() => {\r\n const actions = props.dataSource.actions;\r\n if (actions == null) return [];\r\n const list = actions.filter((item: any) => item.position === 'top' || item.action === 'create' || item.action === 'back');\r\n return list.map((item: any) => {\r\n const t = { ...item };\r\n if (t.action === 'create') {\r\n t.cssType = 'primary';\r\n t.icon = 'Plus';\r\n } else if (t.action === 'back') {\r\n t.icon = 'ArrowLeft';\r\n }\r\n return t;\r\n });\r\n});\r\n\r\n/** 新增按鈕(單獨放批量刪除前) */\r\nconst createAction = computed(() => toolbars.value.find((item: any) => item.action === 'create') ?? null);\r\n///批量刪除按鈕\r\nconst batchDeleteAction = computed(() => props.dataSource.actions?.find((item: any) => item.action === 'delete') ?? null);\r\n/** 除新增外的其他頂部按鈕(放導出後面) */\r\nconst toolbarsWithoutCreate = computed(() => toolbars.value.filter((item: any) => item.action !== 'create'));\r\n\r\nconst emitToolbarAction = (item: any) => {\r\n emits('actionEvent', item);\r\n};\r\n\r\n/** 每行顯示的列數,默認 4 */\r\nconst columnsPerRow = computed(() => props.dataSource.searchColumnsPerRow ?? 4);\r\n\r\n/** 每列所佔的 span */\r\nconst columnSpan = computed(() => Math.floor(24 / columnsPerRow.value));\r\n\r\n/** 是否顯示卡片容器 */\r\nconst showCard = computed(() => props.dataSource.showSearchCard ?? true);\r\n\r\n/** 操作按鈕列所佔 span:優先使用配置值,否則根據最後一行的剩餘空間自動計算 */\r\nconst actionColSpan = computed(() => {\r\n // 如果配置了固定值,優先使用\r\n if (props.dataSource.searchActionColSpan != null) {\r\n return props.dataSource.searchActionColSpan;\r\n }\r\n // 否則自動計算\r\n const remainder = searchColumns.value.length % columnsPerRow.value;\r\n const lastRowColumns = remainder === 0 ? columnsPerRow.value : remainder;\r\n const rest = 24 - lastRowColumns * columnSpan.value;\r\n return rest <= 0 ? 24 : rest;\r\n});\r\n\r\n/** 計算底部內邊距:當最後一行不滿時為 0,否則為 15px */\r\nconst computedPaddingBottom = computed(() => {\r\n const remainder = searchColumns.value.length % columnsPerRow.value;\r\n return remainder === 0 ? '15px !important' : '0px !important';\r\n});\r\n\r\nconst { initSearchDicts } = useDictData();\r\n\r\nconst searchForm = ref<any>({});\r\nconst searchColumns = ref<any>([]);\r\n/**\r\n * 執行查詢\r\n */\r\nconst handleQuery = () => {\r\n emits(\"actionEvent\", { action: 'filter' })\r\n}\r\n\r\nconst handleExport = async () => {\r\n try {\r\n let queryParams = getQueryParams();\r\n queryParams = { ...(props.dataSource.queryParams || {}), ...queryParams };\r\n if (props.dataSource.beforeExport) {\r\n const result = await props.dataSource.beforeExport(queryParams);\r\n if (result === false) return;\r\n }\r\n const dateStr = dayjs().format(\"YYYYMMDDHHmmss\");\r\n let fileName = props.dataSource.exportFileName ? `${props.dataSource.exportFileName}` : `export`;\r\n fileName += `_${dateStr}.xlsx`;\r\n proxy?.download(props.dataSource.exportUrl, queryParams, fileName);\r\n } catch (error) {\r\n console.error('Export failed:', error);\r\n proxy?.$modal.msgError(t('導出失敗'));\r\n }\r\n}\r\n\r\n/**\r\n * 獲取查詢參數\r\n * @returns {Object} 查詢參數對象\r\n */\r\nconst getQueryParams = (): Record<string, any> => {\r\n // 直接使用 searchForm.value,避免创建多余的 params 对象\r\n const queryParams: Record<string, any> = { ...searchForm.value };\r\n\r\n // 处理日期/时间范围参数\r\n searchColumns.value.forEach((item: ColumnConfig) => {\r\n if (item.search?.type === 'daterange' || item.search?.type === 'timerange') {\r\n const dataTemp = queryParams[item.search.prop];\r\n if (dataTemp && dataTemp[0] && dataTemp[1]) {\r\n formatDateRangeParams(queryParams, dataTemp, item.search.prop, proxy);\r\n }\r\n delete queryParams[item.search.prop];\r\n }\r\n });\r\n return deleteEmptyParams(queryParams);\r\n}\r\n\r\n/**\r\n * 操作清空查詢條件\r\n */\r\nconst handleClear = async () => {\r\n await initData();\r\n emits(\"actionEvent\", { action: 'filter' }, {})\r\n}\r\n\r\n/**\r\n * 初始化数据\r\n */\r\nconst initData = async () => {\r\n // 先同步設置 searchColumns,讓搜索框立即顯示\r\n const searchColumnsTemp = props.dataSource.columns.filter((item: ColumnConfig) => item.search);\r\n searchColumns.value = sortColumns(\r\n searchColumnsTemp.map((col, i) => {\r\n if (!col.search.order) {\r\n col.search.order = i + 100;\r\n }\r\n return col;\r\n }),\r\n 'search.order'\r\n );\r\n\r\n // 異步初始化表單數據和字典(不阻塞搜索框顯示)\r\n await nextTick();\r\n const searchFormTemp: Record<string, any> = {};\r\n for (let i = 0; i < searchColumnsTemp.length; i++) {\r\n const column = searchColumnsTemp[i];\r\n if (!column.search) continue;\r\n\r\n const searchProp = column.search.prop || ''\r\n const searchType = column.search.type || ''\r\n\r\n // 根据类型初始化\r\n if (searchType === 'daterange' || searchType === 'timerange') {\r\n searchFormTemp[searchProp] = column.search.default ? column.search.default.split(',') : [];\r\n } else if (searchType === 'selectApi' || searchType === 'treeSelectApi' || searchType === 'selectDict') {\r\n await initSearchDicts(column);\r\n searchFormTemp[searchProp] = column.search.default;\r\n } else {\r\n searchFormTemp[searchProp] = column.search.default;\r\n }\r\n }\r\n searchForm.value = searchFormTemp;\r\n}\r\n\r\nonMounted(() => initData());\r\n\r\ndefineExpose({ initData, getQueryParams })\r\n</script>\r\n\r\n<style scoped lang=\"scss\">\r\n.curd-search-card {\r\n margin-bottom: 12px !important;\r\n\r\n /* 與表格內容字體、顏色保持一致 */\r\n :deep(.el-form-item__label) {\r\n font-weight: normal;\r\n font-size: 14px;\r\n color: var(--el-text-color-regular);\r\n }\r\n}\r\n\r\n.search {\r\n cursor: pointer;\r\n padding: 12px 14px 0px;\r\n border-radius: 4px;\r\n}\r\n</style>\r\n","<template>\r\n <div :class=\"{ 'p-3': showPadding }\">\r\n <curdSearch :dataSource=\"props.dataSource\" :selectedCount=\"selectedCount\" ref=\"curdSearchRef\"\r\n @actionEvent=\"actionEvent\"> </curdSearch>\r\n <el-card v-if=\"showCard\" :body-style=\"{ paddingBottom: props.dataSource.paging === false ? '20px' : '0px' }\"\r\n shadow=\"hover\">\r\n <curd-table ref=\"curdTableRef\" @actionEvent=\"actionEvent\" @selectionChange=\"onSelectionChange\"\r\n :dataSource=\"props.dataSource\" :maxHeight=\"legacyMaxHeight\">\r\n <template v-for=\"name in columnSlotNames\" :key=\"name\" #[name]=\"scope\">\r\n <slot :name=\"name\" v-bind=\"scope\" />\r\n </template>\r\n </curd-table>\r\n <curd-edit @close=\"closeEdit\" v-model=\"editDialogVisible\" :dataSource=\"props.dataSource\" :action=\"action\"\r\n :editData=\"editData\"></curd-edit>\r\n </el-card>\r\n <div v-else>\r\n <curd-table ref=\"curdTableRef\" @actionEvent=\"actionEvent\" @selectionChange=\"onSelectionChange\"\r\n :dataSource=\"props.dataSource\" :maxHeight=\"legacyMaxHeight\">\r\n <template v-for=\"name in columnSlotNames\" :key=\"name\" #[name]=\"scope\">\r\n <slot :name=\"name\" v-bind=\"scope\" />\r\n </template>\r\n </curd-table>\r\n <curd-edit @close=\"closeEdit\" v-model=\"editDialogVisible\" :dataSource=\"props.dataSource\" :action=\"action\"\r\n :editData=\"editData\"></curd-edit>\r\n </div>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport curdTable from '@/components/CURD/curdTable.vue';\r\nimport curdEdit from '@/components/CURD/curdEdit.vue';\r\nimport curdSearch from '@/components/CURD/curdSearch.vue';\r\nimport { initializeColumns } from './utils';\r\nimport type { DataSourceConfig, ActionEvent } from './types';\r\n\r\nconst { proxy } = getCurrentInstance() as ComponentInternalInstance;\r\n\r\nconst emits = defineEmits<{\r\n actionEvent: [ev: ActionEvent, data: any];\r\n beforeQuery: [params: any];\r\n}>();\r\n\r\nconst props = withDefaults(defineProps<{\r\n dataSource: DataSourceConfig;\r\n showCard?: boolean;\r\n showToolbar?: boolean;\r\n showPadding?: boolean;\r\n /** @deprecated 請使用 dataSource.maxHeight */\r\n maxHeight?: number | string,\r\n}>(), {\r\n showCard: true,\r\n showToolbar: true,\r\n showPadding: true,\r\n});\r\n\r\n/** 僅在頁面仍傳 :maxHeight 時作為兜底,dataSource.maxHeight 優先 */\r\nconst legacyMaxHeight = computed(() =>\r\n props.dataSource.maxHeight != null ? undefined : props.maxHeight\r\n);\r\n\r\nconst editDialogVisible = ref(false);\r\nconst editData = ref<any>({});\r\nconst action = ref<any>({});\r\nconst curdTableRef = ref<any>();\r\nconst curdSearchRef = ref<any>();\r\n/** 表格勾選數量,為 0 時批量刪除按鈕禁用 */\r\nconst selectedCount = ref(0);\r\n\r\nconst columnSlotNames = computed(() => {\r\n const names = (props.dataSource.columns || []).map((col) => col.slot).filter((name): name is string => !!name);\r\n return Array.from(new Set(names));\r\n});\r\n\r\nconst onSelectionChange = (val: any[]) => {\r\n selectedCount.value = Array.isArray(val) ? val.length : 0;\r\n};\r\n\r\n\r\nconst closeEdit = (refresh = false) => {\r\n editDialogVisible.value = false;\r\n editData.value = {}\r\n if (refresh === true) refreshTable();\r\n}\r\n\r\n/**\r\n * 刷新表格數據\r\n */\r\nconst refreshTable = () => curdTableRef.value?.refreshTable()\r\n\r\n/**\r\n * 初始化表格\r\n */\r\nconst initTable = async () => await curdTableRef.value?.initData()\r\n\r\n/**\r\n * 刷新查詢\r\n */\r\nconst initSearch = () => curdSearchRef.value?.initData()\r\n\r\n/**\r\n * 操作事件\r\n */\r\nconst actionEvent = async (ev: ActionEvent, data: any) => {\r\n let processedData = data;\r\n if (props.dataSource.beforeAction) {\r\n processedData = await props.dataSource.beforeAction(ev, data);\r\n }\r\n execActionEvent(ev, processedData);\r\n}\r\n\r\n/**\r\n * 执行 action 事件\r\n */\r\nconst execActionEvent = (ev: ActionEvent, data: any) => {\r\n // 如果 action 是函数,直接执行\r\n if (typeof ev.action === 'function') {\r\n ev.action(data);\r\n return;\r\n }\r\n\r\n // 根据 action 类型执行不同操作\r\n const actionHandlers: Record<string, () => void> = {\r\n update: () => handleUpdate(ev, data),\r\n delete: () => handleDelete(ev, data),\r\n batchDelete: () => handleBatchDelete(),\r\n create: () => handleCreate(ev, data),\r\n view: () => handleView(ev, data),\r\n filter: () => handleFilter(),\r\n back: () => handleBack(),\r\n };\r\n\r\n const handler = actionHandlers[ev.action as string];\r\n if (handler) {\r\n handler();\r\n } else {\r\n emits('actionEvent', ev, data);\r\n }\r\n};\r\n\r\n/**\r\n * 处理更新\r\n */\r\nconst handleUpdate = async (ev: ActionEvent, data: any) => {\r\n if (props.dataSource.beforeUpdate) {\r\n const result = await props.dataSource.beforeUpdate(data);\r\n if (result === false) return;\r\n }\r\n editData.value = data;\r\n action.value = ev;\r\n editDialogVisible.value = true;\r\n};\r\n\r\n/**\r\n * 处理删除\r\n */\r\nconst handleDelete = async (ev: ActionEvent, data: any) => {\r\n try {\r\n if (props.dataSource.beforeDelete) {\r\n const result = await props.dataSource.beforeDelete(data);\r\n if (result === false) return;\r\n }\r\n const deleteId = data[props.dataSource.rowKey];\r\n if (!deleteId) {\r\n proxy?.$modal.msgError('rowKey参数不正确');\r\n return;\r\n }\r\n const confirmText = ev.confirm || ev.confirmText || '確認刪除此數據嗎?';\r\n await proxy?.$modal.confirm(confirmText);\r\n await props.dataSource.deleteApi?.(deleteId);\r\n proxy?.$modal.msgSuccess('刪除成功');\r\n curdTableRef.value?.refreshTable();\r\n } catch (error) {\r\n if (error !== 'cancel') {\r\n console.error('Delete failed:', error);\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * 处理批量删除(deleteApi 支持傳入 id 數組一次刪除)\r\n */\r\nconst handleBatchDelete = async () => {\r\n if (!props.dataSource.deleteApi) {\r\n proxy?.$modal.msgError('deleteApi 方法未設置');\r\n return;\r\n }\r\n const selection = curdTableRef.value?.getSelection?.() ?? [];\r\n if (!selection.length) {\r\n proxy?.$modal.msgWarning('請先勾選要刪除的數據');\r\n return;\r\n }\r\n await proxy?.$modal.confirm(`確認刪除選中的 ${selection.length} 條數據嗎?`);\r\n const deleteIds: any[] = [];\r\n for (const row of selection) {\r\n if (props.dataSource.beforeDelete) {\r\n const result = await props.dataSource.beforeDelete(row);\r\n if (result === false) continue;\r\n }\r\n const id = row[props.dataSource.rowKey];\r\n if (id != null) deleteIds.push(id);\r\n }\r\n if (deleteIds.length) {\r\n await props.dataSource.deleteApi?.(deleteIds);\r\n proxy?.$modal.msgSuccess('刪除成功');\r\n curdTableRef.value?.refreshTable();\r\n }\r\n};\r\n\r\n/**\r\n * 处理创建\r\n */\r\nconst handleCreate = async (ev: ActionEvent, data: any) => {\r\n if (props.dataSource.beforeCreate) {\r\n const result = await props.dataSource.beforeCreate(data || {});\r\n if (result === false) return;\r\n }\r\n editData.value = data || {};\r\n action.value = ev;\r\n editDialogVisible.value = true;\r\n};\r\n\r\n/**\r\n * 提供外部調用,打開與「新增」按鈕一致的新增彈窗\r\n * 支持傳入配置對象控制標題、寬度等 action 參數\r\n */\r\nconst openCreate = async (options: any = {}) => {\r\n // 兼容舊用法:openCreate(data)\r\n const isConfigObject = options && (\r\n Object.prototype.hasOwnProperty.call(options, 'data') ||\r\n Object.prototype.hasOwnProperty.call(options, 'title') ||\r\n Object.prototype.hasOwnProperty.call(options, 'text') ||\r\n Object.prototype.hasOwnProperty.call(options, 'width') ||\r\n Object.prototype.hasOwnProperty.call(options, 'labelWidth') ||\r\n Object.prototype.hasOwnProperty.call(options, 'labelPosition')\r\n );\r\n\r\n const data = isConfigObject ? (options.data || {}) : (options || {});\r\n const createAction: ActionEvent = {\r\n action: 'create',\r\n ...(isConfigObject ? {\r\n title: options.title,\r\n text: options.text,\r\n width: options.width,\r\n labelWidth: options.labelWidth,\r\n labelPosition: options.labelPosition,\r\n } : {})\r\n } as ActionEvent;\r\n\r\n await handleCreate(createAction, data);\r\n};\r\n\r\n/**\r\n * 处理查看\r\n */\r\nconst handleView = (ev: ActionEvent, data: any) => {\r\n let url = ev.url;\r\n const pm = ev.pm;\r\n if (pm && data[pm]) {\r\n const separator = url.includes('?') ? '&' : '?';\r\n url += `${separator}${pm}=${data[pm]}`;\r\n }\r\n proxy?.$router.push(url);\r\n};\r\n\r\n\r\n/**\r\n * 处理过滤\r\n */\r\nconst handleFilter = () => {\r\n const queryParams = curdSearchRef.value?.getQueryParams();\r\n curdTableRef.value?.refreshTable(queryParams || {});\r\n};\r\n\r\n/**\r\n * 獲取查詢參數\r\n */\r\nconst getQueryParams = () => {\r\n return curdSearchRef.value?.getQueryParams();\r\n}\r\n\r\n/**\r\n * 獲取已選中行\r\n */\r\nconst getSelection = () => curdTableRef.value?.getSelection()\r\n\r\n/**\r\n * 返回\r\n */\r\nconst handleBack = () => proxy?.$router.back();\r\n\r\nonMounted(() => initializeColumns(props.dataSource.columns));\r\n\r\ndefineExpose({ refreshTable, initSearch, initTable, openCreate, getQueryParams, getSelection })\r\n</script>\r\n\r\n<style scoped lang=\"scss\"></style>\r\n","/**\n * 可配置的权限指令(库内自包含版本)\n * 默认全部放行;消费者通过 setPermissionChecker 或 install 选项注入真实校验逻辑。\n */\nimport type { Directive, DirectiveBinding } from 'vue';\n\ntype PermChecker = (value: string | string[]) => boolean;\n\nlet _hasPermiChecker: PermChecker = () => true;\nlet _hasRolesChecker: PermChecker = () => true;\n\nexport const setPermissionChecker = (opts: { hasPermi?: PermChecker; hasRoles?: PermChecker }) => {\n if (opts.hasPermi) _hasPermiChecker = opts.hasPermi;\n if (opts.hasRoles) _hasRolesChecker = opts.hasRoles;\n};\n\nexport const hasPermi: Directive = {\n mounted(el: HTMLElement, binding: DirectiveBinding) {\n const { value } = binding;\n if (value == null) return;\n if (Array.isArray(value) && value.length > 0) {\n if (!_hasPermiChecker(value)) {\n el.parentNode?.removeChild(el);\n }\n }\n }\n};\n\nexport const hasRoles: Directive = {\n mounted(el: HTMLElement, binding: DirectiveBinding) {\n const { value } = binding;\n if (value == null) return;\n if (Array.isArray(value) && value.length > 0) {\n if (!_hasRolesChecker(value)) {\n el.parentNode?.removeChild(el);\n }\n }\n }\n};\n","export const errorCode: any = {\r\n '401': '认证失败,无法访问系统资源',\r\n '403': '当前操作没有权限',\r\n '404': '访问资源不存在',\r\n default: '系统未知错误,请反馈给管理员'\r\n};\r\nexport default errorCode;\r\n","/**\n * 通用下载方法(库内自包含版本)\n * 通过 getRequest() 获取 axios 实例,避免直接依赖业务侧的 request.ts\n */\nimport FileSaver from 'file-saver';\nimport { getRequest } from '@/utils/http';\nimport { blobValidate, tansParams } from '@/utils/ruoyi';\nimport { errorCode } from '@/utils/errorCode';\n\nexport function download(url: string, params: any, fileName: string) {\n const loadingInstance = ElLoading.service({\n text: '正在下載數據,請稍後',\n background: 'rgba(0, 0, 0, 0.7)'\n });\n return getRequest()\n .post(url, params, {\n transformRequest: [(p: any) => tansParams(p)],\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n responseType: 'blob'\n })\n .then(async (resp: any) => {\n const isBlob = blobValidate(resp);\n if (isBlob) {\n const blob = new Blob([resp]);\n FileSaver.saveAs(blob, fileName);\n } else {\n const resText = await new Blob([resp]).text();\n const rspObj = JSON.parse(resText);\n const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default'];\n ElMessage.error(errMsg);\n }\n loadingInstance.close();\n })\n .catch((r: any) => {\n console.error(r);\n ElMessage.error('下载文件出现错误,请联系管理员!');\n loadingInstance.close();\n });\n}\n","import { MessageBoxData } from 'element-plus';\r\nimport { LoadingInstance } from 'element-plus/es/components/loading/src/loading';\r\nlet loadingInstance: LoadingInstance;\r\nexport default {\r\n // 消息提示\r\n msg(content: any) {\r\n ElMessage.info(content);\r\n },\r\n // 错误消息\r\n msgError(content: any) {\r\n ElMessage.error(content);\r\n },\r\n // 成功消息\r\n msgSuccess(content: any) {\r\n ElMessage.success(content);\r\n },\r\n // 警告消息\r\n msgWarning(content: any) {\r\n ElMessage.warning(content);\r\n },\r\n // 弹出提示\r\n alert(content: any) {\r\n ElMessageBox.alert(content, '系統提示');\r\n },\r\n // 错误提示\r\n alertError(content: any) {\r\n ElMessageBox.alert(content, '系統提示', { type: 'error' });\r\n },\r\n // 成功提示\r\n alertSuccess(content: any) {\r\n ElMessageBox.alert(content, '系統提示', { type: 'success' });\r\n },\r\n // 警告提示\r\n alertWarning(content: any) {\r\n ElMessageBox.alert(content, '系統提示', { type: 'warning' });\r\n },\r\n // 通知提示\r\n notify(content: any) {\r\n ElNotification.info(content);\r\n },\r\n // 错误通知\r\n notifyError(content: any) {\r\n ElNotification.error(content);\r\n },\r\n // 成功通知\r\n notifySuccess(content: any) {\r\n ElNotification.success(content);\r\n },\r\n // 警告通知\r\n notifyWarning(content: any) {\r\n ElNotification.warning(content);\r\n },\r\n // 确认窗体\r\n confirm(content: any): Promise<MessageBoxData> {\r\n return ElMessageBox.confirm(content, '系統提示', {\r\n confirmButtonText: '確認',\r\n cancelButtonText: '取消',\r\n type: 'warning'\r\n });\r\n },\r\n // 提交内容\r\n prompt(content: any, title: any, inputType: any = '') {\r\n return ElMessageBox.prompt(content, title || '系統提示', {\r\n confirmButtonText: '確認',\r\n cancelButtonText: '取消',\r\n type: 'warning',\r\n inputType: inputType,\r\n inputPlaceholder: content\r\n });\r\n },\r\n\r\n // 打开遮罩层\r\n loading(content: string) {\r\n loadingInstance = ElLoading.service({\r\n lock: true,\r\n text: content,\r\n background: 'rgba(0, 0, 0, 0.7)'\r\n });\r\n },\r\n // 关闭遮罩层\r\n closeLoading() {\r\n loadingInstance.close();\r\n }\r\n};\r\n","import { getRequest } from '@/utils/http';\r\nimport { AxiosPromise } from 'axios';\r\nimport { DictDataForm, DictDataQuery, DictDataVO } from './types';\r\n// 根据字典类型查询字典数据信息\r\nexport function getDicts(dictType: string): AxiosPromise<DictDataVO[]> {\r\n return getRequest()({\r\n url: '/system/dict/data/type/' + dictType,\r\n method: 'get'\r\n });\r\n}\r\n\r\n// 查询字典数据列表\r\nexport function listData(query: DictDataQuery): AxiosPromise<DictDataVO[]> {\r\n return getRequest()({\r\n url: '/system/dict/data/list',\r\n method: 'get',\r\n params: query\r\n });\r\n}\r\n\r\n// 查询字典数据详细\r\nexport function getData(dictCode: string | number): AxiosPromise<DictDataVO> {\r\n return getRequest()({\r\n url: '/system/dict/data/' + dictCode,\r\n method: 'get'\r\n });\r\n}\r\n\r\n// 新增字典数据\r\nexport function addData(data: DictDataForm) {\r\n return getRequest()({\r\n url: '/system/dict/data',\r\n method: 'post',\r\n data: data\r\n });\r\n}\r\n\r\n// 修改字典数据\r\nexport function updateData(data: DictDataForm) {\r\n return getRequest()({\r\n url: '/system/dict/data',\r\n method: 'put',\r\n data: data\r\n });\r\n}\r\n\r\n// 删除字典数据\r\nexport function delData(dictCode: string | number | Array<string | number>) {\r\n return getRequest()({\r\n url: '/system/dict/data/' + dictCode,\r\n method: 'delete'\r\n });\r\n}\r\n","import { getRequest } from '@/utils/http';\r\nimport { AxiosPromise } from 'axios';\r\nimport { CommonDictDataForm, CommonDictDataQuery, CommonDictDataVO } from './types';\r\n// 根据字典类型查询字典数据信息\r\nexport function getDicts(dictType: string): AxiosPromise<CommonDictDataVO[]> {\r\n return getRequest()({\r\n url: '/system/common/dict/data/type/' + dictType,\r\n method: 'get'\r\n });\r\n}\r\n\r\n// 查询字典数据列表\r\nexport function listData(query: CommonDictDataQuery): AxiosPromise<CommonDictDataVO[]> {\r\n return getRequest()({\r\n url: '/system/common/dict/data/list',\r\n method: 'get',\r\n params: query\r\n });\r\n}\r\n\r\n// 查询字典数据详细\r\nexport function getData(dictCode: string | number): AxiosPromise<CommonDictDataVO> {\r\n return getRequest()({\r\n url: '/system/common/dict/data/' + dictCode,\r\n method: 'get'\r\n });\r\n}\r\n\r\n// 新增字典数据\r\nexport function addData(data: CommonDictDataForm) {\r\n return getRequest()({\r\n url: '/system/common/dict/data',\r\n method: 'post',\r\n data: data\r\n });\r\n}\r\n\r\n// 修改字典数据\r\nexport function updateData(data: CommonDictDataForm) {\r\n return getRequest()({\r\n url: '/system/common/dict/data',\r\n method: 'put',\r\n data: data\r\n });\r\n}\r\n\r\n// 删除字典数据\r\nexport function delData(dictCode: string | number | Array<string | number>) {\r\n return getRequest()({\r\n url: '/system/common/dict/data/' + dictCode,\r\n method: 'delete'\r\n });\r\n}\r\n","import { defineStore } from 'pinia';\r\n\r\nexport const useDictStore = defineStore('dict', () => {\r\n const dict = ref<Map<string, DictDataOption[]>>(new Map());\r\n\r\n /**\r\n * 获取字典\r\n * @param _key 字典key\r\n */\r\n const getDict = (_key: string): DictDataOption[] | null => {\r\n if (!_key) {\r\n return null;\r\n }\r\n return dict.value.get(_key) || null;\r\n };\r\n\r\n /**\r\n * 设置字典\r\n * @param _key 字典key\r\n * @param _value 字典value\r\n */\r\n const setDict = (_key: string, _value: DictDataOption[]) => {\r\n if (!_key) {\r\n return false;\r\n }\r\n try {\r\n dict.value.set(_key, _value);\r\n return true;\r\n } catch (e) {\r\n console.error('Error in setDict:', e);\r\n return false;\r\n }\r\n };\r\n\r\n /**\r\n * 删除字典\r\n * @param _key\r\n */\r\n const removeDict = (_key: string): boolean => {\r\n if (!_key) {\r\n return false;\r\n }\r\n try {\r\n return dict.value.delete(_key);\r\n } catch (e) {\r\n console.error('Error in removeDict:', e);\r\n return false;\r\n }\r\n };\r\n\r\n /**\r\n * 清空字典\r\n */\r\n const cleanDict = (): void => {\r\n dict.value.clear();\r\n };\r\n\r\n return {\r\n dict,\r\n getDict,\r\n setDict,\r\n removeDict,\r\n cleanDict\r\n };\r\n});\r\n","import { getDicts } from '@/api/system/dict/data';\r\nimport { getDicts as getCommonDicts } from '@/api/system/commonDict/data';\r\nimport { useDictStore } from '@/store/modules/dict';\r\n/**\r\n * 获取字典数据\r\n */\r\nexport const useDict = (...args: string[]): { [key: string]: DictDataOption[] } => {\r\n const res = ref<{\r\n [key: string]: DictDataOption[];\r\n }>({});\r\n return (() => {\r\n args.forEach(async (dictType) => {\r\n res.value[dictType] = [];\r\n const dicts = useDictStore().getDict(dictType);\r\n if (dicts) {\r\n res.value[dictType] = dicts;\r\n } else {\r\n await getDicts(dictType).then((resp) => {\r\n res.value[dictType] = resp.data.map(\r\n (p): DictDataOption => ({ label: p.dictLabel, value: p.dictValue, elTagType: p.listClass, elTagClass: p.cssClass })\r\n );\r\n useDictStore().setDict(dictType, res.value[dictType]);\r\n });\r\n }\r\n });\r\n return res.value;\r\n })();\r\n};\r\n\r\nexport const useOneDict = async (dictType: string) => {\r\n const dicts = useDictStore().getDict(dictType);\r\n if (dicts && dicts.length > 0) return dicts;\r\n else {\r\n const result = await getDicts(dictType);\r\n const data = result.data.map(\r\n (p): DictDataOption => ({ label: p.dictLabel, value: p.dictValue, elTagType: p.listClass, elTagClass: p.cssClass, remark: p.remark })\r\n );\r\n useDictStore().setDict(dictType, data);\r\n return data;\r\n }\r\n};\r\n\r\n/**\r\n * 获取公共字典数据\r\n */\r\nexport const useCommonDict = (...args: string[]): { [key: string]: DictDataOption[] } => {\r\n const res = ref<{\r\n [key: string]: DictDataOption[];\r\n }>({});\r\n return (() => {\r\n args.forEach(async (dictType) => {\r\n res.value[dictType] = [];\r\n const dicts = useDictStore().getDict(dictType);\r\n if (dicts && dicts.length > 0) {\r\n res.value[dictType] = dicts;\r\n } else {\r\n await getCommonDicts(dictType).then((resp) => {\r\n res.value[dictType] = resp.data.map(\r\n (p): DictDataOption => ({ label: p.dictLabel, value: p.dictValue, elTagType: p.listClass, elTagClass: p.cssClass, remark: p.remark })\r\n );\r\n useDictStore().setDict(dictType, res.value[dictType]);\r\n });\r\n }\r\n });\r\n return res.value;\r\n })();\r\n};\r\n\r\nexport const useOneCommonDict = async (dictType: string) => {\r\n const dicts = useDictStore().getDict(dictType);\r\n if (dicts && dicts.length > 0) return dicts;\r\n else {\r\n const result = await getCommonDicts(dictType);\r\n const data = result.data.map((p): DictDataOption => ({ label: p.dictLabel, value: p.dictValue, elTagType: p.listClass, elTagClass: p.cssClass }));\r\n useDictStore().setDict(dictType, data);\r\n return data;\r\n }\r\n};\r\n","/**\r\n * v-copyText 复制文本内容\r\n * Copyright (c) 2022 ruoyi\r\n */\r\nimport { DirectiveBinding } from 'vue';\r\n\r\nexport default {\r\n beforeMount(el: any, { value, arg }: DirectiveBinding) {\r\n if (arg === 'callback') {\r\n el.$copyCallback = value;\r\n } else {\r\n el.$copyValue = value;\r\n const handler = () => {\r\n copyTextToClipboard(el.$copyValue);\r\n if (el.$copyCallback) {\r\n el.$copyCallback(el.$copyValue);\r\n }\r\n };\r\n el.addEventListener('click', handler);\r\n el.$destroyCopy = () => el.removeEventListener('click', handler);\r\n }\r\n }\r\n};\r\n\r\nfunction copyTextToClipboard(input: string, { target = document.body } = {}) {\r\n const element = document.createElement('textarea');\r\n const previouslyFocusedElement = document.activeElement as HTMLInputElement;\r\n element.value = input;\r\n // Prevent keyboard from showing on mobile\r\n element.setAttribute('readonly', '');\r\n\r\n element.style.contain = 'strict';\r\n element.style.position = 'absolute';\r\n element.style.left = '-9999px';\r\n element.style.fontSize = '12pt'; // Prevent zooming on iOS\r\n\r\n const selection = document.getSelection();\r\n let originalRange;\r\n if (selection) {\r\n originalRange = selection?.rangeCount > 0 && selection.getRangeAt(0);\r\n }\r\n target.append(element);\r\n element.select();\r\n\r\n // Explicit selection workaround for iOS\r\n element.selectionStart = 0;\r\n element.selectionEnd = input.length;\r\n\r\n let isSuccess = false;\r\n try {\r\n isSuccess = document.execCommand('copy');\r\n } catch (err) {\r\n console.error(err);\r\n }\r\n element.remove();\r\n\r\n if (originalRange) {\r\n selection?.removeAllRanges();\r\n selection?.addRange(originalRange);\r\n }\r\n\r\n // Get the focus back on the previously focused element, if any\r\n if (previouslyFocusedElement) {\r\n previouslyFocusedElement.focus();\r\n }\r\n return isSuccess;\r\n}\r\n","/**\n * vue3-curd 安装插件\n *\n * 用法:\n * import Vue3Curd from 'vue3-curd'\n * app.use(Vue3Curd, {\n * request, // 业务侧 axios 实例(携带鉴权/拦截器)\n * globalHeaders, // 返回鉴权请求头的函数\n * baseUrl, // 后端 API 基础地址\n * hasPermi, hasRoles // 权限校验函数\n * })\n */\nimport type { App } from 'vue';\nimport type { AxiosInstance } from 'axios';\n\nimport Curd from '@/components/CURD/index.vue';\nimport DictTag from '@/components/DictTag/index.vue';\nimport SvgIcon from '@/components/SvgIcon/index.vue';\nimport OssImagePreview from '@/components/OssImagePreview/index.vue';\nimport OssFile from '@/components/ossFile/index.vue';\nimport Editor from '@/components/Editor/index.vue';\nimport TranslateInput from '@/components/TranslateInput/index.vue';\nimport ImageUpload from '@/components/ImageUpload/index.vue';\nimport FileUpload from '@/components/FileUpload/index.vue';\n\nimport modal from '@/plugins/modal';\nimport { download } from '@/utils/download';\nimport { useDict, useOneDict, useCommonDict, useOneCommonDict } from '@/utils/dict';\nimport { parseTime, addDateRange, handleTree, selectDictLabel, selectDictLabels } from '@/utils/ruoyi';\nimport { setRequest, setGlobalHeaders, setBaseUrl } from '@/utils/http';\nimport { setPermissionChecker, hasPermi, hasRoles } from '@/directive/curd-perm';\nimport copyText from '@/directive/common/copyText';\n\nexport interface Vue3CurdOptions {\n /** 业务侧 axios 实例(携带鉴权/拦截器),不传则使用库内置最小实例 */\n request?: AxiosInstance;\n /** 返回鉴权请求头的函数,用于上传组件 */\n globalHeaders?: () => Record<string, string>;\n /** 后端 API 基础地址,用于上传/下载等 */\n baseUrl?: string;\n /** 按钮权限校验:v-hasPermi */\n hasPermi?: (value: string | string[]) => boolean;\n /** 角色权限校验:v-hasRoles */\n hasRoles?: (value: string | string[]) => boolean;\n}\n\nconst components: Record<string, any> = {\n Curd,\n DictTag,\n SvgIcon,\n OssImagePreview,\n OssFile,\n Editor,\n TranslateInput,\n ImageUpload,\n FileUpload\n};\n\nexport const install = (app: App, options: Vue3CurdOptions = {}) => {\n if (options.baseUrl != null) setBaseUrl(options.baseUrl);\n if (options.request) setRequest(options.request);\n if (options.globalHeaders) setGlobalHeaders(options.globalHeaders);\n if (options.hasPermi || options.hasRoles) setPermissionChecker(options);\n\n for (const [name, comp] of Object.entries(components)) {\n app.component(name, comp);\n }\n\n const gp = app.config.globalProperties as any;\n gp.$modal = modal;\n gp.download = download;\n gp.useDict = useDict;\n gp.useOneDict = useOneDict;\n gp.useOneCommonDict = useOneCommonDict;\n gp.useCommonDict = useCommonDict;\n gp.parseTime = parseTime;\n gp.addDateRange = addDateRange;\n gp.handleTree = handleTree;\n gp.selectDictLabel = selectDictLabel;\n gp.selectDictLabels = selectDictLabels;\n\n app.directive('hasPermi', hasPermi);\n app.directive('hasRoles', hasRoles);\n app.directive('copyText', copyText);\n};\n\nexport default { install };\n"],"names":["propTypes","createTypes","props","__props","iconName","computed","svgClass","tagData","pValue","data","res","valueArray","_request","_globalHeaders","_baseUrl","createDefaultRequest","instance","axios","err","getRequest","setRequest","getGlobalHeaders","setGlobalHeaders","fn","getBaseUrl","setBaseUrl","url","listByIds","ossId","delOss","src","ref","realSrc","realSrcList","real_src_list","srcList","item","realWidth","width","height","watch","value","list","realHeight","LOCAL_DICTS","FIXED_WIDTH_FIELDS","PAGE_SIZES","DEFAULT_PAGE_OPTIONS","PAGING_LAYOUT","MIN_TOOLBAR_WIDTH","TOOLBAR_BUTTON_WIDTH","deleteEmptyParams","params","result","key","calculateColumnWidth","column","fixedField","field","getDeepProperty","obj","prefix","suffix","initializeColumns","columns","initializeFormRules","rules","sortColumns","b","getNestedValue","path","keys","k","aOrder","bOrder","filterEditableColumns","isUpdate","hasKeyId","isButtonVisible","action","isButtonDisabled","formatDateRangeParams","queryParams","dateRange","propName","proxy","useTableData","dataSource","getCurrentInstance","listData","loading","multipleSelection","filterParams","defaultSort","pageOptions","listSize","pagingLayout","executeQuery","_a","getQueryParams","resData","isSinglePage","error","beforeQueryParams","refreshTable","handlePageChange","pageNum","handleSizeChange","pageSize","handleSortChange","handleSelectionChange","val","initQueryParams","useDictData","initColumnDicts","toRaw","initSearchDicts","initSelectApi","initTreeSelectApi","valueKey","emits","__emit","tableMaxHeight","curdTableRef","initData","localDicts","getRowKeyValue","row","columnToolbars","isRowSelectable","columnToolbarWidth","customWidth","buttonVisible","buttonDisabled","calColumnWidth","calColumnAlign","align","fromDbDicts","dictData","fromLocalDicts","fromData","getTagType","deepJsonProp","onSelectionChange","normalizedSelection","onRowClick","event","_b","actionEvent","ev","getSelection","applyDefaultSelectedRows","nextTick","table","keyStr","applyDefaultDisabledSelectedRows","initTableData","onMounted","__expose","emit","number","uploadList","baseUrl","uploadFileUrl","headers","fileList","showTip","fileUploadRef","temp","oss","handleBeforeUpload","file","fileName","fileExt","handleExceed","handleUploadError","handleUploadSuccess","uploadedSuccessfully","handleDelete","index","listToString","f","getFileName","name","separator","strs","dialogImageUrl","dialogVisible","uploadImgUrl","imageUploadRef","fileAccept","type","itemData","isImg","fileExtension","compressAccurately","handlePictureCardPreview","i","getTranslationByName","addTranslationBatch","useTranslationStore","defineStore","translation","_key","_value","useTranslation","dataVo","languages","hasInit","updateModelValue","text","zhHkOnBlur","getTranslateData","languageTemp","changeLanguage","lang","getTranslate","element","setInputFocus","newVal","upload","reactive","quillEditorRef","uploadRef","options","Quill","styles","style","content","v","quill","length","parseTime","time","pattern","format","date","formatObj","addDateRange","search","selectDictLabel","datas","actions","selectDictLabels","currentSeparator","match","handleTree","id","parentId","children","config","childrenListMap","tree","d","parentObj","tansParams","part","subPart","blobValidate","deepClone","source","targetObj","useFormData","editData","formData","getFormComponentDefaultValue","form","initFormData","currentEditData","formDataTemp","dataToUse","formProp","clonedEditData","initUpdateFormData","formType","prop","_c","rangeBeginProp","_d","rangeEndProp","_e","saveParams","mergedParams","arr","t","useI18n","translateInputRef","curdFormRef","initFormDataComposable","prepareSaveData","formObjs","filteredColumns","formInline","getTreeDefaultExpandedKeys","propsConfig","initFormRules","handleSave","valid","saveTranslation","handleClose","translateInputRefs","bRefTable","toolbars","createAction","batchDeleteAction","toolbarsWithoutCreate","emitToolbarAction","columnsPerRow","columnSpan","showCard","actionColSpan","remainder","searchColumns","rest","computedPaddingBottom","searchForm","handleQuery","handleExport","dateStr","dayjs","dataTemp","handleClear","searchColumnsTemp","col","searchFormTemp","searchProp","searchType","legacyMaxHeight","editDialogVisible","curdSearchRef","selectedCount","columnSlotNames","names","closeEdit","refresh","initTable","initSearch","processedData","execActionEvent","handler","handleUpdate","handleBatchDelete","handleCreate","handleView","handleFilter","handleBack","deleteId","confirmText","selection","deleteIds","openCreate","isConfigObject","pm","_hasPermiChecker","_hasRolesChecker","setPermissionChecker","opts","hasPermi","el","binding","hasRoles","errorCode","download","loadingInstance","ElLoading","p","resp","blob","FileSaver","resText","rspObj","errMsg","ElMessage","r","modal","ElMessageBox","ElNotification","title","inputType","getDicts","dictType","useDictStore","dict","e","useDict","args","dicts","useOneDict","useCommonDict","getCommonDicts","useOneCommonDict","copyText","arg","copyTextToClipboard","input","target","previouslyFocusedElement","originalRange","isSuccess","components","Curd","DictTag","SvgIcon","OssImagePreview","OssFile","Editor","TranslateInput","ImageUpload","FileUpload","install","app","comp","gp","plugin"],"mappings":"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"}