From 0e8771c22eaf45f63b61166222f9549f1423c4f1 Mon Sep 17 00:00:00 2001 From: hwj Date: Fri, 13 Mar 2026 11:18:27 +0800 Subject: [PATCH 001/265] =?UTF-8?q?style=EF=BC=9A=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E8=BF=90=E8=A1=8C=E5=8F=82=E6=95=B0=E5=88=86=E6=9E=90-?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E6=8E=A7=E4=BB=B6=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/iot/deviceParamAnalysis/index.vue | 143 ++++++++++++++------ 1 file changed, 104 insertions(+), 39 deletions(-) diff --git a/src/views/iot/deviceParamAnalysis/index.vue b/src/views/iot/deviceParamAnalysis/index.vue index 2508c84b..722a5e53 100644 --- a/src/views/iot/deviceParamAnalysis/index.vue +++ b/src/views/iot/deviceParamAnalysis/index.vue @@ -33,15 +33,19 @@ - +
+ +
@@ -131,45 +135,37 @@ const keyword = ref('') const treeProps = { children: 'children', label: 'label', disabled: 'disabled' } const treeData = ref([]) +const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss' +const MAX_DATE_RANGE_HOURS = 8 +const MAX_DATE_RANGE_MS = MAX_DATE_RANGE_HOURS * 60 * 60 * 1000 +const lastValidDateRange = new Map() + +const disableFutureDate = (date: Date) => { + return dayjs(date).isAfter(dayjs(), 'day') +} + const buildDefaultDateRange = (): [string, string] => { const end = dayjs() const start = end.subtract(2, 'hour') - return [start.format('YYYY-MM-DD HH:mm:ss'), end.format('YYYY-MM-DD HH:mm:ss')] + return [start.format(DATE_TIME_FORMAT), end.format(DATE_TIME_FORMAT)] } -const dateShortcuts = [ - { - text: t('DataCollection.DeviceParamAnalysis.shortcutLast7Days'), - value: () => { - const end = dayjs().endOf('day').toDate() - const start = dayjs().subtract(6, 'day').startOf('day').toDate() - return [start, end] - } - }, - { - text: t('DataCollection.DeviceParamAnalysis.shortcutLastWeek'), - value: () => { - const start = dayjs().subtract(1, 'week').startOf('week').toDate() - const end = dayjs().subtract(1, 'week').endOf('week').toDate() - return [start, end] - } - }, - { - text: t('DataCollection.DeviceParamAnalysis.shortcutLastMonth'), - value: () => { - const start = dayjs().subtract(1, 'month').startOf('month').toDate() - const end = dayjs().subtract(1, 'month').endOf('month').toDate() - return [start, end] - } - }, - { - text: t('DataCollection.DeviceParamAnalysis.shortcutLast3Months'), +const createLastHoursShortcut = (hours: number) => { + return { + text: `最近${hours}小时`, value: () => { - const end = dayjs().endOf('day').toDate() - const start = dayjs().subtract(3, 'month').startOf('day').toDate() + const end = dayjs().toDate() + const start = dayjs().subtract(hours, 'hour').toDate() return [start, end] } } +} + +const dateShortcuts = [ + createLastHoursShortcut(1), + createLastHoursShortcut(2), + createLastHoursShortcut(4), + createLastHoursShortcut(8) ] type ChartState = 'idle' | 'loading' | 'empty' | 'ready' @@ -346,10 +342,78 @@ const loadTree = async () => { } } +const normalizeDateRangeValue = (value: any): [string, string] | undefined => { + if (!Array.isArray(value) || value.length !== 2) return undefined + const start = value[0] + const end = value[1] + if (!start || !end) return undefined + return [String(start), String(end)] +} + +const ensureDateRangeWithin8Hours = (group: SelectedGroup, dateRange: [string, string]) => { + const now = dayjs() + let start = dayjs(dateRange[0]) + let end = dayjs(dateRange[1]) + if (!start.isValid() || !end.isValid()) return dateRange + + if (start.isAfter(now) && end.isAfter(now)) { + end = now + start = now.subtract(2, 'hour') + } else { + if (end.isAfter(now)) end = now + if (start.isAfter(now)) start = end.subtract(2, 'hour') + } + + const diffMs = end.valueOf() - start.valueOf() + if (diffMs < 0) { + end = now + start = now.subtract(2, 'hour') + const nextRange: [string, string] = [start.format(DATE_TIME_FORMAT), end.format(DATE_TIME_FORMAT)] + return nextRange + } + if (diffMs <= MAX_DATE_RANGE_MS) { + return [start.format(DATE_TIME_FORMAT), end.format(DATE_TIME_FORMAT)] + } + const clampedEnd = start.add(MAX_DATE_RANGE_HOURS, 'hour') + const finalEnd = clampedEnd.isAfter(now) ? now : clampedEnd + return [start.format(DATE_TIME_FORMAT), finalEnd.format(DATE_TIME_FORMAT)] +} + const ensureDateRange = (group: SelectedGroup) => { if (!group.dateRange || group.dateRange.length !== 2) { group.dateRange = buildDefaultDateRange() + lastValidDateRange.set(group.id, group.dateRange) + return + } + const normalized = normalizeDateRangeValue(group.dateRange) + if (!normalized) { + group.dateRange = buildDefaultDateRange() + lastValidDateRange.set(group.id, group.dateRange) + return + } + const nextRange = ensureDateRangeWithin8Hours(group, normalized) + group.dateRange = nextRange + lastValidDateRange.set(group.id, nextRange) +} + +const handleDateRangeChange = (group: SelectedGroup, value: any) => { + const normalized = normalizeDateRangeValue(value) || normalizeDateRangeValue(group.dateRange) + if (!normalized) return + + const now = dayjs() + const selectedStart = dayjs(normalized[0]) + const selectedEnd = dayjs(normalized[1]) + const hasFuture = (selectedStart.isValid() && selectedStart.isAfter(now)) || (selectedEnd.isValid() && selectedEnd.isAfter(now)) + + const nextRange = ensureDateRangeWithin8Hours(group, normalized) + const isClamped = nextRange[0] !== normalized[0] || nextRange[1] !== normalized[1] + if (hasFuture) { + message.warning('不能选择未来日期') + } else if (isClamped) { + message.warning('时间范围最多只能选择 8 小时') } + group.dateRange = nextRange + lastValidDateRange.set(group.id, nextRange) } const resetChartData = (group: SelectedGroup) => { @@ -536,6 +600,7 @@ const syncGroupsByCheckedNodes = (checkedNodes: DeviceTreeNode[]) => { chartRenderKey: 0 } selectedGroups.value.push(group) + lastValidDateRange.set(group.id, group.dateRange) addedGroups.push(group) } return addedGroups From 84916bbded91c17320a95ecdc160ba5c7b36d987 Mon Sep 17 00:00:00 2001 From: hwj Date: Fri, 13 Mar 2026 11:18:57 +0800 Subject: [PATCH 002/265] =?UTF-8?q?style=EF=BC=9A=E9=85=8D=E6=96=B9?= =?UTF-8?q?=E5=BA=93-=E6=89=8B=E5=8A=A8=E5=BD=95=E5=85=A5=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E5=8F=AF=E4=BB=A5=E8=A1=A5=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/iot/recipepointrecord/index.ts | 17 +++- .../components/FormulaLibraryDetailTabs.vue | 87 +++++++++++++++++++ 2 files changed, 102 insertions(+), 2 deletions(-) diff --git a/src/api/iot/recipepointrecord/index.ts b/src/api/iot/recipepointrecord/index.ts index 830967d3..8f4e2ee0 100644 --- a/src/api/iot/recipepointrecord/index.ts +++ b/src/api/iot/recipepointrecord/index.ts @@ -17,5 +17,18 @@ export const RecipePointRecordApi = { }, getRecipePointRecordPage: async (params: any) => { return await request.get({ url: `/iot/recipe-point-record/page`, params }) - } -} \ No newline at end of file + }, + updateRecipePointRecord: async (params: { + id: string | number + recipeId?: string | number + name?: string + max?: string | number + min?: string | number + dataType?: string | number + dataUnit?: string | number + remark?: string + refer?: string + }) => { + return await request.put({ url: `/iot/recipe-point-record/update`, data:params }) + } +} diff --git a/src/views/formula/formulaLibrary/components/FormulaLibraryDetailTabs.vue b/src/views/formula/formulaLibrary/components/FormulaLibraryDetailTabs.vue index fbb014b3..c6687b13 100644 --- a/src/views/formula/formulaLibrary/components/FormulaLibraryDetailTabs.vue +++ b/src/views/formula/formulaLibrary/components/FormulaLibraryDetailTabs.vue @@ -129,6 +129,11 @@ prop="remark" min-width="180" /> + + + + + + + + + + + + + + + + + + + + + + + + From 559c341ee15f9f06131bd802f15497da3fa1e637 Mon Sep 17 00:00:00 2001 From: liutao <790864623@qq.com> Date: Tue, 24 Mar 2026 11:23:25 +0800 Subject: [PATCH 025/265] =?UTF-8?q?esop=E6=96=87=E4=BB=B6=E7=AE=A1?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/mes/esopFile/index.ts | 85 ++++++++++ src/locales/en.ts | 63 ++++++++ src/locales/zh-CN.ts | 61 ++++++++ src/utils/dict.ts | 2 + src/views/mes/calholiday/index.vue | 21 ++- src/views/mes/esopFile/FileForm.vue | 228 +++++++++++++++++++++++++++ src/views/mes/esopFile/index.vue | 234 ++++++++++++++++++++++++++++ src/views/mes/esopFile/useUpload.ts | 117 ++++++++++++++ 8 files changed, 803 insertions(+), 8 deletions(-) create mode 100644 src/api/mes/esopFile/index.ts create mode 100644 src/views/mes/esopFile/FileForm.vue create mode 100644 src/views/mes/esopFile/index.vue create mode 100644 src/views/mes/esopFile/useUpload.ts diff --git a/src/api/mes/esopFile/index.ts b/src/api/mes/esopFile/index.ts new file mode 100644 index 00000000..dc61322a --- /dev/null +++ b/src/api/mes/esopFile/index.ts @@ -0,0 +1,85 @@ +import request from '@/config/axios' + +// esop文件表库 VO +export interface FileVO { + id: number // id + configId: number // 配置编号 + code: string // 文件编码 + name: string // 文件名 + path: string // 文件路径 + url: string // 文件 URL + type: string // 文件类型 + class: string // 文件分类 + status: number // 文件状态 + size: number // 文件大小 +} +// 文件预签名地址 Response VO +export interface FilePresignedUrlRespVO { + // 文件配置编号 + configId: number + // 文件上传 URL + uploadUrl: string + // 文件 URL + url: string +} + +export enum ApiUrl{ + '/admin-api/mes/esop/file/create' +} + +// esop文件表库 API +export const FileApi = { + // 查询esop文件表库分页 + getFilePage: async (params: any) => { + return await request.get({ url: `/esop/file/page`, params }) + }, + + + // 查询esop文件表库详情 + getFile: async (id: number) => { + return await request.get({ url: `/esop/file/get?id=` + id }) + }, + + // 新增esop文件表库 + createFile: async (data: FileVO) => { + return await request.post({ url: `/esop/file/create`, data }) + }, + + // 修改esop文件表库 + updateFile: async (data: FileVO) => { + return await request.put({ url: `/esop/file/update`, data }) + }, + + // 删除esop文件表库 + deleteFile: async (id: number) => { + return await request.delete({ url: `/esop/file/delete?id=` + id }) + }, + + // 导出esop文件表库 Excel + exportFile: async (params) => { + return await request.download({ url: `/esop/file/export-excel`, params }) + }, + // 获取文件编码 + generateCode: async () => { + return await request.get({ url: `/esop/file/generate`}) + }, + +} + +// 获取文件预签名地址 +export const getFilePresignedUrl = (path: string) => { + return request.get({ + url: '/infra/file/presigned-url', + params: { path } + }) +} + +// 创建文件 +export const createFile = (data: any) => { + return request.post({ url: '/esop/file/create', data }) +} + +// 上传文件 +export const updateFile = (data: any) => { + return request.upload({ url: '/esop/file/create', data }) +} diff --git a/src/locales/en.ts b/src/locales/en.ts index f3635b38..b87650a0 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -2069,6 +2069,69 @@ export default { validatorNameRequired: 'Unit name can not be empty', validatorStatusRequired: 'Unit status can not be empty', validatorPrimaryFlagRequired: 'Primary unit flag can not be empty' + }, + CalHoliday: { + setWorkingDays: 'set working days', + setHoliday: 'set holiday', + lastMonth:'last month', + today:'today', + nextMonth:'next month', + work:'work', + rest:'rest', + searchDateStartPlaceholder: '开始日期', + searchDateEndPlaceholder: '结束日期', + searchTypeLabel: '类型', + searchTypePlaceholder: '请选择类型', + searchRemarkLabel: '备注', + searchRemarkPlaceholder: '请输入备注', + searchButtonText: '搜索', + resetButtonText: '重置', + addButtonText: '新增', + exportButtonText: '导出' + }, + + EsopFile: { + moduleName: 'File Management', + searchCodeLabel: 'File Code', + searchCodePlaceholder: 'Please enter file code', + searchFileNameLabel: 'File_Name', + searchFileNamePlaceholder: 'Please enter file name', + searchClassificationLabel: 'File Classification', + searchClassificationPlaceholder: 'Please select file Classification', + searchStatusLabel: 'File Status', + searchStatusPlaceholder: 'Please select file status', + searchButtonText: 'Search', + resetButtonText: 'Reset', + uploadButtonText: 'Upload', + exportButtonText: 'Export', + tableCodeColumn: 'File Code', + tableNameColumn: 'File Name', + tableClassificationColumn: 'File Classification', + tableTypeColumn: 'File Type', + tableStatusColumn: 'File Status', + tableCreatorNameColumn: 'Uploader', + tableCreateTimeColumn: 'Upload Time', + tableOperateColumn: 'Operation', + tableEditAction: 'Edit', + tableDownloadAction: 'Download', + tableDeleteAction: 'Delete', + dialogFileNameLabel: 'File Name', + dialogFileNamePlaceholder: 'Please enter file name', + dialogClassificationLabel: 'File Classification', + dialogClassificationPlaceholder: 'Please select file classification', + dialogStatusLabel: 'File Status', + dialogCancelButton: 'Cancel', + dialogSubmitButton: 'Confirm', + messageOne: 'Tip: Only ', + messageTwo: ' format files are allowed to be imported!', + messageThree: ' Drag the file to this area, or ', + messageFour: ' click to upload', + validatorCodeRequired: 'File code can not be empty', + validatorNameRequired: 'File name can not be empty', + validatorClassificationRequired: 'File classification can not be empty', + validatorTypeRequired: 'File type can not be empty', + validatorStatusRequired: 'File status can not be empty' + } }, diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index 19048918..0a50671b 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -2875,7 +2875,66 @@ export default { validatorNameRequired: '单位名称不能为空', validatorStatusRequired: '单位状态不能为空', validatorPrimaryFlagRequired: '是否主单位不能为空' + }, + //假日管理 + CalHoliday: { + setWorkingDays: '设置工作日', + setHoliday: '设置休息日', + lastMonth:'上个月', + today:'今天', + nextMonth:'下个月', + work:'班', + rest:'休', + searchDateStartPlaceholder: '开始日期', + searchDateEndPlaceholder: '结束日期', + searchTypeLabel: '类型', + searchTypePlaceholder: '请选择类型', + searchRemarkLabel: '备注', + searchRemarkPlaceholder: '请输入备注', + searchButtonText: '搜索', + resetButtonText: '重置', + addButtonText: '新增', + exportButtonText: '导出' + }, + //esop文件管理 + EsopFile: { + searchFileNameLabel: '文件名称', + searchFileNamePlaceholder: '请输入文件名', + searchClassificationLabel: '文件分类', + searchClassificationPlaceholder: '请选择文件分类', + searchStatusLabel: '文件状态', + searchStatusPlaceholder: '请选择文件状态', + searchButtonText: '搜索', + resetButtonText: '重置', + uploadButtonText: '文件上传', + addButtonText: '新增', + exportButtonText: '导出', + tableCodeColumn:'文件编码', + tableNameColumn: '文件名称', + tableClassificationColumn: '文件分类', + tableTypeColumn: '文件类型', + tableStatusColumn: '文件状态', + tableCreatorNameColumn: '上传人', + tableCreateTimeColumn: '上传时间', + tableOperateColumn: '操作', + tableEditAction: '编辑', + tableDownloadAction:'下载', + tableDeleteAction: '删除', + dialogFileNameLabel: '文件名', + dialogFileNamePlaceholder: '请输入文件名', + dialogClassificationLabel: '文件分类', + dialogClassificationPlaceholder: '请选择文件分类', + dialogStatusLabel: '文件状态', + dialogCancelButton: '取 消', + dialogSubmitButton: '确 定', + messageOne:'提示:仅允许导入 ', + messageTwo:' 格式文件!', + messageThree:' 将文件拖到此处,或', + messageFour:'点击上传', + validatorNameRequired: '文件名不能为空', + validatorStatusRequired: '文件状态不能为空' } + }, ProductionPlan: { Task: { @@ -4221,6 +4280,8 @@ export default { messageDeviceNoParams: '该设备下没有参数', messageFetchChartFailed: '获取图表数据失败' } + + } } } diff --git a/src/utils/dict.ts b/src/utils/dict.ts index 2d3cc806..1b5589e4 100644 --- a/src/utils/dict.ts +++ b/src/utils/dict.ts @@ -266,6 +266,8 @@ export enum DICT_TYPE { MOLD_GET_STATUS = "mold_get_status", MES_PRE_PRODUCTION = "mes_pre_production", MES_ZJ_PRODUCT = "mes_zj_product", + FILE_STATUS = "file_status", + Classification = "classification", //====iot IOT_SIEMENS_TYPE = "iot_siemens_type", IOT_MODBUS_TYPE = "iot_modbus_type", diff --git a/src/views/mes/calholiday/index.vue b/src/views/mes/calholiday/index.vue index 928a562c..118cdaf9 100644 --- a/src/views/mes/calholiday/index.vue +++ b/src/views/mes/calholiday/index.vue @@ -22,33 +22,33 @@ type="primary" size="small" @click="handleWorkday(date)" - >设置工作日 + >{{ t('FactoryModeling.CalHoliday.setWorkingDays') }} 设置休息日 + >{{ t('FactoryModeling.CalHoliday.setHoliday') }} - 上个月 + {{ t('FactoryModeling.CalHoliday.lastMonth') }} - 今天 + {{ t('FactoryModeling.CalHoliday.today') }} - 下个月 + {{ t('FactoryModeling.CalHoliday.nextMonth') }} @@ -64,8 +64,8 @@ - - + {{ t('FactoryModeling.CalHoliday.work') }} + {{ t('FactoryModeling.CalHoliday.rest') }} @@ -138,21 +138,26 @@ const loadMonthData = () => { // 原按钮的事件处理 const handleOriginalPrevMonth = () => { + queryParams.theDay = dayjs(currentDate.value).format('YYYY-MM-DD'); currentDate.value = dayjs(currentDate.value) .subtract(1, 'month') .toDate() loadMonthData() + getList() } const handleOriginalNextMonth = () => { + queryParams.theDay = dayjs(currentDate.value).format('YYYY-MM-DD'); currentDate.value = dayjs(currentDate.value) .add(1, 'month') .toDate() loadMonthData() + getList() } const handleOriginalToday = () => { - currentDate.value = new Date() + currentDate.value = new Date(); + queryParams.theDay = dayjs(currentDate.value).format('YYYY-MM-DD'); loadMonthData() } diff --git a/src/views/mes/esopFile/FileForm.vue b/src/views/mes/esopFile/FileForm.vue new file mode 100644 index 00000000..6da59b3a --- /dev/null +++ b/src/views/mes/esopFile/FileForm.vue @@ -0,0 +1,228 @@ + + diff --git a/src/views/mes/esopFile/index.vue b/src/views/mes/esopFile/index.vue new file mode 100644 index 00000000..a8266a08 --- /dev/null +++ b/src/views/mes/esopFile/index.vue @@ -0,0 +1,234 @@ + + + diff --git a/src/views/mes/esopFile/useUpload.ts b/src/views/mes/esopFile/useUpload.ts new file mode 100644 index 00000000..5276ae76 --- /dev/null +++ b/src/views/mes/esopFile/useUpload.ts @@ -0,0 +1,117 @@ +import * as FileApi from '@/api/mes/esopFile' +import CryptoJS from 'crypto-js' +import { UploadRawFile, UploadRequestOptions } from 'element-plus/es/components/upload/src/upload' +import axios from 'axios' + +/** + * 获得上传 URL + */ +export const getUploadUrl = (): string => { + return import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL + '/esop/file/create' +} + +export const useUpload = () => { + // 后端上传地址 + const uploadUrl = getUploadUrl() + // 是否使用前端直连上传 + const isClientUpload = UPLOAD_TYPE.CLIENT === import.meta.env.VITE_UPLOAD_TYPE + // 重写ElUpload上传方法 + const httpRequest = async (options: UploadRequestOptions) => { + // 模式一:前端上传 + if (isClientUpload) { + const fileName = await generateFileName(options.file) + const presignedInfo = await FileApi.getFilePresignedUrl(fileName) + return axios + .put(presignedInfo.uploadUrl, options.file, { + headers: { + 'Content-Type': options.file.type + } + }) + .then(() => { + const fileVo = createFile(presignedInfo, fileName, options.file) + return { + code: 0, + status: 0, + data: { + fileName: fileVo.path, + fileUrl: fileVo.url + }, + msg: '' + } + }) + } else { + const fileVo = { + file: options.file, + code: options.data.params.code, + name: options.data.params.name, + type: options.data.params.type, + classification: options.data.params.classification, + status: options.data.params.status, + } + // 模式二:后端上传 + // 重写 el-upload httpRequest 文件上传成功会走成功的钩子,失败走失败的钩子 + return new Promise((resolve, reject) => { + FileApi.updateFile(fileVo) + .then((res) => { + if (res.code === 0) { + resolve(res) + } else { + reject(res) + } + }) + .catch((res) => { + reject(res) + }) + }) + } + } + + return { + uploadUrl, + httpRequest + } +} + +/** + * 创建文件信息 + * @param vo 文件预签名信息 + * @param name 文件名称 + * @param file 文件 + */ +function createFile(vo: FileApi.FilePresignedUrlRespVO, name: string, file: UploadRawFile) { + const fileVo = { + configId: vo.configId, + url: vo.url, + path: name, + name: file.name, + type: file.type, + size: file.size + } + FileApi.createFile(fileVo) + return fileVo +} + +/** + * 生成文件名称(使用算法SHA256) + * @param file 要上传的文件 + */ +async function generateFileName(file: UploadRawFile) { + // 读取文件内容 + const data = await file.arrayBuffer() + const wordArray = CryptoJS.lib.WordArray.create(data) + // 计算SHA256 + const sha256 = CryptoJS.SHA256(wordArray).toString() + // 拼接后缀 + const ext = file.name.substring(file.name.lastIndexOf('.')) + return `${sha256}${ext}` +} + +/** + * 上传类型 + */ +enum UPLOAD_TYPE { + // 客户端直接上传(只支持S3服务) + CLIENT = 'client', + // 客户端发送到后端上传 + SERVER = 'server' +} From 791cbb2f39b5302e522cf6f4fcdb228f6f627de0 Mon Sep 17 00:00:00 2001 From: liutao <790864623@qq.com> Date: Tue, 24 Mar 2026 16:50:08 +0800 Subject: [PATCH 026/265] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=8F=98=E9=87=8F?= =?UTF-8?q?=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/mes/esopFile/FileForm.vue | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/views/mes/esopFile/FileForm.vue b/src/views/mes/esopFile/FileForm.vue index 6da59b3a..f33bafc9 100644 --- a/src/views/mes/esopFile/FileForm.vue +++ b/src/views/mes/esopFile/FileForm.vue @@ -159,7 +159,7 @@ const handleFileChange = (file) => { /** 提交表单 */ const submitFileForm = async () => { data.value.params = formData - if(isEdit){ + if(isEdit.value){ // 提交请求 formLoading.value = true try { @@ -172,6 +172,7 @@ const submitFileForm = async () => { formLoading.value = false } }else{ + isEdit.value=false if (fileList.value.length == 0) { message.error('请上传文件') return From f91a45042017709e07e3b587b67309c7bc9adcfb Mon Sep 17 00:00:00 2001 From: liutao <790864623@qq.com> Date: Wed, 25 Mar 2026 16:22:00 +0800 Subject: [PATCH 027/265] =?UTF-8?q?=E9=A6=96=E9=A1=B5=E4=B8=AD=E8=8B=B1?= =?UTF-8?q?=E6=96=87=E5=88=87=E6=8D=A2=E5=8F=8A=E5=A2=9E=E5=8A=A0=E5=AD=97?= =?UTF-8?q?=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/locales/en.ts | 40 +++++++++++++++ src/locales/zh-CN.ts | 39 +++++++++++++++ src/views/Home/Index.vue | 68 +++++++++++++------------- src/views/mes/esopFile/FileForm.vue | 4 +- src/views/system/dict/DictTypeForm.vue | 8 +++ src/views/system/dict/index.vue | 6 +++ src/views/system/menu/index.vue | 1 + 7 files changed, 130 insertions(+), 36 deletions(-) diff --git a/src/locales/en.ts b/src/locales/en.ts index b87650a0..af7f1607 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -542,6 +542,43 @@ export default { large: 'Large', small: 'Small' }, + home:{ + welcome: 'Welcome to use the Bishuo Digital Intelligent Central Control Platform', + message: 'The Bishuo Digital Intelligent Central Control Platform takes production operation as the core, uniformly integrating key business modules such as production planning, data collection, warehousing, equipment, energy, molds, quality, formulas and report analysis, to achieve full-process digital management from planning to execution, from equipment to products, and from data to decision-making.', + productTitle:'Overall production overview', + placeholderCreateTimeStart: 'Start Date', + placeholderCreateTimeEnd: 'End Date', + productionSchedule:'Real-time production progress', + productionPlan:'Production plan', + productionName:'Production name', + planNumber:'Plan quantity', + productionLine:'Production line', + planStartTime:'Plan start time', + planEndTime:'Plan end time', + completedQuantity:'Completed quantity', + qualifiedQuantity:'Qualified quantity', + unqualifiedQuantity:'Unqualified quantity', + yieldRate: 'YIELD', + actualEndTime:'Actual end time', + gtasks: 'Gtask', + missionNumber:'Mission number', + taskType:'Task type', + target:'Target', + creationTime:'Creation time', + equipment:'Equipment', + mould:'Mould', + equipmentTile:'Overall condition of the collection equipment', + equipmentCount:'Total equipment count', + operation:'Operation', + standbyMode:'Standby mode', + malfunction:'During the malfunction', + alerting:'Alerting...', + useRatio:'use ratio', + failureRate:'failure rate', + equipmentTitle:'Statistics of equipment maintenance quantities', + equipmentClass:'Equipment classification statistics', + equipmentMessage:'Classify and count by mold type, and select the top 10 items with the largest number of molds.', + }, login: { welcome: 'Welcome to the system', message: 'Backstage management system', @@ -892,6 +929,7 @@ export default { SystemManagement: { Dept: { name: 'Department Name', + enName: 'Department Name (en)', statusLabel: 'Department Status', status: 'Status', leader: 'Leader', @@ -1079,6 +1117,7 @@ export default { Dict: { id: 'Dict ID', name: 'Dict Name', + enName: 'Dict Name (en)', type: 'Dict Type', label: 'Dict Label', labelEn: 'Dict Label (EN)', @@ -1097,6 +1136,7 @@ export default { searchLabelPlaceholder: 'Please input dict label', searchDataStatusPlaceholder: 'Please select data status', namePlaceholder: 'Please input dict name', + enNamePlaceholder: 'Please input dict name (en)', typePlaceholder: 'Please input dict type', labelPlaceholder: 'Please input data label', labelEnPlaceholder: 'Please input data label (EN)', diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index 0a50671b..8e0409cc 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -543,6 +543,43 @@ export default { large: '大', small: '小' }, + home: { + welcome: '欢迎您使用必硕数字化智能中控平台', + message: '必硕数字化智能中控平台以生产运营为核心,统一整合生产计划、数据采集、仓储、设备、能源、模具、质量、配方及报表分析等关键业务模块,实现从计划到执行、从设备到制品、从数据到决策的全流程数字化管理。', + productTitle: '整体生产概况', + placeholderCreateTimeStart: '开始日期', + placeholderCreateTimeEnd: '结束日期', + productionSchedule:'实时生产进度', + productionPlan:'生产计划', + productionName:'生产名称', + planNumber:'计划数量', + productionLine:'生产线', + planStartTime:'计划开始时间', + planEndTime:'计划结束时间', + completedQuantity:'完工数量', + qualifiedQuantity:'合格数量', + unqualifiedQuantity:'不合格数量', + yieldRate:'合格率', + actualEndTime:'实际结束时间', + gtasks:'待办任务', + missionNumber:'任务编号', + taskType:'任务类型', + target:'任务目标', + creationTime:'创建时间', + equipment:'设备', + mould:'模具', + equipmentTile:'采集设备整体情况', + equipmentCount:'设备总数', + operation:'运行', + standbyMode:'待机', + malfunction:'故障中', + alerting:'报警中', + useRatio:'利用率', + failureRate:'故障率', + equipmentTitle:'设备维修数量统计', + equipmentClass:'设备分类统计', + equipmentMessage:'按模具分类统计,取模具数量最多的前10项' + }, login: { welcome: '欢迎使用必硕数字化智能中控平台', message: '必硕智能“纸”为绿色生活', @@ -1079,6 +1116,7 @@ export default { Dict: { id: '字典编号', name: '字典名称', + enName: '字典英文名称', type: '字典类型', label: '字典标签', labelEn: '字典标签(英文)', @@ -1097,6 +1135,7 @@ export default { searchLabelPlaceholder: '请输入字典标签', searchDataStatusPlaceholder: '请选择数据状态', namePlaceholder: '请输入字典名称', + enNamePlaceholder: '请输入字典英文名称', typePlaceholder: '请输入字典类型', labelPlaceholder: '请输入数据标签', labelEnPlaceholder: '请输入数据标签(英文)', diff --git a/src/views/Home/Index.vue b/src/views/Home/Index.vue index e2a9be1c..0e0be202 100644 --- a/src/views/Home/Index.vue +++ b/src/views/Home/Index.vue @@ -2,9 +2,9 @@
-
欢迎您使用必硕数字化智能中控平台
+
{{t("home.welcome")}}
- 必硕数字化智能中控平台以生产运营为核心,统一整合生产计划、数据采集、仓储、设备、能源、模具、质量、配方及报表分析等关键业务模块,实现从计划到执行、从设备到制品、从数据到决策的全流程数字化管理。 + {{t("home.message")}}
@@ -15,11 +15,11 @@ alt="banner" :src="bannerImg" fit="contain" class="home-welcome-image"
-
整体生产概况
+
{{t("home.productTitle")}}
+ :start-placeholder="t('home.placeholderCreateTimeStart')" :end-placeholder="t('home.placeholderCreateTimeEnd')" size="small" @change="handleProductionOverviewRangeChange" />
@@ -45,14 +45,14 @@ v-model="productionOverviewRange" type="daterange" unlink-panels value-format="Y
-
实时生产进度
+
{{t("home.productionSchedule")}}
-
生产计划 {{ item.code }} +
{{t("home.productionPlan")}} {{ item.code }} {{ getPlanStatusLabel(item.status) }} @@ -60,45 +60,45 @@ v-model="productionOverviewRange" type="daterange" unlink-panels value-format="Y
- 产品名称 + {{t("home.productionName")}} {{ item.productName }}
- 计划数量 + {{t("home.planNumber")}} {{ item.planNumber }}
- 生产线 + {{t("home.productionLine")}} {{ item.feedingPipelineName }}
- 计划开始时间 + {{t("home.planStartTime")}} {{ formatDate(item.planStartTime) }}
- 计划结束时间 + {{t("home.planEndTime")}} {{ formatDate(item.planEndTime) }}
- 完工数量 + {{t("home.completedQuantity")}} {{ item.wangongNumber }}
- 合格数量 + {{t("home.qualifiedQuantity")}} {{ item.passNumber }}
- 不合格数量 + {{t("home.unqualifiedQuantity")}} {{ item.noPassNumber }}%
- 合格率 + {{t("home.yieldRate")}} {{ item.passRate }}
- 实际结束时间 + {{t("home.actualEndTime")}} {{ formatDate(item.endTime) }}
@@ -111,7 +111,7 @@ v-model="productionOverviewRange" type="daterange" unlink-panels value-format="Y
-
待办任务
+
{{t("home.gtasks")}}
@@ -119,10 +119,10 @@ v-model="productionOverviewRange" type="daterange" unlink-panels value-format="Y
{{ item.name }}
-
任务编号:{{ item.code }}
-
任务类型:{{ item.type }}
-
目标:{{ item.deviceName }}
-
创建时间:{{ formatDate(item.createTime) }}
+
{{t("home.missionNumber")}}:{{ item.code }}
+
{{t("home.taskType")}}:{{ item.type }}
+
{{t("home.target")}}:{{ item.deviceName }}
+
{{t("home.creationTime")}}:{{ formatDate(item.createTime) }}
@@ -133,7 +133,7 @@ v-model="productionOverviewRange" type="daterange" unlink-panels value-format="Y
-
设备
+
{{t("home.equipment")}}
+ + + @@ -196,6 +201,13 @@ const queryParams = reactive({ const queryFormRef = ref() // 搜索的表单 const exportLoading = ref(false) // 导出的加载中 +const getBarcodeTypeLabel = (value: any) => { + const str = value === undefined || value === null ? '' : String(value) + if (str === '1') return '条码' + if (str === '2') return '二维码' + return '-' +} + /** 查询列表 */ const getList = async () => { loading.value = true From 3708e99cf867c65d30f7f3bddecf6bfdb9aa49ae Mon Sep 17 00:00:00 2001 From: hwj Date: Thu, 26 Mar 2026 13:47:23 +0800 Subject: [PATCH 035/265] =?UTF-8?q?style=EF=BC=9A=E7=BC=96=E7=A0=81?= =?UTF-8?q?=E8=A7=84=E5=88=99-=E6=96=B0=E5=A2=9E/=E7=BC=96=E8=BE=91?= =?UTF-8?q?=E5=BC=B9=E6=A1=86=E6=B7=BB=E5=8A=A0=E7=A0=81=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/erp/autocode/index.ts | 1 + src/locales/en.ts | 3 +++ src/locales/zh-CN.ts | 3 +++ src/views/erp/autocode/AutocodeIndex.vue | 2 +- src/views/erp/autocode/AutocodeRuleForm.vue | 22 +++++++++++++++++++-- 5 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/api/erp/autocode/index.ts b/src/api/erp/autocode/index.ts index 6eaacff1..60c12a7e 100644 --- a/src/api/erp/autocode/index.ts +++ b/src/api/erp/autocode/index.ts @@ -53,6 +53,7 @@ export interface AutocodeRuleVO { ruleCode: string // 规则编码 ruleName: string // 规则名称 barcodeType?: number | string + barCodeType?: number | string ruleDesc: string // 描述 maxLength: number // 最大长度 isPadded: string // 是否补齐 diff --git a/src/locales/en.ts b/src/locales/en.ts index bf042f4c..2edee44e 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -1904,6 +1904,9 @@ export default { dialogRuleCodePlaceholder: 'Please enter rule code', dialogRuleNameLabel: 'Rule Name', dialogRuleNamePlaceholder: 'Please enter rule name', + dialogBarcodeTypeLabel: 'Code Type', + barcodeTypeBarcodeLabel: 'Barcode', + barcodeTypeQrcodeLabel: 'QR Code', dialogRuleDescLabel: 'Description', dialogRuleDescPlaceholder: 'Please enter description', dialogMaxLengthLabel: 'Max Length', diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index e4beaf26..6f66ec17 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -2709,6 +2709,9 @@ export default { dialogRuleCodePlaceholder: '请输入规则编码', dialogRuleNameLabel: '规则名称', dialogRuleNamePlaceholder: '请输入规则名称', + dialogBarcodeTypeLabel: '码类型', + barcodeTypeBarcodeLabel: '条形码', + barcodeTypeQrcodeLabel: '二维码', dialogRuleDescLabel: '描述', dialogRuleDescPlaceholder: '请输入描述', dialogMaxLengthLabel: '最大长度', diff --git a/src/views/erp/autocode/AutocodeIndex.vue b/src/views/erp/autocode/AutocodeIndex.vue index dafc70ac..d3e06ba6 100644 --- a/src/views/erp/autocode/AutocodeIndex.vue +++ b/src/views/erp/autocode/AutocodeIndex.vue @@ -203,7 +203,7 @@ const exportLoading = ref(false) // 导出的加载中 const getBarcodeTypeLabel = (value: any) => { const str = value === undefined || value === null ? '' : String(value) - if (str === '1') return '条码' + if (str === '1') return '条形码' if (str === '2') return '二维码' return '-' } diff --git a/src/views/erp/autocode/AutocodeRuleForm.vue b/src/views/erp/autocode/AutocodeRuleForm.vue index c0aa5106..ca14a976 100644 --- a/src/views/erp/autocode/AutocodeRuleForm.vue +++ b/src/views/erp/autocode/AutocodeRuleForm.vue @@ -20,6 +20,12 @@ :placeholder="t('FactoryModeling.AutocodeRule.dialogRuleNamePlaceholder')" /> + + + {{ t('FactoryModeling.AutocodeRule.barcodeTypeBarcodeLabel') }} + {{ t('FactoryModeling.AutocodeRule.barcodeTypeQrcodeLabel') }} + + { if (id) { formLoading.value = true try { - formData.value = await AutocodeRuleApi.getAutocodeRule(id) + const data = await AutocodeRuleApi.getAutocodeRule(id) + const barcodeType = Number(data?.barcodeType ?? data?.barCodeType) === 2 ? 2 : 1 + formData.value = { + ...data, + barcodeType + } } finally { formLoading.value = false } @@ -162,7 +174,12 @@ const submitForm = async () => { // 提交请求 formLoading.value = true try { - const data = formData.value as unknown as AutocodeRuleVO + const barcodeType = Number(formData.value.barcodeType) === 2 ? 2 : 1 + const data = { + ...formData.value, + barcodeType, + barCodeType: barcodeType + } as unknown as AutocodeRuleVO // 拼接子表的数据 data.autocodeParts = autocodePartFormRef.value.getData() if (formType.value === 'create') { @@ -186,6 +203,7 @@ const resetForm = () => { id: undefined, ruleCode: undefined, ruleName: undefined, + barcodeType: 1, ruleDesc: undefined, maxLength: undefined, isPadded: undefined, From 6ce08fb7e3ac056ef2cda917232b4b72ffcdb157 Mon Sep 17 00:00:00 2001 From: liutao <790864623@qq.com> Date: Thu, 26 Mar 2026 15:20:03 +0800 Subject: [PATCH 036/265] =?UTF-8?q?=E9=85=8D=E6=96=B9=E5=BA=93=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/iot/recipepointrecord/index.ts | 4 + .../components/FormulaLibraryDetailTabs.vue | 193 +++++++++++------- 2 files changed, 127 insertions(+), 70 deletions(-) diff --git a/src/api/iot/recipepointrecord/index.ts b/src/api/iot/recipepointrecord/index.ts index 8f4e2ee0..423518f9 100644 --- a/src/api/iot/recipepointrecord/index.ts +++ b/src/api/iot/recipepointrecord/index.ts @@ -12,6 +12,10 @@ export interface RecipePointRecordVO { export const RecipePointRecordApi = { // 新增记录 + createRecipePointRecord: async (data: RecipePointRecordVO) => { + return await request.post({ url: `/iot/recipe-point-record/create`, data }) + }, + // 批量新增记录 createRecipePointRecordBatch: async (data: RecipePointRecordVO[]) => { return await request.post({ url: `/iot/recipe-point-record/batch-create`, data }) }, diff --git a/src/views/formula/formulaLibrary/components/FormulaLibraryDetailTabs.vue b/src/views/formula/formulaLibrary/components/FormulaLibraryDetailTabs.vue index 3e48583d..976d7b37 100644 --- a/src/views/formula/formulaLibrary/components/FormulaLibraryDetailTabs.vue +++ b/src/views/formula/formulaLibrary/components/FormulaLibraryDetailTabs.vue @@ -46,58 +46,62 @@ /> - - - 查询 - - - 重置 + + + + {{ t('RecipeManagement.RecipeLibrary.createButtonText') }} - --> + + sortable /> + sortable /> - + - + - + - - + + + + + - + @@ -183,10 +197,12 @@ + diff --git a/src/views/erp/mold/components/MoldForm.vue b/src/views/erp/mold/components/MoldForm.vue index 4865cb98..58bcf7fe 100644 --- a/src/views/erp/mold/components/MoldForm.vue +++ b/src/views/erp/mold/components/MoldForm.vue @@ -4,15 +4,28 @@ ref="formRef" :model="formData" :rules="formRules" - label-width="100px" + label-width="120px" v-loading="formLoading" > - + + + + + +
+ +
+
+
+ +
+ + + +
{{ t('MoldManagement.Mold.qrcodeEmpty') }}
+
+ + + +
+
+
@@ -117,9 +157,11 @@ const dialogVisible = ref(false) // 弹窗的是否展示 const dialogTitle = ref('') // 弹窗的标题 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formType = ref('') // 表单的类型:create - 新增;update - 修改 +const regenerateCodeLoading = ref(false) const formData = ref({ id: undefined, code: undefined, + isCode: undefined, name: undefined, unitId: undefined, machineId: undefined, @@ -127,14 +169,26 @@ const formData = ref({ inTime: undefined, status: undefined, images: undefined, + qrcodeUrl: undefined, remark: undefined, isEnable: undefined, fileUrl: '', brandId: undefined }) +const validateCode = (_rule, value, callback) => { + if (Boolean(formData.value.isCode)) { + callback() + return + } + if (value === undefined || value === null || String(value).trim() === '') { + callback(new Error(t('MoldManagement.Mold.validatorCodeRequired'))) + return + } + callback() +} const formRules = reactive({ code: [ - { required: true, message: t('MoldManagement.Mold.validatorCodeRequired'), trigger: 'blur' } + { validator: validateCode, trigger: ['blur', 'change'] } ], name: [ { required: true, message: t('MoldManagement.Mold.validatorNameRequired'), trigger: 'blur' } @@ -176,6 +230,31 @@ const open = async (type: string, id?: number, brandId: number) => { } defineExpose({ open }) // 提供 open 方法,用于打开弹窗 +const handleCodeAutoChange = (value: boolean) => { + if (value) { + formData.value.code = undefined + } + formRef.value?.clearValidate('code') +} + +const handleRegenerateCode = async () => { + if (!formData.value.id || !formData.value.code) return + regenerateCodeLoading.value = true + try { + const data = await MoldBrandApi.regenerateCode(formData.value.id, formData.value.code) + if (data?.qrcodeUrl) { + formData.value.qrcodeUrl = data.qrcodeUrl + } else { + const moldData = await MoldBrandApi.getMold(formData.value.id) + formData.value.qrcodeUrl = moldData?.qrcodeUrl + formData.value.code = moldData?.code ?? formData.value.code + } + message.success(t('common.updateSuccess')) + } finally { + regenerateCodeLoading.value = false + } +} + /** 提交表单 */ const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 const submitForm = async () => { @@ -205,6 +284,7 @@ const resetForm = () => { formData.value = { id: undefined, code: undefined, + isCode: true, name: undefined, unitId: undefined, machineId: undefined, @@ -212,6 +292,7 @@ const resetForm = () => { inTime: undefined, status: 3, images: undefined, + qrcodeUrl: undefined, remark: undefined, isEnable: true, fileUrl: '', @@ -220,3 +301,47 @@ const resetForm = () => { formRef.value?.resetFields() } + diff --git a/src/views/erp/product/product/ProductForm.vue b/src/views/erp/product/product/ProductForm.vue index 042e1924..9016913e 100644 --- a/src/views/erp/product/product/ProductForm.vue +++ b/src/views/erp/product/product/ProductForm.vue @@ -11,7 +11,24 @@ - + + + + + +
+ +
+
+
@@ -116,6 +133,39 @@
+ + +
+ + + +
+ {{ t('FactoryModeling.ProductInformation.qrcodeEmpty') }} +
+
+ + + +
+
+
+
@@ -147,10 +197,13 @@ const dialogVisible = ref(false) // 弹窗的是否展示 const dialogTitle = ref('') // 弹窗的标题 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 const formType = ref('') // 表单的类型:create - 新增;update - 修改 +const regenerateCodeLoading = ref(false) const formData = ref({ id: undefined, name: undefined, barCode: undefined, + isCode: undefined, + qrcodeUrl: undefined, categoryId: undefined, unitId: undefined, status: undefined, @@ -163,9 +216,20 @@ const formData = ref({ minPrice: undefined, safetyNumber: undefined }) +const validateBarCode = (_rule, value, callback) => { + if (Boolean(formData.value.isCode)) { + callback() + return + } + if (value === undefined || value === null || String(value).trim() === '') { + callback(new Error(t('FactoryModeling.ProductInformation.validatorBarCodeRequired'))) + return + } + callback() +} const formRules = reactive({ name: [{ required: true, message: t('FactoryModeling.ProductInformation.validatorNameRequired'), trigger: 'blur' }], - barCode: [{ required: true, message: t('FactoryModeling.ProductInformation.validatorBarCodeRequired'), trigger: 'blur' }], + barCode: [{ validator: validateBarCode, trigger: ['blur', 'change'] }], categoryId: [{ required: true, message: t('FactoryModeling.ProductInformation.validatorCategoryRequired'), trigger: 'blur' }], unitId: [{ required: true, message: t('FactoryModeling.ProductInformation.validatorUnitRequired'), trigger: 'blur' }], status: [{ required: true, message: t('FactoryModeling.ProductInformation.validatorStatusRequired'), trigger: 'blur' }] @@ -197,6 +261,31 @@ const open = async (type: string, id?: number) => { } defineExpose({ open }) // 提供 open 方法,用于打开弹窗 +const handleCodeAutoChange = (value: boolean) => { + if (value) { + formData.value.barCode = undefined + } + formRef.value?.clearValidate('barCode') +} + +const handleRegenerateCode = async () => { + if (!formData.value.id || !formData.value.barCode) return + regenerateCodeLoading.value = true + try { + const data = await ProductApi.regenerateCode(formData.value.id, formData.value.barCode) + if (data?.qrcodeUrl) { + formData.value.qrcodeUrl = data.qrcodeUrl + } else { + const productData = await ProductApi.getProduct(formData.value.id) + formData.value.qrcodeUrl = productData?.qrcodeUrl + formData.value.barCode = productData?.barCode ?? formData.value.barCode + } + message.success(t('common.updateSuccess')) + } finally { + regenerateCodeLoading.value = false + } +} + /** 提交表单 */ const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 const submitForm = async () => { @@ -227,6 +316,8 @@ const resetForm = () => { id: undefined, name: undefined, barCode: undefined, + isCode: true, + qrcodeUrl: undefined, categoryId: undefined, unitId: undefined, status: CommonStatusEnum.ENABLE, @@ -241,3 +332,47 @@ const resetForm = () => { formRef.value?.resetFields() } + diff --git a/src/views/iot/device/components/DeviceAttributeForm.vue b/src/views/iot/device/components/DeviceAttributeForm.vue index 69c086b6..f01bd941 100644 --- a/src/views/iot/device/components/DeviceAttributeForm.vue +++ b/src/views/iot/device/components/DeviceAttributeForm.vue @@ -2,22 +2,26 @@ - - - - @@ -25,17 +29,20 @@ - - -
diff --git a/src/views/mes/calholiday/index.vue b/src/views/mes/calholiday/index.vue index 118cdaf9..4958400b 100644 --- a/src/views/mes/calholiday/index.vue +++ b/src/views/mes/calholiday/index.vue @@ -2,7 +2,7 @@
- diff --git a/src/views/mes/printTemplate/PrintTemplateForm.vue b/src/views/mes/printTemplate/PrintTemplateForm.vue new file mode 100644 index 00000000..13b7b856 --- /dev/null +++ b/src/views/mes/printTemplate/PrintTemplateForm.vue @@ -0,0 +1,125 @@ + + + diff --git a/src/views/mes/printTemplate/index.vue b/src/views/mes/printTemplate/index.vue new file mode 100644 index 00000000..5e9b09c6 --- /dev/null +++ b/src/views/mes/printTemplate/index.vue @@ -0,0 +1,169 @@ + + + From 288d10f3c7cafe70a0e73dd82d3397b6bf096fb5 Mon Sep 17 00:00:00 2001 From: hwj Date: Wed, 13 May 2026 14:29:51 +0800 Subject: [PATCH 242/265] =?UTF-8?q?feat=EF=BC=9A=E4=BA=A7=E5=93=81?= =?UTF-8?q?=E7=89=A9=E6=96=99=E4=BF=A1=E6=81=AF-=E4=BA=8C=E7=BB=B4?= =?UTF-8?q?=E7=A0=81=E6=A8=A1=E6=9D=BF=E5=9B=9E=E6=98=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/erp/product/product/index.ts | 1 + src/components/QrcodeActionCard/index.vue | 100 +++++++++++++++--- src/views/erp/product/product/ProductForm.vue | 19 ++++ .../printTemplate/PrintTemplateDesigner.vue | 1 - 4 files changed, 104 insertions(+), 17 deletions(-) diff --git a/src/api/erp/product/product/index.ts b/src/api/erp/product/product/index.ts index 7270a803..3b8fbb3d 100644 --- a/src/api/erp/product/product/index.ts +++ b/src/api/erp/product/product/index.ts @@ -7,6 +7,7 @@ export interface ProductVO { barCode: string // 产品条码 isCode?: boolean qrcodeUrl?: string + templateJson?: string | any categoryId: number // 产品类型编号 subCategoryId: number // 产品类型子类编号 subCategoryName: string // 产品类型子类名称 diff --git a/src/components/QrcodeActionCard/index.vue b/src/components/QrcodeActionCard/index.vue index ca74d771..7477e779 100644 --- a/src/components/QrcodeActionCard/index.vue +++ b/src/components/QrcodeActionCard/index.vue @@ -63,6 +63,9 @@ const props = withDefaults( printPaperHeight?: number printMaxWidth?: number printMaxHeight?: number + templateJsonUrl?: string + templateJson?: any + printData?: Record }>(), { imageUrl: '', @@ -82,7 +85,10 @@ const props = withDefaults( printPaperWidth: 80, printPaperHeight: 80, printMaxWidth: 180, - printMaxHeight: 120 + printMaxHeight: 120, + templateJsonUrl: '', + templateJson: undefined, + printData: () => ({}) } ) @@ -202,26 +208,88 @@ const handlePreview = () => { }) } +const replaceTemplateValues = (templateJson: any, printData: Record) => { + if (!templateJson?.panels) return templateJson + + return { + ...templateJson, + panels: templateJson.panels.map((panel: any) => ({ + ...panel, + printElements: panel.printElements?.map((element: any) => { + if (!element?.options?.qid) return element + + const qid = element.options.qid + const value = printData[qid] + + if (value === undefined || value === null) return element + + const newOptions = { ...element.options } + + if (qid === 'qrcodeUrl') { + newOptions.src = value + newOptions.testData = value + } else { + newOptions.field = qid + if (newOptions.testData === undefined || newOptions.testData === '') { + newOptions.testData = String(value) + } + } + + return { + ...element, + options: newOptions + } + }) || [] + })) + } +} + const handlePrint = async () => { if (!props.imageUrl || !props.showPrint) return - const printSize = await resolvePrintSize(props.imageUrl) - const imageWidth = printSize.width - const imageHeight = printSize.height - const printId = props.printId === undefined || props.printId === null ? '' : String(props.printId) - const idAreaHeight = printId ? 14 : 0 - const paperHeight = imageHeight + idAreaHeight + + let templateJson: any + let printData: Record + + printData = { + qrcodeUrl: props.imageUrl, + printId: props.printId === undefined || props.printId === null ? '' : String(props.printId), + ...props.printData + } + + if (props.templateJson) { + templateJson = replaceTemplateValues(props.templateJson, printData) + } else if (props.templateJsonUrl) { + try { + const response = await request.get({ url: props.templateJsonUrl }) + templateJson = typeof response.data === 'string' ? JSON.parse(response.data) : response.data + templateJson = replaceTemplateValues(templateJson, printData) + } catch (error) { + console.error('获取打印模板失败', error) + message.error('获取打印模板失败') + return + } + } else { + const printSize = await resolvePrintSize(props.imageUrl) + const imageWidth = printSize.width + const imageHeight = printSize.height + const printId = props.printId === undefined || props.printId === null ? '' : String(props.printId) + const idAreaHeight = printId ? 14 : 0 + const paperHeight = imageHeight + idAreaHeight + + templateJson = buildQrcodeTemplateJson(props.imageUrl, printId, imageWidth, imageHeight, paperHeight) + } + + const paperSize = templateJson?.panels?.[0] ? { + width: templateJson.panels[0].width, + height: templateJson.panels[0].height + } : { width: 80, height: 80 } + hiprintPreviewDialogRef.value?.open({ title: props.printTitle, - printData: { - qrcodeUrl: props.imageUrl, - printId - }, - templateJson: buildQrcodeTemplateJson(props.imageUrl, printId, imageWidth, imageHeight, paperHeight), + printData, + templateJson, withDefaultQrcodeLayout: false, - paperSize: { - width: imageWidth, - height: paperHeight - } + paperSize }) } diff --git a/src/views/erp/product/product/ProductForm.vue b/src/views/erp/product/product/ProductForm.vue index 784f2609..cf6db022 100644 --- a/src/views/erp/product/product/ProductForm.vue +++ b/src/views/erp/product/product/ProductForm.vue @@ -163,6 +163,8 @@ :refresh-url="getQrcodeRefreshUrl()" :refresh-disabled="!formData.id || !formData.barCode" refresh-confirm-text="确认刷新该产品二维码吗?" + :template-json="formData.templateJson" + :print-data="buildPrintData()" @refresh-success="handleQrcodeRefreshSuccess" /> @@ -296,6 +298,7 @@ const formData = ref({ barCode: undefined, isCode: undefined, qrcodeUrl: undefined, + templateJson: undefined, categoryId: undefined, unitId: undefined, status: undefined, @@ -511,9 +514,14 @@ const open = async (type: string, id?: number) => { ['name', 'code'], '模具' ) + const templateJson = productData?.templateJson + const parsedTemplateJson = typeof templateJson === 'string' + ? JSON.parse(templateJson) + : templateJson formData.value = { ...formData.value, ...productData, + templateJson: parsedTemplateJson, devices, molds } @@ -538,6 +546,17 @@ const getQrcodeRefreshUrl = () => { return `/erp/product/regenerate-code?id=${formData.value.id}&code=${encodeURIComponent(String(formData.value.barCode))}` } +const buildPrintData = () => { + return { + id: formData.value.id, + name: formData.value.name, + barCode: formData.value.barCode, + standard: formData.value.standard, + remark: formData.value.remark, + qrcodeUrl: formData.value.qrcodeUrl + } +} + const handleQrcodeRefreshSuccess = async (data: any) => { if (!formData.value.id) return if (data?.qrcodeUrl) { diff --git a/src/views/mes/printTemplate/PrintTemplateDesigner.vue b/src/views/mes/printTemplate/PrintTemplateDesigner.vue index c3f57bf2..f004929b 100644 --- a/src/views/mes/printTemplate/PrintTemplateDesigner.vue +++ b/src/views/mes/printTemplate/PrintTemplateDesigner.vue @@ -82,7 +82,6 @@ const baseElements = [ { tid: 'qrcodeModule.qrcode', label: '二维码' }, { tid: 'defaultModule.longText', label: '长文' }, { tid: 'defaultModule.table', label: '表格' }, - { tid: 'defaultModule.html', label: 'HTML' }, { tid: 'defaultModule.hline', label: '横线' }, { tid: 'defaultModule.vline', label: '竖线' }, { tid: 'defaultModule.rect', label: '矩形' }, From 5ff4d7e41aa987953b8d63fdd8d54b0b80032ff8 Mon Sep 17 00:00:00 2001 From: hwj Date: Wed, 13 May 2026 14:54:31 +0800 Subject: [PATCH 243/265] =?UTF-8?q?feat=EF=BC=9A=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E5=8F=B0=E8=B4=A6-=E4=BA=8C=E7=BB=B4=E7=A0=81=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=E5=9B=9E=E6=98=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/mes/deviceledger/index.ts | 1 + .../mes/deviceledger/DeviceLedgerForm.vue | 25 ++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/api/mes/deviceledger/index.ts b/src/api/mes/deviceledger/index.ts index 6370fcf4..f258911d 100644 --- a/src/api/mes/deviceledger/index.ts +++ b/src/api/mes/deviceledger/index.ts @@ -26,6 +26,7 @@ export interface DeviceLedgerVO { remark: string // 备注 fileUrl?: string // 附件下载 qrcodeUrl?: string + templateJson?: string | any isSchedueld?: number isScheduled?: number ratedCapacity?: number diff --git a/src/views/mes/deviceledger/DeviceLedgerForm.vue b/src/views/mes/deviceledger/DeviceLedgerForm.vue index dc58b8ab..82ff617a 100644 --- a/src/views/mes/deviceledger/DeviceLedgerForm.vue +++ b/src/views/mes/deviceledger/DeviceLedgerForm.vue @@ -203,7 +203,10 @@ :print-max-width="220" :empty-text="t('EquipmentManagement.EquipmentLedger.qrcodeEmpty')" :error-text="t('EquipmentManagement.EquipmentLedger.qrcodeLoadError')" :refresh-url="getQrcodeRefreshUrl()" :refresh-disabled="!formData.id || !formData.deviceCode" - refresh-confirm-text="确认刷新该设备二维码吗?" @refresh-success="handleQrcodeRefreshSuccess" /> + refresh-confirm-text="确认刷新该设备二维码吗?" + :template-json="formData.templateJson" + :print-data="buildPrintData()" + @refresh-success="handleQrcodeRefreshSuccess" /> @@ -498,6 +501,7 @@ const initFormData = () => ({ beijianIds: [] as number[], fileUrl: undefined, qrcodeUrl: undefined, + templateJson: undefined, sort: undefined, dvId: undefined }) @@ -628,6 +632,20 @@ const getQrcodeRefreshUrl = () => { return `/mes/device-ledger/regenerate-code?id=${formData.value.id}&code=${encodeURIComponent(String(formData.value.deviceCode))}` } +const buildPrintData = () => { + return { + id: formData.value.id, + deviceCode: formData.value.deviceCode, + deviceName: formData.value.deviceName, + deviceSpec: formData.value.deviceSpec, + deviceBrand: formData.value.deviceBrand, + deviceModel: formData.value.deviceModel, + deviceLocation: formData.value.deviceLocation, + remark: formData.value.remark, + qrcodeUrl: formData.value.qrcodeUrl + } +} + const handleQrcodeRefreshSuccess = async (data: any) => { if (!formData.value.id) return if (data?.qrcodeUrl) { @@ -677,9 +695,14 @@ const open = async (type: string, id?: number, defaultDeviceTypeId?: number) => formLoading.value = true try { const detail = await DeviceLedgerApi.getDeviceLedger(id) + const templateJson = (detail as any)?.templateJson + const parsedTemplateJson = typeof templateJson === 'string' + ? JSON.parse(templateJson) + : templateJson formData.value = { ...initFormData(), ...(detail as any), + templateJson: parsedTemplateJson, isCode: (detail as any)?.isCode ?? false, isScheduled: normalizeNumberish((detail as any)?.isScheduled ?? (detail as any)?.isScheduled) ?? 0, ratedCapacity: normalizeNumberish((detail as any)?.ratedCapacity), From 4f6ee867984a57815942679161e183109d44f9ad Mon Sep 17 00:00:00 2001 From: hwj Date: Wed, 13 May 2026 15:23:16 +0800 Subject: [PATCH 244/265] =?UTF-8?q?feat=EF=BC=9A=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E5=85=B3=E9=94=AE=E4=BB=B6-=E4=BA=8C=E7=BB=B4=E7=A0=81?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=E5=9B=9E=E6=98=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/mes/criticalComponent/index.ts | 1 + .../CriticalComponentForm.vue | 24 ++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/api/mes/criticalComponent/index.ts b/src/api/mes/criticalComponent/index.ts index e249417e..38255d36 100644 --- a/src/api/mes/criticalComponent/index.ts +++ b/src/api/mes/criticalComponent/index.ts @@ -9,6 +9,7 @@ export interface CriticalComponentVO { count?: number remark?: string qrcodeUrl?: string + templateJson?: string | any createTime?: string images?: string } diff --git a/src/views/mes/criticalComponent/CriticalComponentForm.vue b/src/views/mes/criticalComponent/CriticalComponentForm.vue index 5b1dc87c..6bf29149 100644 --- a/src/views/mes/criticalComponent/CriticalComponentForm.vue +++ b/src/views/mes/criticalComponent/CriticalComponentForm.vue @@ -72,6 +72,8 @@ :refresh-url="getQrcodeRefreshUrl()" :refresh-disabled="!formData.id || !formData.code" refresh-confirm-text="确认刷新该关键件二维码吗?" + :template-json="formData.templateJson" + :print-data="buildPrintData()" @refresh-success="handleQrcodeRefreshSuccess" /> @@ -123,6 +125,7 @@ const formData = ref>({ count: undefined, remark: undefined, qrcodeUrl: undefined, + templateJson: undefined, images: undefined }) @@ -152,7 +155,9 @@ const resetForm = () => { description: undefined, count: undefined, remark: undefined, - qrcodeUrl: undefined + qrcodeUrl: undefined, + templateJson: undefined, + images: undefined } formRef.value?.resetFields?.() } @@ -169,6 +174,18 @@ const getQrcodeRefreshUrl = () => { return `/mes/critical-component/regenerate-code?id=${formData.value.id}&code=${encodeURIComponent(String(formData.value.code))}` } +const buildPrintData = () => { + return { + id: formData.value.id, + code: formData.value.code, + name: formData.value.name, + description: formData.value.description, + count: formData.value.count, + remark: formData.value.remark, + qrcodeUrl: formData.value.qrcodeUrl + } +} + const handleQrcodeRefreshSuccess = async (data: any) => { if (!formData.value.id) return if (data?.qrcodeUrl) { @@ -189,6 +206,10 @@ const open = async (type: 'create' | 'update', id?: number) => { formLoading.value = true try { const detail = await CriticalComponentApi.getCriticalComponent(id) + const templateJson = detail?.templateJson + const parsedTemplateJson = typeof templateJson === 'string' + ? JSON.parse(templateJson) + : templateJson formData.value = { id: detail?.id, code: detail?.code, @@ -199,6 +220,7 @@ const open = async (type: 'create' | 'update', id?: number) => { count: detail?.count, remark: detail?.remark, qrcodeUrl: detail?.qrcodeUrl, + templateJson: parsedTemplateJson, images: detail?.images } } finally { From ae6ef128ee37061163921558d98d9e07efc6dc2d Mon Sep 17 00:00:00 2001 From: hwj Date: Wed, 13 May 2026 15:40:59 +0800 Subject: [PATCH 245/265] =?UTF-8?q?feat=EF=BC=9A=E6=A8=A1=E5=85=B7?= =?UTF-8?q?=E7=AE=A1=E7=90=86-=E4=BA=8C=E7=BB=B4=E7=A0=81=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=E5=9B=9E=E6=98=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/erp/mold/index.ts | 1 + src/views/erp/mold/components/MoldForm.vue | 29 +++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/api/erp/mold/index.ts b/src/api/erp/mold/index.ts index 6e850d2d..b712cb71 100644 --- a/src/api/erp/mold/index.ts +++ b/src/api/erp/mold/index.ts @@ -35,6 +35,7 @@ export interface MoldVO { status: number // 状态 images: string // 模具图片 qrcodeUrl?: string + templateJson?: string | any fileUrl?: string remark: string // 备注 isEnable: boolean // 是否启用 diff --git a/src/views/erp/mold/components/MoldForm.vue b/src/views/erp/mold/components/MoldForm.vue index 72c6d109..4fa4df6c 100644 --- a/src/views/erp/mold/components/MoldForm.vue +++ b/src/views/erp/mold/components/MoldForm.vue @@ -97,6 +97,8 @@ :refresh-url="getQrcodeRefreshUrl()" :refresh-disabled="!formData.id || !formData.code" refresh-confirm-text="确认刷新该模具二维码吗?" + :template-json="formData.templateJson" + :print-data="buildPrintData()" @refresh-success="handleQrcodeRefreshSuccess" /> @@ -159,6 +161,7 @@ const formData = ref({ status: undefined, images: undefined, qrcodeUrl: undefined, + templateJson: undefined, remark: undefined, isEnable: undefined, fileUrl: '', @@ -208,7 +211,15 @@ const open = async (type: string, id?: number, brandId: number) => { if (id) { formLoading.value = true try { - formData.value = await MoldBrandApi.getMold(id) + const moldData = await MoldBrandApi.getMold(id) + const templateJson = moldData?.templateJson + const parsedTemplateJson = typeof templateJson === 'string' + ? JSON.parse(templateJson) + : templateJson + formData.value = { + ...moldData, + templateJson: parsedTemplateJson + } } finally { formLoading.value = false } @@ -231,6 +242,21 @@ const getQrcodeRefreshUrl = () => { return `/erp/mold-brand/regenerate-code?id=${formData.value.id}&code=${encodeURIComponent(String(formData.value.code))}` } +const buildPrintData = () => { + return { + id: formData.value.id, + code: formData.value.code, + name: formData.value.name, + unitId: formData.value.unitId, + machineId: formData.value.machineId, + useTime: formData.value.useTime, + inTime: formData.value.inTime, + status: formData.value.status, + remark: formData.value.remark, + qrcodeUrl: formData.value.qrcodeUrl + } +} + const handleQrcodeRefreshSuccess = async (data: any) => { if (!formData.value.id) return if (data?.qrcodeUrl) { @@ -280,6 +306,7 @@ const resetForm = () => { status: 3, images: undefined, qrcodeUrl: undefined, + templateJson: undefined, remark: undefined, isEnable: true, fileUrl: '', From 2d20ec5344d1dbe4571414ff3b45a3f18bd09667 Mon Sep 17 00:00:00 2001 From: hwj Date: Wed, 13 May 2026 15:52:11 +0800 Subject: [PATCH 246/265] =?UTF-8?q?feat=EF=BC=9A=E5=A4=87=E4=BB=B6?= =?UTF-8?q?=E4=BF=A1=E6=81=AF-=E4=BA=8C=E7=BB=B4=E7=A0=81=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=E5=9B=9E=E6=98=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../erp/component/product/ProductForm.vue | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/views/erp/component/product/ProductForm.vue b/src/views/erp/component/product/ProductForm.vue index 435319f1..6ee2127b 100644 --- a/src/views/erp/component/product/ProductForm.vue +++ b/src/views/erp/component/product/ProductForm.vue @@ -118,6 +118,8 @@ :refresh-url="getQrcodeRefreshUrl()" :refresh-disabled="!formData.id || !formData.barCode" refresh-confirm-text="确认刷新该备件二维码吗?" + :template-json="formData.templateJson" + :print-data="buildPrintData()" @refresh-success="handleQrcodeRefreshSuccess" /> @@ -158,6 +160,7 @@ const formData = ref({ barCode: undefined, isCode: undefined, qrcodeUrl: undefined, + templateJson: undefined, categoryId: undefined, unitId: undefined, status: undefined, @@ -201,7 +204,15 @@ const open = async (type: string, id?: number) => { if (id) { formLoading.value = true try { - formData.value = await ProductApi.getProduct(id) + const productData = await ProductApi.getProduct(id) + const templateJson = productData?.templateJson + const parsedTemplateJson = typeof templateJson === 'string' + ? JSON.parse(templateJson) + : templateJson + formData.value = { + ...productData, + templateJson: parsedTemplateJson + } } finally { formLoading.value = false } @@ -223,6 +234,20 @@ const getQrcodeRefreshUrl = () => { return `/erp/product/regenerate-code?id=${formData.value.id}&code=${encodeURIComponent(String(formData.value.barCode))}` } +const buildPrintData = () => { + return { + id: formData.value.id, + barCode: formData.value.barCode, + name: formData.value.name, + standard: formData.value.standard, + unitId: formData.value.unitId, + safetyNumber: formData.value.safetyNumber, + status: formData.value.status, + remark: formData.value.remark, + qrcodeUrl: formData.value.qrcodeUrl + } +} + const handleQrcodeRefreshSuccess = async (data: any) => { if (!formData.value.id) return if (data?.qrcodeUrl) { @@ -267,6 +292,7 @@ const resetForm = () => { barCode: undefined, isCode: true, qrcodeUrl: undefined, + templateJson: undefined, categoryId: undefined, unitId: undefined, status: CommonStatusEnum.ENABLE, From 13133c28be8e2ad5c2f8a9b77daa4eb628755fa9 Mon Sep 17 00:00:00 2001 From: hwj Date: Wed, 13 May 2026 17:21:55 +0800 Subject: [PATCH 247/265] =?UTF-8?q?feat=EF=BC=9A=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=89=93=E5=8D=B0=E6=A8=A1=E6=9D=BF=E9=A1=B5=E9=9D=A2-?= =?UTF-8?q?=E4=BA=8C=E7=BB=B4=E7=A0=81=E6=A0=B7=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/imgs/qrCode.png | Bin 0 -> 366 bytes .../mes/printTemplate/PrintTemplateDesigner.vue | 9 +++++---- 2 files changed, 5 insertions(+), 4 deletions(-) create mode 100644 src/assets/imgs/qrCode.png diff --git a/src/assets/imgs/qrCode.png b/src/assets/imgs/qrCode.png new file mode 100644 index 0000000000000000000000000000000000000000..0b688860a6977608b4fdc7a4d08b0b319e630eb6 GIT binary patch literal 366 zcmV-!0g?WRP)e!y7^} zRHn6m48>5}O3`gf0x~dG7XDts8=01MqK(@J85HvZP$!#R4_JkgfDDa&ss{5)ag+Rw zpZk+RvV9LIe(2|m!$1bh!pV>KfL二维码` @@ -165,8 +166,8 @@ const qrcodeProvider = function () { type: 'image', options: { field: '', - src: 'https://p119-minio-upload.ngsk.tech:7001/besure/productmaterial/qr/2026-04-17/295.png', - testData: 'https://p119-minio-upload.ngsk.tech:7001/besure/productmaterial/qr/2026-04-17/295.png', + src: qrCodeImg, + testData: qrCodeImg, width: 80, height: 80, title: '二维码' From 3745ef5bbca5181e0bfc6d8fb582bcd19155bb3a Mon Sep 17 00:00:00 2001 From: liutao <790864623@qq.com> Date: Thu, 14 May 2026 15:30:38 +0800 Subject: [PATCH 248/265] =?UTF-8?q?=E8=83=BD=E6=BA=90=E6=A6=82=E8=A7=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/mes/energydevice/index.ts | 59 +++++ src/views/mes/energyOverview/index.vue | 349 ++++++++++--------------- 2 files changed, 190 insertions(+), 218 deletions(-) diff --git a/src/api/mes/energydevice/index.ts b/src/api/mes/energydevice/index.ts index 0ea5247f..feccb85a 100644 --- a/src/api/mes/energydevice/index.ts +++ b/src/api/mes/energydevice/index.ts @@ -42,6 +42,61 @@ export interface EnergyDeviceDataRecordVO { latestDataTime?: string } +export interface EnergyOverviewMetricVO { + key: string + label: string + value: string + unit?: string + subLabel?: string + subValue?: string + change?: string + down?: boolean +} + +export interface EnergyOverviewTrendVO { + unit?: string + xAxis: string[] + data: string[] +} + +export interface EnergyOverviewRegionItemVO { + name: string + value: string + percent: string +} + +export interface EnergyOverviewRegionVO { + unit?: string + totalValue: string + items: EnergyOverviewRegionItemVO[] +} + +export interface EnergyOverviewRankVO { + id: number + name: string + region: string + value: string +} + +export interface EnergyOverviewDetailVO { + id: number + name: string + energyType: string + region: string + value: string + startTime: string + endTime: string +} + +export interface EnergyOverviewRespVO { + metrics: EnergyOverviewMetricVO[] + trendChart: EnergyOverviewTrendVO + regionChart: EnergyOverviewRegionVO + rankList: EnergyOverviewRankVO[] + total: number + detailList: EnergyOverviewDetailVO[] +} + // 能源设备 API export const EnergyDeviceApi = { // 查询能源设备分页 @@ -66,6 +121,10 @@ export const EnergyDeviceApi = { return await request.get({ url: `/mes/energy-device/queryDataRecords`, params }) }, + queryOverviewData: async (params: any) => { + return await request.get({ url: `/mes/energy-device/queryOverviewData`, params }) + }, + exportQueryDataRecords: async (params: any) => { return await request.download({ url: `/mes/energy-device/record-export-excel`, params }) }, diff --git a/src/views/mes/energyOverview/index.vue b/src/views/mes/energyOverview/index.vue index ea7d7c2f..a674c335 100644 --- a/src/views/mes/energyOverview/index.vue +++ b/src/views/mes/energyOverview/index.vue @@ -9,7 +9,13 @@ label-width="auto" > - + @@ -63,7 +69,7 @@
{{ item.label }}
{{ item.value }} - {{ item.unit }} + {{ item.unit }}
{{ item.subLabel }} @@ -85,14 +91,6 @@ 能源用量趋势
-
- - - - - - -
@@ -104,14 +102,6 @@ 区域能耗占比
-
- - - - - - -
@@ -176,6 +166,14 @@ import { EChartsOption } from 'echarts' import { Echart } from '@/components/Echart' import { EnergyTypeApi, EnergyTypeVO } from '@/api/mes/energytype' +import { + EnergyDeviceApi, + EnergyOverviewDetailVO, + EnergyOverviewMetricVO, + EnergyOverviewRankVO, + EnergyOverviewRespVO +} from '@/api/mes/energydevice' +import {OrganizationApi, OrganizationVO} from "@/api/mes/organization"; defineOptions({ name: 'EnergyOverview' }) @@ -192,24 +190,12 @@ interface MetricCard { subValue?: string } -interface DetailRow { - id: number - name: string - energyType: string - region: string - value: string - startTime: string - endTime: string -} +type DetailRow = EnergyOverviewDetailVO const message = useMessage() const defaultTimeRange = ['2026-05-04 00:00:00', '2026-05-05 23:59:59'] -const orgOptions = [ - { label: '成型系统', value: 1 }, - { label: '蛋托线01', value: 2 }, - { label: '烘干系统', value: 3 } -] +const orgOptions = ref<{ label: string; value: string | number; raw?: OrganizationVO }[]>([]) const loading = ref(false) const queryFormRef = ref() @@ -224,55 +210,19 @@ const queryParams = reactive({ }) const metricCards = ref([]) -const rankList = ref([ - { name: '蛋托成型柜用电量', region: '成型系统', value: '2,139.09' }, - { name: '蛋托线真空柜用电量', region: '蛋托线01', value: '0' }, - { name: '蛋托干燥柜用电量', region: '烘干系统', value: '0' }, - { name: '蛋托线其它用电量', region: '蛋托线01', value: '0' }, - { name: '烘干系统其它用电量', region: '烘干系统', value: '0' } -]) -const detailList = ref([]) +const rankList = ref([]) const pageDetailList = ref([]) const total = ref(0) const hasEnergyTypes = computed(() => energyTypeOptions.value.length > 0) const selectedEnergyType = computed(() => energyTypeOptions.value.find((item) => item.id === queryParams.energyTypeId) ) -const selectedEnergyName = computed(() => selectedEnergyType.value?.name || '电') const selectedEnergyUnit = computed(() => selectedEnergyType.value?.unit || 'kWh') -const selectedUnitConsumptionUnit = computed(() => `${selectedEnergyUnit.value}/件`) - -const hours = [ - '00:00', - '01:00', - '02:00', - '03:00', - '04:00', - '05:00', - '06:00', - '07:00', - '08:00', - '09:00', - '10:00', - '11:00', - '12:00', - '13:00', - '14:00', - '15:00', - '16:00', - '17:00', - '18:00', - '19:00', - '20:00', - '21:00', - '22:00' -] -const trendData = [90, 80, 72, 75, 74, 85, 105, 116, 120, 112, 123, 127, 122, 146, 156.8, 147, 116, 94, 88, 102, 103, 116, 106] -const regionData = [ - { name: '成型系统', value: 2139.09 }, - { name: '蛋托线01', value: 0 }, - { name: '烘干系统', value: 0 } -] + +const trendXAxis = ref([]) +const trendSeries = ref([]) +const regionItems = ref<{ name: string; value: number; percent: string }[]>([]) +const regionTotal = ref('0') const trendChartOptions = reactive({ color: ['#2f7df6'], @@ -280,12 +230,12 @@ const trendChartOptions = reactive({ trigger: 'axis', formatter: (params: any) => { const item = params?.[0] - return `${item.axisValue}
用电量:${item.data} kWh` + return `${item?.axisValue || ''}
用量:${item?.data || 0} ${selectedEnergyUnit.value}` } }, legend: { top: 4, - data: ['用电量 (kWh)'] + data: ['用量'] }, grid: { left: 24, @@ -297,25 +247,23 @@ const trendChartOptions = reactive({ xAxis: { type: 'category', boundaryGap: false, - name: '时间(小时)', + name: '时间', nameLocation: 'middle', nameGap: 28, - data: hours, + data: trendXAxis.value, axisLine: { lineStyle: { color: '#dcdfe6' } }, axisLabel: { color: '#606266' } }, yAxis: { type: 'value', - name: 'kWh', + name: selectedEnergyUnit.value, min: 0, - max: 180, - interval: 30, axisLabel: { color: '#606266' }, splitLine: { lineStyle: { color: '#e8edf5' } } }, series: [ { - name: '用电量 (kWh)', + name: '用量', type: 'line', smooth: true, symbol: 'circle', @@ -334,21 +282,16 @@ const trendChartOptions = reactive({ ] } }, - data: trendData, - markPoint: { - symbolSize: 48, - label: { color: '#2f7df6', formatter: '{c}' }, - data: [{ type: 'max', name: '峰值' }] - } + data: trendSeries.value } ] -}) as EChartsOption +}) const regionPieOptions = reactive({ - color: ['#2f7df6', '#52c41a', '#faad14'], + color: ['#2f7df6', '#52c41a', '#faad14', '#13c2c2', '#fa8c16'], tooltip: { trigger: 'item', - formatter: '{b}
{c} kWh ({d}%)' + formatter: `{b}
{c} ${selectedEnergyUnit.value} ({d}%)` }, legend: { orient: 'vertical', @@ -357,10 +300,10 @@ const regionPieOptions = reactive({ itemWidth: 12, itemHeight: 12, formatter: (name: string) => { - const item = regionData.find((row) => row.name === name) + const item = regionItems.value.find((row) => row.name === name) const value = item?.value ?? 0 - const percent = value > 0 ? '100.00' : '0.00' - return `${name} ${value.toLocaleString()} kWh (${percent}%)` + const percent = item?.percent ?? '0.00' + return `${name} ${value.toLocaleString()} ${selectedEnergyUnit.value} (${percent}%)` } }, series: [ @@ -373,7 +316,7 @@ const regionPieOptions = reactive({ label: { show: true, position: 'center', - formatter: '总用电量\n{total|2,139.09 kWh}', + formatter: `总用量\n{total|${regionTotal.value} ${selectedEnergyUnit.value}}`, color: '#303133', rich: { total: { @@ -385,116 +328,93 @@ const regionPieOptions = reactive({ } }, labelLine: { show: false }, - data: regionData + data: regionItems.value } ] -}) as EChartsOption - -const buildMetrics = () => { - metricCards.value = [ - { - key: 'total', - label: '总用电量', - value: '2,139.09', - unit: selectedEnergyUnit.value, - icon: 'ep:lightning', - theme: 'blue', - subLabel: '较昨日', - change: '12.53%' - }, - { - key: 'today', - label: '今日用电量', - value: '860.32', - unit: selectedEnergyUnit.value, - icon: 'ep:calendar', - theme: 'green', - subLabel: '较昨日', - change: '12.15%' - }, - { - key: 'peak', - label: '峰值用电', - value: '156.8', - unit: selectedEnergyUnit.value, - icon: 'ep:histogram', - theme: 'purple', - subLabel: '时间:', - subValue: '2026-05-04 14:00 ~ 15:00' - }, - { - key: 'unit', - label: '单位产量能耗', - value: '0.38', - unit: selectedUnitConsumptionUnit.value, - icon: 'ep:orange', - theme: 'orange', - subLabel: '较昨日', - change: '5.21%', - down: true - }, - { - key: 'region', - label: '最大耗能区域', - value: '成型系统', - unit: '', - icon: 'ep:pie-chart', - theme: 'cyan', - subLabel: '占比:', - subValue: '100.00%' - } - ] -} +}) -const buildDetails = () => { - const timeRange = queryParams.timeRange?.length === 2 ? queryParams.timeRange : defaultTimeRange - detailList.value = [ - { - id: 1, - name: '蛋托线真空柜用电量', - energyType: selectedEnergyName.value, - region: '蛋托线01', - value: '0', - startTime: timeRange[0], - endTime: timeRange[1] - }, - { - id: 2, - name: '蛋托成型柜用电量', - energyType: selectedEnergyName.value, - region: '成型系统', - value: '2,139.09', - startTime: timeRange[0], - endTime: timeRange[1] - }, - { - id: 3, - name: '蛋托干燥柜用电量', - energyType: selectedEnergyName.value, - region: '烘干系统', - value: '0', - startTime: timeRange[0], - endTime: timeRange[1] +const metricThemeMap: Record = { + total: { icon: 'ep:lightning', theme: 'blue' }, + deviceCount: { icon: 'ep:cpu', theme: 'green' }, + topDevice: { icon: 'ep:histogram', theme: 'purple' }, + topRegion: { icon: 'ep:pie-chart', theme: 'cyan' }, + range: { icon: 'ep:calendar', theme: 'orange' } +} + +const normalizeMetricCards = (metrics: EnergyOverviewMetricVO[]) => { + metricCards.value = metrics.map((item) => ({ + key: item.key, + label: item.label, + value: item.value, + unit: item.unit || '', + icon: metricThemeMap[item.key]?.icon || 'ep:data-line', + theme: metricThemeMap[item.key]?.theme || 'blue', + subLabel: item.subLabel || '', + subValue: item.subValue || '', + change: item.change, + down: item.down + })) +} + +const updateCharts = (data: EnergyOverviewRespVO) => { + trendXAxis.value = data.trendChart?.xAxis || [] + trendSeries.value = (data.trendChart?.data || []).map((item) => Number(item) || 0) + regionItems.value = (data.regionChart?.items || []).map((item) => ({ + name: item.name, + value: Number(item.value) || 0, + percent: item.percent + })) + regionTotal.value = data.regionChart?.totalValue || '0' + + trendChartOptions.xAxis = { + ...(trendChartOptions.xAxis as any), + data: trendXAxis.value + } + trendChartOptions.yAxis = { + ...(trendChartOptions.yAxis as any), + name: selectedEnergyUnit.value + } + if (Array.isArray(trendChartOptions.series) && trendChartOptions.series[0]) { + ;(trendChartOptions.series[0] as any).data = trendSeries.value + } + if (Array.isArray(regionPieOptions.series) && regionPieOptions.series[0]) { + ;(regionPieOptions.series[0] as any).data = regionItems.value + ;(regionPieOptions.series[0] as any).label = { + ...(regionPieOptions.series[0] as any).label, + formatter: `总用量\n{total|${regionTotal.value} ${selectedEnergyUnit.value}}` } - ] - total.value = detailList.value.length + } } -const getList = () => { +const getList = async () => { if (!hasEnergyTypes.value) { metricCards.value = [] - detailList.value = [] + rankList.value = [] pageDetailList.value = [] total.value = 0 return } loading.value = true - buildMetrics() - buildDetails() - const start = (queryParams.pageNo - 1) * queryParams.pageSize - pageDetailList.value = detailList.value.slice(start, start + queryParams.pageSize) - loading.value = false + try { + const timeRange = queryParams.timeRange?.length === 2 ? queryParams.timeRange : defaultTimeRange + const res = await EnergyDeviceApi.queryOverviewData({ + orgId: queryParams.orgId, + energyTypeId: queryParams.energyTypeId, + startTime: timeRange[0], + endTime: timeRange[1], + pageNo: queryParams.pageNo, + pageSize: queryParams.pageSize + }) + const data = ((res as any)?.data || res) as EnergyOverviewRespVO + normalizeMetricCards(data.metrics || []) + rankList.value = data.rankList || [] + pageDetailList.value = data.detailList || [] + total.value = data.total || 0 + updateCharts(data) + } finally { + loading.value = false + } } const normalizeEnergyTypeList = (data: unknown): EnergyTypeVO[] => { @@ -508,14 +428,7 @@ const normalizeEnergyTypeList = (data: unknown): EnergyTypeVO[] => { return [] } -const getDefaultEnergyType = (list: EnergyTypeVO[]) => { - return ( - list.find((item) => item.name === '电') || - list.find((item) => item.name === '水') || - list.find((item) => item.name === '气') || - list[0] - ) -} +const getDefaultEnergyType = (list: EnergyTypeVO[]) => list[0] const getEnergyTypes = async () => { try { @@ -531,10 +444,6 @@ const getEnergyTypes = async () => { } const handleQuery = () => { - if (!hasEnergyTypes.value) { - getList() - return - } queryParams.pageNo = 1 getList() } @@ -547,11 +456,28 @@ const resetQuery = () => { handleQuery() } -const handleExport = () => { - message.info('示例页暂未接入导出接口') +const handleExport = async () => { + const timeRange = queryParams.timeRange?.length === 2 ? queryParams.timeRange : defaultTimeRange + await EnergyDeviceApi.exportQueryDataRecords({ + orgId: queryParams.orgId, + startTime: timeRange[0], + endTime: timeRange[1] + }) + message.success('导出已发起') +} + +const loadOrgOptions = async () => { + if (orgOptions.value.length) return + const data: any = await OrganizationApi.getOrganizationList({}) + const rows = (Array.isArray(data) ? data : data?.list ?? data?.data ?? []) as OrganizationVO[] + orgOptions.value = rows + .filter((r) => r?.id !== undefined && r?.name) + .map((r) => ({ label: r.name, value: r.id, raw: r })) } + onMounted(async () => { + await loadOrgOptions() await getEnergyTypes() if (hasEnergyTypes.value) { getList() @@ -705,19 +631,6 @@ onMounted(async () => { color: var(--el-color-primary-light-3); } -.panel-actions { - display: inline-flex; - align-items: center; - gap: 4px; - - .el-button { - width: 30px; - height: 30px; - padding: 0; - border: 1px solid var(--el-border-color-light); - } -} - .rank-index { display: inline-flex; width: 22px; From 9b82f41b375c85c81d1c006a815eeaaafa948c55 Mon Sep 17 00:00:00 2001 From: liutao <790864623@qq.com> Date: Thu, 14 May 2026 15:47:08 +0800 Subject: [PATCH 249/265] =?UTF-8?q?=E8=83=BD=E8=80=97=E6=A6=82=E8=A7=88?= =?UTF-8?q?=E7=9A=84=E4=B8=AD=E8=8B=B1=E6=96=87=E5=88=87=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/locales/en.ts | 34 ++++++++++ src/locales/zh-CN.ts | 34 ++++++++++ src/views/mes/energyOverview/index.vue | 88 +++++++++++++++----------- 3 files changed, 120 insertions(+), 36 deletions(-) diff --git a/src/locales/en.ts b/src/locales/en.ts index ed859725..42a9b56f 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -4722,5 +4722,39 @@ export default { warningNoPlanData: 'No plan data to save', saveSuccess: 'Schedule saved successfully' } + }, + EnergyOverview: { + filters: { + org: 'Region', + orgPlaceholder: 'Please select region', + energyType: 'Energy Type', + energyTypePlaceholder: 'Please select energy type', + timeRange: 'Time Range' + }, + panels: { + trend: 'Energy Usage Trend', + region: 'Regional Energy Share', + top5: 'Top 5 Energy Consumers', + detail: 'Energy Details' + }, + table: { + rank: 'Rank', + name: 'Name', + region: 'Region', + usage: 'Usage', + meterName: 'Meter Name', + energyType: 'Energy Type', + energyUsage: 'Energy Usage', + startTime: 'Start Time', + endTime: 'End Time' + }, + chart: { + usage: 'Usage', + time: 'Time', + regionEnergy: 'Regional Energy', + totalUsage: 'Total Usage' + }, + empty: 'Please configure energy types first', + exportSuccess: 'Export started' } } diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index 0d226c27..36f12d80 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -4933,5 +4933,39 @@ export default { warningNoPlanData: '暂无可保存的计划数据', saveSuccess: '排产计划保存成功' } + }, + EnergyOverview: { + filters: { + org: '所属区域', + orgPlaceholder: '请选择所属区域', + energyType: '能源类型', + energyTypePlaceholder: '请选择能源类型', + timeRange: '时间范围' + }, + panels: { + trend: '能源用量趋势', + region: '区域能耗占比', + top5: '能耗排行 TOP5', + detail: '能源明细表' + }, + table: { + rank: '排名', + name: '名称', + region: '所属区域', + usage: '用量', + meterName: '表名称', + energyType: '能源类型', + energyUsage: '能源用量', + startTime: '开始时间', + endTime: '结束时间' + }, + chart: { + usage: '用量', + time: '时间', + regionEnergy: '区域能耗', + totalUsage: '总用量' + }, + empty: '请先配置能源类型', + exportSuccess: '导出已发起' } } diff --git a/src/views/mes/energyOverview/index.vue b/src/views/mes/energyOverview/index.vue index a674c335..e96bd42f 100644 --- a/src/views/mes/energyOverview/index.vue +++ b/src/views/mes/energyOverview/index.vue @@ -8,35 +8,35 @@ :inline="true" label-width="auto" > - + - + - + - 查询 + {{ t('common.query') }} - 重置 + {{ t('common.reset') }} - 导出 + {{ t('action.export') }} @@ -88,7 +88,7 @@
- 能源用量趋势 + {{ t('EnergyOverview.panels.trend') }}
@@ -99,7 +99,7 @@
- 区域能耗占比 + {{ t('EnergyOverview.panels.region') }}
@@ -113,19 +113,24 @@
- 能耗排行 TOP5 + {{ t('EnergyOverview.panels.top5') }}
- + - - - + + +
@@ -133,17 +138,22 @@
- 能源明细表 + {{ t('EnergyOverview.panels.detail') }}
- - - - - - + + + + + + - +
@@ -173,7 +183,7 @@ import { EnergyOverviewRankVO, EnergyOverviewRespVO } from '@/api/mes/energydevice' -import {OrganizationApi, OrganizationVO} from "@/api/mes/organization"; +import { OrganizationApi, OrganizationVO } from '@/api/mes/organization' defineOptions({ name: 'EnergyOverview' }) @@ -192,6 +202,7 @@ interface MetricCard { type DetailRow = EnergyOverviewDetailVO +const { t } = useI18n() const message = useMessage() const defaultTimeRange = ['2026-05-04 00:00:00', '2026-05-05 23:59:59'] @@ -230,12 +241,12 @@ const trendChartOptions = reactive({ trigger: 'axis', formatter: (params: any) => { const item = params?.[0] - return `${item?.axisValue || ''}
用量:${item?.data || 0} ${selectedEnergyUnit.value}` + return `${item?.axisValue || ''}
${t('EnergyOverview.chart.usage')}:${item?.data || 0} ${selectedEnergyUnit.value}` } }, legend: { top: 4, - data: ['用量'] + data: [t('EnergyOverview.chart.usage')] }, grid: { left: 24, @@ -247,7 +258,7 @@ const trendChartOptions = reactive({ xAxis: { type: 'category', boundaryGap: false, - name: '时间', + name: t('EnergyOverview.chart.time'), nameLocation: 'middle', nameGap: 28, data: trendXAxis.value, @@ -263,7 +274,7 @@ const trendChartOptions = reactive({ }, series: [ { - name: '用量', + name: t('EnergyOverview.chart.usage'), type: 'line', smooth: true, symbol: 'circle', @@ -308,7 +319,7 @@ const regionPieOptions = reactive({ }, series: [ { - name: '区域能耗', + name: t('EnergyOverview.chart.regionEnergy'), type: 'pie', radius: ['58%', '76%'], center: ['34%', '50%'], @@ -316,7 +327,7 @@ const regionPieOptions = reactive({ label: { show: true, position: 'center', - formatter: `总用量\n{total|${regionTotal.value} ${selectedEnergyUnit.value}}`, + formatter: `${t('EnergyOverview.chart.totalUsage')}\n{total|${regionTotal.value} ${selectedEnergyUnit.value}}`, color: '#303133', rich: { total: { @@ -368,20 +379,26 @@ const updateCharts = (data: EnergyOverviewRespVO) => { trendChartOptions.xAxis = { ...(trendChartOptions.xAxis as any), - data: trendXAxis.value + data: trendXAxis.value, + name: t('EnergyOverview.chart.time') } trendChartOptions.yAxis = { ...(trendChartOptions.yAxis as any), name: selectedEnergyUnit.value } + if (Array.isArray(trendChartOptions.legend?.data)) { + trendChartOptions.legend.data = [t('EnergyOverview.chart.usage')] + } if (Array.isArray(trendChartOptions.series) && trendChartOptions.series[0]) { + ;(trendChartOptions.series[0] as any).name = t('EnergyOverview.chart.usage') ;(trendChartOptions.series[0] as any).data = trendSeries.value } if (Array.isArray(regionPieOptions.series) && regionPieOptions.series[0]) { + ;(regionPieOptions.series[0] as any).name = t('EnergyOverview.chart.regionEnergy') ;(regionPieOptions.series[0] as any).data = regionItems.value ;(regionPieOptions.series[0] as any).label = { ...(regionPieOptions.series[0] as any).label, - formatter: `总用量\n{total|${regionTotal.value} ${selectedEnergyUnit.value}}` + formatter: `${t('EnergyOverview.chart.totalUsage')}\n{total|${regionTotal.value} ${selectedEnergyUnit.value}}` } } } @@ -463,7 +480,7 @@ const handleExport = async () => { startTime: timeRange[0], endTime: timeRange[1] }) - message.success('导出已发起') + message.success(t('EnergyOverview.exportSuccess')) } const loadOrgOptions = async () => { @@ -475,7 +492,6 @@ const loadOrgOptions = async () => { .map((r) => ({ label: r.name, value: r.id, raw: r })) } - onMounted(async () => { await loadOrgOptions() await getEnergyTypes() From db1e813950d9f5d304bc76b2ce78e357487509a3 Mon Sep 17 00:00:00 2001 From: liutao <790864623@qq.com> Date: Mon, 18 May 2026 08:07:44 +0800 Subject: [PATCH 250/265] =?UTF-8?q?=E6=97=B6=E9=97=B4=E9=99=90=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/locales/en.ts | 3 + src/locales/zh-CN.ts | 3 + src/views/mes/energyOverview/index.vue | 113 +++++++++++++++++++++++-- 3 files changed, 113 insertions(+), 6 deletions(-) diff --git a/src/locales/en.ts b/src/locales/en.ts index 42a9b56f..17c11117 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -4724,6 +4724,9 @@ export default { } }, EnergyOverview: { + messages: { + singleDayOnly: 'The time range can only query data within a single day' + }, filters: { org: 'Region', orgPlaceholder: 'Please select region', diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index 36f12d80..5ca068dc 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -4935,6 +4935,9 @@ export default { } }, EnergyOverview: { + messages: { + singleDayOnly: '时间范围只能查询同一天的数据' + }, filters: { org: '所属区域', orgPlaceholder: '请选择所属区域', diff --git a/src/views/mes/energyOverview/index.vue b/src/views/mes/energyOverview/index.vue index e96bd42f..f525cba0 100644 --- a/src/views/mes/energyOverview/index.vue +++ b/src/views/mes/energyOverview/index.vue @@ -205,7 +205,34 @@ type DetailRow = EnergyOverviewDetailVO const { t } = useI18n() const message = useMessage() -const defaultTimeRange = ['2026-05-04 00:00:00', '2026-05-05 23:59:59'] +const getTodayTimeRange = () => { + const now = new Date() + const year = now.getFullYear() + const month = String(now.getMonth() + 1).padStart(2, '0') + const day = String(now.getDate()).padStart(2, '0') + const date = `${year}-${month}-${day}` + return [`${date} 00:00:00`, `${date} 23:59:59`] +} + +const parseRangeTime = (value?: string) => { + if (!value) return undefined + const date = new Date(value.replace(/-/g, '/')) + return Number.isNaN(date.getTime()) ? undefined : date +} + +const isSingleDayRange = (timeRange?: string[]) => { + if (!timeRange || timeRange.length !== 2) return false + const start = parseRangeTime(timeRange[0]) + const end = parseRangeTime(timeRange[1]) + if (!start || !end) return false + return ( + start.getFullYear() === end.getFullYear() && + start.getMonth() === end.getMonth() && + start.getDate() === end.getDate() + ) +} + +const defaultTimeRange = getTodayTimeRange() const orgOptions = ref<{ label: string; value: string | number; raw?: OrganizationVO }[]>([]) const loading = ref(false) @@ -352,6 +379,47 @@ const metricThemeMap: Record = { range: { icon: 'ep:calendar', theme: 'orange' } } +const buildFallbackTrendXAxis = () => { + const timeRange = queryParams.timeRange?.length === 2 ? queryParams.timeRange : defaultTimeRange + const [startTime, endTime] = timeRange + const start = new Date(startTime.replace(/-/g, '/')) + const end = new Date(endTime.replace(/-/g, '/')) + if (Number.isNaN(start.getTime()) || Number.isNaN(end.getTime()) || start > end) { + return [] + } + + const sameDay = + start.getFullYear() === end.getFullYear() && + start.getMonth() === end.getMonth() && + start.getDate() === end.getDate() + + const axis: string[] = [] + if (sameDay) { + const current = new Date(start) + current.setMinutes(0, 0, 0) + const limit = new Date(end) + limit.setMinutes(0, 0, 0) + while (current <= limit) { + axis.push(`${String(current.getHours()).padStart(2, '0')}:00`) + current.setHours(current.getHours() + 1) + } + return axis + } + + const current = new Date(start) + current.setHours(0, 0, 0, 0) + const limit = new Date(end) + limit.setHours(0, 0, 0, 0) + while (current <= limit) { + const year = current.getFullYear() + const month = String(current.getMonth() + 1).padStart(2, '0') + const day = String(current.getDate()).padStart(2, '0') + axis.push(`${year}-${month}-${day}`) + current.setDate(current.getDate() + 1) + } + return axis +} + const normalizeMetricCards = (metrics: EnergyOverviewMetricVO[]) => { metricCards.value = metrics.map((item) => ({ key: item.key, @@ -368,8 +436,13 @@ const normalizeMetricCards = (metrics: EnergyOverviewMetricVO[]) => { } const updateCharts = (data: EnergyOverviewRespVO) => { - trendXAxis.value = data.trendChart?.xAxis || [] - trendSeries.value = (data.trendChart?.data || []).map((item) => Number(item) || 0) + const fallbackXAxis = buildFallbackTrendXAxis() + trendXAxis.value = data.trendChart?.xAxis?.length ? data.trendChart.xAxis : fallbackXAxis + const responseSeries = (data.trendChart?.data || []).map((item) => Number(item) || 0) + trendSeries.value = + responseSeries.length > 0 + ? responseSeries + : new Array(trendXAxis.value.length).fill(0) regionItems.value = (data.regionChart?.items || []).map((item) => ({ name: item.name, value: Number(item.value) || 0, @@ -449,18 +522,31 @@ const getDefaultEnergyType = (list: EnergyTypeVO[]) => list[0] const getEnergyTypes = async () => { try { - const data = await EnergyTypeApi.getEnergyTypeList() + const data = await EnergyTypeApi.getEnergyTypeList({ orgId: queryParams.orgId }) energyTypeOptions.value = normalizeEnergyTypeList(data) } catch (error) { energyTypeOptions.value = [] } - const defaultEnergyType = getDefaultEnergyType(energyTypeOptions.value) + let defaultEnergyType = getDefaultEnergyType(energyTypeOptions.value) + try { + const deviceRes = await EnergyDeviceApi.getList({ orgId: queryParams.orgId }) + const devices = ((deviceRes as any)?.data || deviceRes || []) as Array<{ deviceTypeId?: number }> + const deviceTypeIds = new Set(devices.map((item) => item.deviceTypeId).filter(Boolean)) + defaultEnergyType = + energyTypeOptions.value.find((item) => deviceTypeIds.has(item.id)) || defaultEnergyType + } catch (error) { + // ignore and use the first energy type + } defaultEnergyTypeId.value = defaultEnergyType?.id queryParams.energyTypeId = defaultEnergyType?.id } const handleQuery = () => { + if (!isSingleDayRange(queryParams.timeRange)) { + message.warning(t('EnergyOverview.messages.singleDayOnly')) + return + } queryParams.pageNo = 1 getList() } @@ -469,11 +555,15 @@ const resetQuery = () => { queryFormRef.value?.resetFields() queryParams.orgId = undefined queryParams.energyTypeId = defaultEnergyTypeId.value - queryParams.timeRange = [...defaultTimeRange] + queryParams.timeRange = getTodayTimeRange() handleQuery() } const handleExport = async () => { + if (!isSingleDayRange(queryParams.timeRange)) { + message.warning(t('EnergyOverview.messages.singleDayOnly')) + return + } const timeRange = queryParams.timeRange?.length === 2 ? queryParams.timeRange : defaultTimeRange await EnergyDeviceApi.exportQueryDataRecords({ orgId: queryParams.orgId, @@ -499,6 +589,17 @@ onMounted(async () => { getList() } }) + +watch( + () => queryParams.orgId, + async () => { + await getEnergyTypes() + queryParams.pageNo = 1 + if (hasEnergyTypes.value) { + getList() + } + } +) + + \ No newline at end of file diff --git a/src/components/Icon/src/appIconData.ts b/src/components/Icon/src/appIconData.ts new file mode 100644 index 00000000..9117847c --- /dev/null +++ b/src/components/Icon/src/appIconData.ts @@ -0,0 +1,382 @@ +const uniIconsUnicodeMap: Record = { + 'arrow-down': '\ue6be', + 'arrow-left': '\ue6bc', + 'arrow-right': '\ue6bb', + 'arrow-up': '\ue6bd', + 'auth': '\ue6ab', + 'auth-filled': '\ue6cc', + 'back': '\ue6b9', + 'bars': '\ue627', + 'calendar': '\ue6a0', + 'calendar-filled': '\ue6c0', + 'camera': '\ue65a', + 'camera-filled': '\ue658', + 'cart': '\ue631', + 'cart-filled': '\ue6d0', + 'chat': '\ue65d', + 'chat-filled': '\ue659', + 'chatboxes': '\ue696', + 'chatboxes-filled': '\ue692', + 'chatbubble': '\ue697', + 'chatbubble-filled': '\ue694', + 'checkbox': '\ue62b', + 'checkbox-filled': '\ue62c', + 'checkmarkempty': '\ue65c', + 'circle': '\ue65b', + 'circle-filled': '\ue65e', + 'clear': '\ue66d', + 'close': '\ue673', + 'closeempty': '\ue66c', + 'cloud-download': '\ue647', + 'cloud-download-filled': '\ue646', + 'cloud-upload': '\ue645', + 'cloud-upload-filled': '\ue648', + 'color': '\ue6cf', + 'color-filled': '\ue6c9', + 'compose': '\ue67f', + 'contact': '\ue693', + 'contact-filled': '\ue695', + 'down': '\ue6b8', + 'bottom': '\ue6b8', + 'download': '\ue68d', + 'download-filled': '\ue681', + 'email': '\ue69e', + 'email-filled': '\ue69a', + 'eye': '\ue651', + 'eye-filled': '\ue66a', + 'eye-slash': '\ue6b3', + 'eye-slash-filled': '\ue6b4', + 'fire': '\ue6a1', + 'fire-filled': '\ue6c5', + 'flag': '\ue65f', + 'flag-filled': '\ue660', + 'folder-add': '\ue6a9', + 'folder-add-filled': '\ue6c8', + 'font': '\ue6a3', + 'forward': '\ue6ba', + 'gear': '\ue664', + 'gear-filled': '\ue661', + 'gift': '\ue6a4', + 'gift-filled': '\ue6c4', + 'hand-down': '\ue63d', + 'hand-down-filled': '\ue63c', + 'hand-up': '\ue63f', + 'hand-up-filled': '\ue63e', + 'headphones': '\ue630', + 'heart': '\ue639', + 'heart-filled': '\ue641', + 'help': '\ue679', + 'help-filled': '\ue674', + 'home': '\ue662', + 'home-filled': '\ue663', + 'image': '\ue670', + 'image-filled': '\ue678', + 'images': '\ue650', + 'images-filled': '\ue64b', + 'info': '\ue669', + 'info-filled': '\ue649', + 'left': '\ue6b7', + 'link': '\ue6a5', + 'list': '\ue644', + 'location': '\ue6ae', + 'location-filled': '\ue6af', + 'locked': '\ue66b', + 'locked-filled': '\ue668', + 'loop': '\ue633', + 'mail-open': '\ue643', + 'mail-open-filled': '\ue63a', + 'map': '\ue667', + 'map-filled': '\ue666', + 'map-pin': '\ue6ad', + 'map-pin-ellipse': '\ue6ac', + 'medal': '\ue6a2', + 'medal-filled': '\ue6c3', + 'mic': '\ue671', + 'mic-filled': '\ue677', + 'micoff': '\ue67e', + 'micoff-filled': '\ue6b0', + 'minus': '\ue66f', + 'minus-filled': '\ue67d', + 'more': '\ue64d', + 'more-filled': '\ue64e', + 'navigate': '\ue66e', + 'navigate-filled': '\ue67a', + 'notification': '\ue6a6', + 'notification-filled': '\ue6c1', + 'paperclip': '\ue652', + 'paperplane': '\ue672', + 'paperplane-filled': '\ue675', + 'person': '\ue699', + 'person-filled': '\ue69d', + 'personadd': '\ue69f', + 'personadd-filled': '\ue698', + 'phone': '\ue69c', + 'phone-filled': '\ue69b', + 'plus': '\ue676', + 'plus-filled': '\ue6c7', + 'plusempty': '\ue67b', + 'pulldown': '\ue632', + 'pyq': '\ue682', + 'qq': '\ue680', + 'redo': '\ue64a', + 'redo-filled': '\ue655', + 'refresh': '\ue657', + 'refresh-filled': '\ue656', + 'refreshempty': '\ue6bf', + 'reload': '\ue6b2', + 'right': '\ue6b5', + 'scan': '\ue62a', + 'search': '\ue654', + 'settings': '\ue653', + 'settings-filled': '\ue6ce', + 'shop': '\ue62f', + 'shop-filled': '\ue6cd', + 'smallcircle': '\ue67c', + 'smallcircle-filled': '\ue665', + 'sound': '\ue684', + 'sound-filled': '\ue686', + 'spinner-cycle': '\ue68a', + 'staff': '\ue6a7', + 'staff-filled': '\ue6cb', + 'star': '\ue688', + 'star-filled': '\ue68f', + 'starhalf': '\ue683', + 'trash': '\ue687', + 'trash-filled': '\ue685', + 'tune': '\ue6aa', + 'tune-filled': '\ue6ca', + 'undo': '\ue64f', + 'undo-filled': '\ue64c', + 'up': '\ue6b6', + 'top': '\ue6b6', + 'upload': '\ue690', + 'upload-filled': '\ue68e', + 'videocam': '\ue68c', + 'videocam-filled': '\ue689', + 'vip': '\ue6a8', + 'vip-filled': '\ue6c6', + 'wallet': '\ue6b1', + 'wallet-filled': '\ue6c2', + 'weibo': '\ue68b', + 'weixin': '\ue691' +} + +const uviewIconsUnicodeMap: Record = { + 'level': '\ue693', + 'column-line': '\ue68e', + 'checkbox-mark': '\ue807', + 'folder': '\ue7f5', + 'movie': '\ue7f6', + 'star-fill': '\ue669', + 'star': '\ue65f', + 'phone-fill': '\ue64f', + 'phone': '\ue622', + 'apple-fill': '\ue881', + 'chrome-circle-fill': '\ue885', + 'backspace': '\ue67b', + 'attach': '\ue632', + 'cut': '\ue948', + 'empty-car': '\ue602', + 'empty-coupon': '\ue682', + 'empty-address': '\ue646', + 'empty-favor': '\ue67c', + 'empty-permission': '\ue686', + 'empty-news': '\ue687', + 'empty-search': '\ue664', + 'github-circle-fill': '\ue887', + 'rmb': '\ue608', + 'person-delete-fill': '\ue66a', + 'reload': '\ue788', + 'order': '\ue68f', + 'server-man': '\ue6bc', + 'search': '\ue62a', + 'fingerprint': '\ue955', + 'more-dot-fill': '\ue630', + 'scan': '\ue662', + 'share-square': '\ue60b', + 'map': '\ue61d', + 'map-fill': '\ue64e', + 'tags': '\ue629', + 'tags-fill': '\ue651', + 'bookmark-fill': '\ue63b', + 'bookmark': '\ue60a', + 'eye': '\ue613', + 'eye-fill': '\ue641', + 'mic': '\ue64a', + 'mic-off': '\ue649', + 'calendar': '\ue66e', + 'calendar-fill': '\ue634', + 'trash': '\ue623', + 'trash-fill': '\ue658', + 'play-left': '\ue66d', + 'play-right': '\ue610', + 'minus': '\ue618', + 'plus': '\ue62d', + 'info': '\ue653', + 'info-circle': '\ue7d2', + 'info-circle-fill': '\ue64b', + 'question': '\ue715', + 'error': '\ue6d3', + 'close': '\ue685', + 'checkmark': '\ue6a8', + 'android-circle-fill': '\ue67e', + 'android-fill': '\ue67d', + 'ie': '\ue87b', + 'IE-circle-fill': '\ue889', + 'google': '\ue87a', + 'google-circle-fill': '\ue88a', + 'setting-fill': '\ue872', + 'setting': '\ue61f', + 'minus-square-fill': '\ue855', + 'plus-square-fill': '\ue856', + 'heart': '\ue7df', + 'heart-fill': '\ue851', + 'camera': '\ue7d7', + 'camera-fill': '\ue870', + 'more-circle': '\ue63e', + 'more-circle-fill': '\ue645', + 'chat': '\ue620', + 'chat-fill': '\ue61e', + 'bag-fill': '\ue617', + 'bag': '\ue619', + 'error-circle-fill': '\ue62c', + 'error-circle': '\ue624', + 'close-circle': '\ue63f', + 'close-circle-fill': '\ue637', + 'checkmark-circle': '\ue63d', + 'checkmark-circle-fill': '\ue635', + 'question-circle-fill': '\ue666', + 'question-circle': '\ue625', + 'share': '\ue631', + 'share-fill': '\ue65e', + 'shopping-cart': '\ue621', + 'shopping-cart-fill': '\ue65d', + 'bell': '\ue609', + 'bell-fill': '\ue640', + 'list': '\ue650', + 'list-dot': '\ue616', + 'zhihu': '\ue6ba', + 'zhihu-circle-fill': '\ue709', + 'zhifubao': '\ue6b9', + 'zhifubao-circle-fill': '\ue6b8', + 'weixin-circle-fill': '\ue6b1', + 'weixin-fill': '\ue6b2', + 'twitter-circle-fill': '\ue6ab', + 'twitter': '\ue6aa', + 'taobao-circle-fill': '\ue6a7', + 'taobao': '\ue6a6', + 'weibo-circle-fill': '\ue6a5', + 'weibo': '\ue6a4', + 'qq-fill': '\ue6a1', + 'qq-circle-fill': '\ue6a0', + 'moments-circel-fill': '\ue69a', + 'moments': '\ue69b', + 'qzone': '\ue695', + 'qzone-circle-fill': '\ue696', + 'baidu-circle-fill': '\ue680', + 'baidu': '\ue681', + 'facebook-circle-fill': '\ue68a', + 'facebook': '\ue689', + 'car': '\ue60c', + 'car-fill': '\ue636', + 'warning-fill': '\ue64d', + 'warning': '\ue694', + 'clock-fill': '\ue638', + 'clock': '\ue60f', + 'edit-pen': '\ue612', + 'edit-pen-fill': '\ue66b', + 'email': '\ue611', + 'email-fill': '\ue642', + 'minus-circle': '\ue61b', + 'minus-circle-fill': '\ue652', + 'plus-circle': '\ue62e', + 'plus-circle-fill': '\ue661', + 'file-text': '\ue663', + 'file-text-fill': '\ue665', + 'pushpin': '\ue7e3', + 'pushpin-fill': '\ue86e', + 'grid': '\ue673', + 'grid-fill': '\ue678', + 'play-circle': '\ue647', + 'play-circle-fill': '\ue655', + 'pause-circle-fill': '\ue654', + 'pause': '\ue8fa', + 'pause-circle': '\ue643', + 'eye-off': '\ue648', + 'eye-off-outline': '\ue62b', + 'gift-fill': '\ue65c', + 'gift': '\ue65b', + 'rmb-circle-fill': '\ue657', + 'rmb-circle': '\ue677', + 'kefu-ermai': '\ue656', + 'server-fill': '\ue751', + 'coupon-fill': '\ue8c4', + 'coupon': '\ue8ae', + 'integral': '\ue704', + 'integral-fill': '\ue703', + 'home-fill': '\ue964', + 'home': '\ue965', + 'hourglass-half-fill': '\ue966', + 'hourglass': '\ue967', + 'account': '\ue628', + 'plus-people-fill': '\ue626', + 'minus-people-fill': '\ue615', + 'account-fill': '\ue614', + 'thumb-down-fill': '\ue726', + 'thumb-down': '\ue727', + 'thumb-up': '\ue733', + 'thumb-up-fill': '\ue72f', + 'lock-fill': '\ue979', + 'lock-open': '\ue973', + 'lock-opened-fill': '\ue974', + 'lock': '\ue97a', + 'red-packet-fill': '\ue690', + 'photo-fill': '\ue98b', + 'photo': '\ue98d', + 'volume-off-fill': '\ue659', + 'volume-off': '\ue644', + 'volume-fill': '\ue670', + 'volume': '\ue633', + 'red-packet': '\ue691', + 'download': '\ue63c', + 'arrow-up-fill': '\ue6b0', + 'arrow-down-fill': '\ue600', + 'play-left-fill': '\ue675', + 'play-right-fill': '\ue676', + 'rewind-left-fill': '\ue679', + 'rewind-right-fill': '\ue67a', + 'arrow-downward': '\ue604', + 'arrow-leftward': '\ue601', + 'arrow-rightward': '\ue603', + 'arrow-upward': '\ue607', + 'arrow-down': '\ue60d', + 'arrow-right': '\ue605', + 'arrow-left': '\ue60e', + 'arrow-up': '\ue606', + 'skip-back-left': '\ue674', + 'skip-forward-right': '\ue672', + 'rewind-right': '\ue66f', + 'rewind-left': '\ue671', + 'arrow-right-double': '\ue68d', + 'arrow-left-double': '\ue68c', + 'wifi-off': '\ue668', + 'wifi': '\ue667', + 'empty-data': '\ue62f', + 'empty-history': '\ue684', + 'empty-list': '\ue68b', + 'empty-page': '\ue627', + 'empty-order': '\ue639', + 'man': '\ue697', + 'woman': '\ue69c', + 'man-add': '\ue61c', + 'man-add-fill': '\ue64c', + 'man-delete': '\ue61a', + 'man-delete-fill': '\ue66a', + 'zh': '\ue70a', + 'en': '\ue692' +} + +const uniIconsList = Object.keys(uniIconsUnicodeMap) +const uviewIconsList = Object.keys(uviewIconsUnicodeMap) + +export { uniIconsList, uviewIconsList, uniIconsUnicodeMap, uviewIconsUnicodeMap } diff --git a/src/locales/en.ts b/src/locales/en.ts index ed859725..0558b2c3 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -1144,6 +1144,8 @@ export default { refreshCache: 'Refresh Menu Cache', updateCacheConfirm: 'The cache will be updated and the browser will be refreshed!', updateCacheTitle: 'Refresh Menu Cache', + clientTypeWeb: 'Web Menu', + clientTypeApp: 'APP Menu', visibleShow: 'Show', visibleHide: 'Hide', always: 'Always', @@ -2805,6 +2807,9 @@ export default { templateName: 'Template Name', templateType: 'Template Type', templateJson: 'Template JSON', + isConfigured: 'Configured', + configured: 'Configured', + notConfigured: 'Not Configured', remark: 'Remark', isEnable: 'Enabled', enabled: 'Enabled', diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index 0d226c27..abe15b57 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -1145,6 +1145,8 @@ export default { refreshCache: '刷新菜单缓存', updateCacheConfirm: '即将更新缓存刷新浏览器!', updateCacheTitle: '刷新菜单缓存', + clientTypeWeb: 'Web端菜单', + clientTypeApp: 'APP端菜单', visibleShow: '显示', visibleHide: '隐藏', always: '总是', @@ -2307,6 +2309,9 @@ export default { templateName: '模板名称', templateType: '模板类型', templateJson: '模板JSON', + isConfigured: '是否已配置', + configured: '已配置', + notConfigured: '未配置', remark: '备注', isEnable: '是否启用', enabled: '启用', diff --git a/src/views/mes/printTemplate/index.vue b/src/views/mes/printTemplate/index.vue index 5e9b09c6..7d811861 100644 --- a/src/views/mes/printTemplate/index.vue +++ b/src/views/mes/printTemplate/index.vue @@ -50,8 +50,13 @@ - + + + diff --git a/src/views/system/menu/MenuForm.vue b/src/views/system/menu/MenuForm.vue index db1a42be..dd281540 100644 --- a/src/views/system/menu/MenuForm.vue +++ b/src/views/system/menu/MenuForm.vue @@ -36,7 +36,8 @@ - + + diff --git a/src/views/mes/printTemplate/PrintTemplateDesigner.vue b/src/views/mes/printTemplate/PrintTemplateDesigner.vue index 57a9ca46..9d90648b 100644 --- a/src/views/mes/printTemplate/PrintTemplateDesigner.vue +++ b/src/views/mes/printTemplate/PrintTemplateDesigner.vue @@ -354,6 +354,7 @@ const handleSave = async () => { templateCode: currentRow.value.templateCode, templateName: currentRow.value.templateName, templateType: currentRow.value.templateType, + templateBizType: currentRow.value.templateBizType ?? 1, templateJson: JSON.stringify(templateJson), } as any) message.success(t('common.updateSuccess')) diff --git a/src/views/mes/printTemplate/ReportTemplateDesigner.vue b/src/views/mes/printTemplate/ReportTemplateDesigner.vue index b72f83c4..7b766a99 100644 --- a/src/views/mes/printTemplate/ReportTemplateDesigner.vue +++ b/src/views/mes/printTemplate/ReportTemplateDesigner.vue @@ -593,6 +593,7 @@ const handleSave = async () => { templateCode: currentRow.value.templateCode, templateName: currentRow.value.templateName, templateType: currentRow.value.templateType, + templateBizType: currentRow.value.templateBizType ?? 2, templateJson: JSON.stringify(templateJson), } as any) message.success(t('common.updateSuccess')) From 7e5252d990c0710b62a97f378bb646ec06618e46 Mon Sep 17 00:00:00 2001 From: hwj Date: Mon, 25 May 2026 16:58:35 +0800 Subject: [PATCH 263/265] =?UTF-8?q?feat=EF=BC=9A=E8=8F=9C=E5=8D=95?= =?UTF-8?q?=E7=AE=A1=E7=90=86-app=E7=AB=AF=E8=8F=9C=E5=8D=95=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E7=BB=88=E7=AB=AF=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/system/menu/index.ts | 1 + src/locales/en.ts | 4 ++++ src/locales/zh-CN.ts | 4 ++++ src/views/system/menu/MenuForm.vue | 18 ++++++++++++++---- src/views/system/menu/index.vue | 23 +++++++++++++++++++---- 5 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/api/system/menu/index.ts b/src/api/system/menu/index.ts index 5148dd03..804082d7 100644 --- a/src/api/system/menu/index.ts +++ b/src/api/system/menu/index.ts @@ -17,6 +17,7 @@ export interface MenuVO { keepAlive: boolean alwaysShow?: boolean clientType?: number + terminalType?: number createTime: Date } diff --git a/src/locales/en.ts b/src/locales/en.ts index 725e7847..fd6e19ff 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -1146,6 +1146,9 @@ export default { updateCacheTitle: 'Refresh Menu Cache', clientTypeWeb: 'Web Menu', clientTypeApp: 'APP Menu', + terminalType: 'Terminal Type', + terminalTypeMobile: 'Phone', + terminalTypeScanner: 'Scanner', visibleShow: 'Show', visibleHide: 'Hide', always: 'Always', @@ -1156,6 +1159,7 @@ export default { nameRequired: 'Menu name is required', sortRequired: 'Menu order is required', pathRequired: 'Route path is required', + terminalTypeRequired: 'Terminal type is required', statusRequired: 'Status is required', pathMustStartWithSlash: 'Path must start with /', pathMustNotStartWithSlash: 'Path must not start with /', diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts index 276de481..b08e55d5 100644 --- a/src/locales/zh-CN.ts +++ b/src/locales/zh-CN.ts @@ -1147,6 +1147,9 @@ export default { updateCacheTitle: '刷新菜单缓存', clientTypeWeb: 'Web端菜单', clientTypeApp: 'APP端菜单', + terminalType: '终端类型', + terminalTypeMobile: '手机', + terminalTypeScanner: '扫码器', visibleShow: '显示', visibleHide: '隐藏', always: '总是', @@ -1157,6 +1160,7 @@ export default { nameRequired: '菜单名称不能为空', sortRequired: '菜单顺序不能为空', pathRequired: '路由地址不能为空', + terminalTypeRequired: '终端类型不能为空', statusRequired: '状态不能为空', pathMustStartWithSlash: '路径必须以 / 开头', pathMustNotStartWithSlash: '路径不能以 / 开头', diff --git a/src/views/system/menu/MenuForm.vue b/src/views/system/menu/MenuForm.vue index dd281540..c747e594 100644 --- a/src/views/system/menu/MenuForm.vue +++ b/src/views/system/menu/MenuForm.vue @@ -66,6 +66,12 @@ + + + {{ t('SystemManagement.Menu.terminalTypeMobile') }} + {{ t('SystemManagement.Menu.terminalTypeScanner') }} + + ({ name: [{ required: true, message: t('SystemManagement.Menu.nameRequired'), trigger: 'blur' }], sort: [{ required: true, message: t('SystemManagement.Menu.sortRequired'), trigger: 'blur' }], path: [{ required: clientType.value === 1, message: t('SystemManagement.Menu.pathRequired'), trigger: 'blur' }], - status: [{ required: true, message: t('SystemManagement.Menu.statusRequired'), trigger: 'blur' }] + status: [{ required: true, message: t('SystemManagement.Menu.statusRequired'), trigger: 'blur' }], + terminalType: [{ required: clientType.value === 2, message: t('SystemManagement.Menu.terminalTypeRequired'), trigger: 'change' }] })) const formRef = ref() // 表单 Ref @@ -210,8 +218,9 @@ const submitForm = async () => { } } } - const data = formData.value as unknown as MenuApi.MenuVO + const data = formData.value as unknown as MenuApi.MenuVO & { terminalType?: number } data.clientType = clientType.value + data.terminalType = clientType.value === 2 ? formData.value.terminalType : undefined if (formType.value === 'create') { await MenuApi.createMenu(data) message.success(t('common.createSuccess')) @@ -257,7 +266,8 @@ const resetForm = () => { visible: true, keepAlive: true, alwaysShow: true, - clientType: 1 + clientType: 1, + terminalType: 1 } formRef.value?.resetFields() } diff --git a/src/views/system/menu/index.vue b/src/views/system/menu/index.vue index 40ac32d7..b157633d 100644 --- a/src/views/system/menu/index.vue +++ b/src/views/system/menu/index.vue @@ -79,19 +79,29 @@ :default-expand-all="isExpandAll" row-key="id" > - - + + - + + + + - +