# Conflicts:
#	src/locales/en.ts
ck
黄伟杰 2 weeks ago
commit 3d42371613

4
.gitignore vendored

@ -9,3 +9,7 @@ auto-*.d.ts
.history .history
pnpm-lock.yaml pnpm-lock.yaml
/.vscode /.vscode
.env.cklocal
.vscode/*
.vscode/settings.json
launch.json

@ -1,144 +0,0 @@
{
"typescript.tsdk": "node_modules/typescript/lib",
"npm.packageManager": "pnpm",
"editor.tabSize": 2,
"prettier.printWidth": 100, //
"editor.defaultFormatter": "esbenp.prettier-vscode",
"files.eol": "\n",
"search.exclude": {
"**/node_modules": true,
"**/*.log": true,
"**/*.log*": true,
"**/bower_components": true,
"**/dist": true,
"**/elehukouben": true,
"**/.git": true,
"**/.gitignore": true,
"**/.svn": true,
"**/.DS_Store": true,
"**/.idea": true,
"**/.vscode": false,
"**/yarn.lock": true,
"**/tmp": true,
"out": true,
"dist": true,
"node_modules": true,
"CHANGELOG.md": true,
"examples": true,
"res": true,
"screenshots": true,
"yarn-error.log": true,
"**/.yarn": true
},
"files.exclude": {
"**/.cache": true,
"**/.editorconfig": true,
"**/.eslintcache": true,
"**/bower_components": true,
"**/.idea": true,
"**/tmp": true,
"**/.git": true,
"**/.svn": true,
"**/.hg": true,
"**/CVS": true,
"**/.DS_Store": true
},
"files.watcherExclude": {
"**/.git/objects/**": true,
"**/.git/subtree-cache/**": true,
"**/.vscode/**": true,
"**/node_modules/**": true,
"**/tmp/**": true,
"**/bower_components/**": true,
"**/dist/**": true,
"**/yarn.lock": true
},
"stylelint.enable": true,
"stylelint.validate": ["css", "less", "postcss", "scss", "vue", "sass"],
"path-intellisense.mappings": {
"@/": "${workspaceRoot}/src"
},
"[javascriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
},
"[typescriptreact]": {
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
},
"[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[css]": {
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
},
"[less]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[scss]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[markdown]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"[vue]": {
"editor.defaultFormatter": "Vue.volar"
},
"i18n-ally.localesPaths": ["src/locales"],
"i18n-ally.keystyle": "nested",
"i18n-ally.sortKeys": true,
"i18n-ally.namespace": false,
"i18n-ally.enabledParsers": ["ts"],
"i18n-ally.sourceLanguage": "en",
"i18n-ally.displayLanguage": "zh-CN",
"i18n-ally.enabledFrameworks": ["vue", "react"],
"cSpell.words": [
"brotli",
"browserslist",
"codemirror",
"commitlint",
"cropperjs",
"echart",
"echarts",
"esnext",
"esno",
"iconify",
"INTLIFY",
"lintstagedrc",
"logicflow",
"nprogress",
"pinia",
"pnpm",
"qrcode",
"sider",
"sortablejs",
"stylelint",
"svgs",
"unocss",
"unplugin",
"unref",
"videojs",
"VITE",
"vitejs",
"vueuse",
"wangeditor",
"xingyu",
"yudao",
"zxcvbn"
],
//
"explorer.fileNesting.enabled": true,
"explorer.fileNesting.expand": false,
"explorer.fileNesting.patterns": {
"*.ts": "$(capture).test.ts, $(capture).test.tsx",
"*.tsx": "$(capture).test.ts, $(capture).test.tsx",
"*.env": "$(capture).env.*",
"package.json": "pnpm-lock.yaml,yarn.lock,LICENSE,README*,CHANGELOG*,CNAME,.gitattributes,.eslintrc-auto-import.json,.gitignore,prettier.config.js,stylelint.config.js,commitlint.config.js,.stylelintignore,.prettierignore,.gitpod.yml,.eslintrc.js,.eslintignore"
},
"terminal.integrated.scrollback": 10000,
"nuxt.isNuxtApp": false
}

@ -27,6 +27,7 @@ export interface DeviceVO {
certificate?: string // 证书 certificate?: string // 证书
secretKey?: string // 秘钥 secretKey?: string // 秘钥
collectionTime?: string | number collectionTime?: string | number
images: string,//图片
} }
export interface DeviceConnectParams { export interface DeviceConnectParams {
@ -102,6 +103,9 @@ export const DeviceApi = {
getAvailableList: async () => { getAvailableList: async () => {
return await request.get({ url: `/iot/device/available-list` }) return await request.get({ url: `/iot/device/available-list` })
}, },
getAvailableListPage: async (params: any) => {
return await request.get({ url: `/iot/device/available-page` , params})
},
// 新增物联设备 // 新增物联设备
createDevice: async (data: DeviceVO) => { createDevice: async (data: DeviceVO) => {
return await request.post({ url: `/iot/device/create`, data }) return await request.post({ url: `/iot/device/create`, data })
@ -147,6 +151,10 @@ export const DeviceApi = {
return await request.get({ url: `/iot/device/singleDevice`, params }) return await request.get({ url: `/iot/device/singleDevice`, params })
}, },
getSingleDeviceFrom: async (params: SingleDeviceParams) => {
return await request.get({ url: `/iot/device/singleDeviceFrom`, params })
},
getHistoryRecord: async (params: HistoryRecordParams) => { getHistoryRecord: async (params: HistoryRecordParams) => {
return await request.get({ return await request.get({
url: `/iot/device/historyRecord`, url: `/iot/device/historyRecord`,
@ -155,6 +163,15 @@ export const DeviceApi = {
}) })
}, },
getHistoryAnalyse: async (params: HistoryRecordParams) => {
return await request.get({
url: `/iot/device/historyAnalyse`,
params,
paramsSerializer: (p) => qs.stringify(p, { allowDots: true, arrayFormat: 'repeat' })
})
},
devicePointList: async () => { devicePointList: async () => {
return await request.get({ url: `/iot/device/devicePointList` }) return await request.get({ url: `/iot/device/devicePointList` })
}, },
@ -170,6 +187,10 @@ export const DeviceApi = {
getDeviceAttributePage: async (params) => { getDeviceAttributePage: async (params) => {
return await request.get({ url: `/iot/device/device-attribute/page`, params }) return await request.get({ url: `/iot/device/device-attribute/page`, params })
}, },
// 获得设备属性按类型分租
getDeviceAttributeGroupList: async (params) => {
return await request.get({ url: `/iot/device/device-attribute/groupList`, params })
},
// 获得设备属性列表 // 获得设备属性列表
getDeviceAttributeList: async (deviceId: number | string) => { getDeviceAttributeList: async (deviceId: number | string) => {
return await request.get({ url: `/iot/device/device-attribute/list?deviceId=` + deviceId }) return await request.get({ url: `/iot/device/device-attribute/list?deviceId=` + deviceId })

@ -64,13 +64,16 @@ const props = withDefaults(
pageSize?: number pageSize?: number
initialRows?: any[] initialRows?: any[]
queryParams?: Record<string, any> // queryParams?: Record<string, any> //
/** ✅ 新增 */
defaultSelectedKeys?: (string | number)[]
}>(), }>(),
{ {
selectionType: 'multiple', selectionType: 'multiple',
rowKey: 'id', rowKey: 'id',
pageSize: 10, pageSize: 10,
initialRows: () => [], initialRows: () => [],
queryParams: () => ({}) // queryParams: () => ({}), //
defaultSelectedKeys: () => []
} }
) )
@ -111,6 +114,16 @@ const refreshSelectionOnTable = async () => {
syncingSelection.value = false syncingSelection.value = false
} }
} }
/**
* 新增根据 defaultSelectedKeys 初始化选中状态
*/
const initDefaultSelection = () => {
if (!props.defaultSelectedKeys.length) return
props.defaultSelectedKeys.forEach((key) => {
selectedMap.value.set(key, { [props.rowKey]: key })
})
}
const getList = async () => { const getList = async () => {
loading.value = true loading.value = true
@ -161,12 +174,30 @@ const handleRowClick = (row: any) => {
} }
const open = async (rows?: any[]) => { const open = async (rows?: any[]) => {
/* selectedMap.value.clear()
const initialRows = rows || props.initialRows
const nextRows = isSingleSelect.value ? initialRows.slice(0, 1) : initialRows
nextRows.forEach((row) => {
selectedMap.value.set(resolveRowKey(row), row)
})
queryParams.pageNo = 1
queryParams.pageSize = props.pageSize
dialogVisible.value = true
await getList()*/
selectedMap.value.clear() selectedMap.value.clear()
// 使 defaultSelectedKeys
if (props.defaultSelectedKeys.length) {
initDefaultSelection()
}
// initialRows / rows
const initialRows = rows || props.initialRows const initialRows = rows || props.initialRows
const nextRows = isSingleSelect.value ? initialRows.slice(0, 1) : initialRows const nextRows = isSingleSelect.value ? initialRows.slice(0, 1) : initialRows
nextRows.forEach((row) => { nextRows.forEach((row) => {
selectedMap.value.set(resolveRowKey(row), row) selectedMap.value.set(resolveRowKey(row), row)
}) })
queryParams.pageNo = 1 queryParams.pageNo = 1
queryParams.pageSize = props.pageSize queryParams.pageSize = props.pageSize
dialogVisible.value = true dialogVisible.value = true

@ -202,6 +202,35 @@ $prefix-cls: #{$namespace}-menu;
.#{$elNamespace}-menu-item { .#{$elNamespace}-menu-item {
padding-right: 0; padding-right: 0;
} }
&:not(.#{$elNamespace}-menu--collapse) {
.#{$elNamespace}-sub-menu__title,
.#{$elNamespace}-menu-item {
display: flex;
align-items: flex-start;
min-height: var(--el-menu-item-height);
height: auto;
padding-top: 10px;
padding-bottom: 10px;
white-space: normal;
}
.#{$elNamespace}-sub-menu__icon-arrow {
margin-top: 8px;
}
.#{$prefix-cls}__title {
display: block;
flex: 1;
min-width: 0;
overflow: visible;
text-overflow: initial;
white-space: normal;
overflow-wrap: anywhere;
word-break: break-word;
line-height: 1.4;
}
}
} }
} }
@ -228,6 +257,10 @@ $prefix-cls: #{$namespace}-menu;
} }
.#{$prefix-cls}__title { .#{$prefix-cls}__title {
display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
/* stylelint-disable-next-line */ /* stylelint-disable-next-line */
max-height: calc(var(--top-tool-height) - 2px) !important; max-height: calc(var(--top-tool-height) - 2px) !important;
/* stylelint-disable-next-line */ /* stylelint-disable-next-line */

@ -10,14 +10,10 @@ export const useRenderMenuTitle = () => {
return icon ? ( return icon ? (
<> <>
<Icon icon={meta.icon}></Icon> <Icon icon={meta.icon}></Icon>
<span class="v-menu__title overflow-hidden overflow-ellipsis whitespace-nowrap"> <span class="v-menu__title">{t(title as string)}</span>
{t(title as string)}
</span>
</> </>
) : ( ) : (
<span class="v-menu__title overflow-hidden overflow-ellipsis whitespace-nowrap"> <span class="v-menu__title">{t(title as string)}</span>
{t(title as string)}
</span>
) )
} }

@ -62,6 +62,7 @@ export default {
yes: 'Yes', yes: 'Yes',
no: 'No', no: 'No',
code:'Auto-generate on Save' code:'Auto-generate on Save'
code: 'Auto-generate on Save'
}, },
ReportDashboard: { ReportDashboard: {
DashboardList: { DashboardList: {
@ -598,42 +599,44 @@ export default {
large: 'Large', large: 'Large',
small: 'Small' small: 'Small'
}, },
home:{ home: {
welcome: 'Welcome to use the Besure Digital Intelligent Central Control Platform', welcome: 'Welcome to use the Besure Digital Intelligent Central Control Platform',
message: 'The Besure 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.', message:
productTitle:'Overall production overview', 'The Besure 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', placeholderCreateTimeStart: 'Start Date',
placeholderCreateTimeEnd: 'End Date', placeholderCreateTimeEnd: 'End Date',
productionSchedule:'Real-time production progress', productionSchedule: 'Real-time production progress',
productionPlan:'Production plan', productionPlan: 'Production plan',
productionName:'Production name', productionName: 'Production name',
planNumber:'Plan quantity', planNumber: 'Plan quantity',
productionLine:'Production line', productionLine: 'Production line',
planStartTime:'Plan start time', planStartTime: 'Plan start time',
planEndTime:'Plan end time', planEndTime: 'Plan end time',
completedQuantity:'Completed quantity', completedQuantity: 'Completed quantity',
qualifiedQuantity:'Qualified quantity', qualifiedQuantity: 'Qualified quantity',
unqualifiedQuantity:'Unqualified quantity', unqualifiedQuantity: 'Unqualified quantity',
yieldRate: 'YIELD', yieldRate: 'YIELD',
actualEndTime:'Actual end time', actualEndTime: 'Actual end time',
gtasks: 'Gtask', gtasks: 'Gtask',
missionNumber:'Mission number', missionNumber: 'Mission number',
taskType:'Task type', taskType: 'Task type',
target:'Target', target: 'Target',
creationTime:'Creation time', creationTime: 'Creation time',
equipment:'Equipment', equipment: 'Equipment',
mould:'Mould', mould: 'Mould',
equipmentTile:'Overall condition of the collection equipment', equipmentTile: 'Overall condition of the collection equipment',
equipmentCount:'Total equipment count', equipmentCount: 'Total equipment count',
operation:'Operation', operation: 'Operation',
standbyMode:'Standby mode', standbyMode: 'Standby mode',
malfunction:'During the malfunction', malfunction: 'During the malfunction',
alerting:'Alerting...', alerting: 'Alerting...',
useRatio:'use ratio', useRatio: 'use ratio',
failureRate:'failure rate', failureRate: 'failure rate',
equipmentTitle:'Statistics of equipment maintenance quantities', equipmentTitle: 'Statistics of equipment maintenance quantities',
equipmentClass:'Equipment classification statistics', equipmentClass: 'Equipment classification statistics',
equipmentMessage:'Classify and count by mold type, and select the top 10 items with the largest number of molds.', equipmentMessage:
'Classify and count by mold type, and select the top 10 items with the largest number of molds.'
}, },
login: { login: {
welcome: 'Welcome to the system', welcome: 'Welcome to the system',
@ -1053,7 +1056,8 @@ export default {
emailInvalid: 'Please input a valid email address', emailInvalid: 'Please input a valid email address',
mobileInvalid: 'Please input a valid mobile number', mobileInvalid: 'Please input a valid mobile number',
importFileRequired: 'Please upload file', importFileRequired: 'Please upload file',
importSuccessTip: 'Upload success: {createCount}; Update success: {updateCount}; Update failed: {failureCount};', importSuccessTip:
'Upload success: {createCount}; Update success: {updateCount}; Update failed: {failureCount};',
importFailed: 'Upload failed, please upload again!', importFailed: 'Upload failed, please upload again!',
importFileLimit: 'Only one file can be uploaded!', importFileLimit: 'Only one file can be uploaded!',
resetPasswordTitle: 'Please input new password for "{username}"', resetPasswordTitle: 'Please input new password for "{username}"',
@ -1247,7 +1251,7 @@ export default {
}, },
// Equipment Ledger // Equipment Ledger
EquipmentLedger: { EquipmentLedger: {
images:'Images', images: 'Images',
deviceCode: 'Code', deviceCode: 'Code',
qrcode: 'QR Code/Barcode', qrcode: 'QR Code/Barcode',
deviceName: 'Name', deviceName: 'Name',
@ -1331,7 +1335,7 @@ export default {
qrcodeEmpty: 'No QR code', qrcodeEmpty: 'No QR code',
validatorDeviceCodeRequired: 'Code can not be empty', validatorDeviceCodeRequired: 'Code can not be empty',
gjTitle: 'Select key components', gjTitle: 'Select key components',
bjTitle: 'Select spare parts', bjTitle: 'Select spare parts'
}, },
// Critical Component // Critical Component
EquipmentKeyItems: { EquipmentKeyItems: {
@ -1353,7 +1357,7 @@ export default {
qrcodeEmpty: 'No QR code', qrcodeEmpty: 'No QR code',
validatorCodeRequired: 'Code can not be empty', validatorCodeRequired: 'Code can not be empty',
validatorNameRequired: 'Name can not be empty', validatorNameRequired: 'Name can not be empty',
images:'images' images: 'images'
}, },
// Maintenance Project (Project Maintenance) // Maintenance Project (Project Maintenance)
DvSubject: { DvSubject: {
@ -1381,7 +1385,7 @@ export default {
placeholderInspectionMethod: 'Please select inspection method', placeholderInspectionMethod: 'Please select inspection method',
placeholderValueType: 'Please select value type', placeholderValueType: 'Please select value type',
placeholderUpperVal: 'Please input upper value', placeholderUpperVal: 'Please input upper value',
placeholderLowerVal: 'Please input lower value', placeholderLowerVal: 'Please input lower value'
}, },
// Plan Maintenance // Plan Maintenance
PlanMaintenance: { PlanMaintenance: {
@ -1872,8 +1876,8 @@ export default {
addButtonText: 'Add', addButtonText: 'Add',
exportButtonText: 'Export', exportButtonText: 'Export',
expandButtonText: 'Expand/Collapse', expandButtonText: 'Expand/Collapse',
tableCodeColumn: 'Category Code', tableCodeColumn: 'Code',
tableNameColumn: 'Category Name', tableNameColumn: 'Name',
tableSortColumn: 'Sort', tableSortColumn: 'Sort',
tableStatusColumn: 'Status', tableStatusColumn: 'Status',
tableCreateTimeColumn: 'Create Time', tableCreateTimeColumn: 'Create Time',
@ -1902,6 +1906,8 @@ export default {
moduleName: 'Product Material Information', moduleName: 'Product Material Information',
searchNameLabel: 'Name', searchNameLabel: 'Name',
searchNamePlaceholder: 'Please enter name', searchNamePlaceholder: 'Please enter name',
searchCodeLabel: 'Code',
searchCodePlaceholder: 'Please enter code',
searchButtonText: 'Search', searchButtonText: 'Search',
resetButtonText: 'Reset', resetButtonText: 'Reset',
addButtonText: 'Add', addButtonText: 'Add',
@ -2196,25 +2202,26 @@ export default {
validatorNameRequired: 'Unit name can not be empty', validatorNameRequired: 'Unit name can not be empty',
validatorStatusRequired: 'Unit status can not be empty', validatorStatusRequired: 'Unit status can not be empty',
validatorPrimaryFlagRequired: 'Primary unit flag can not be empty', validatorPrimaryFlagRequired: 'Primary unit flag can not be empty',
import:'Import unit', import: 'Import unit',
importDragText: 'Drag file here, or', importDragText: 'Drag file here, or',
importClickText: 'click to upload', importClickText: 'click to upload',
importUpdateSupport: 'Update existing user data if present', importUpdateSupport: 'Update existing user data if present',
importFormatLimit: 'Only xls, xlsx format files are allowed.', importFormatLimit: 'Only xls, xlsx format files are allowed.',
importDownloadTemplate: 'Download template', importDownloadTemplate: 'Download template',
importFileRequired: 'Please upload file', importFileRequired: 'Please upload file',
importSuccessTip: 'Upload success: {createCount}; Update success: {updateCount}; Update failed: {failureCount};', importSuccessTip:
'Upload success: {createCount}; Update success: {updateCount}; Update failed: {failureCount};',
importFailed: 'Upload failed, please upload again!', importFailed: 'Upload failed, please upload again!',
importFileLimit: 'Only one file can be uploaded!', importFileLimit: 'Only one file can be uploaded!'
}, },
CalHoliday: { CalHoliday: {
setWorkingDays: 'set working days', setWorkingDays: 'set working days',
setHoliday: 'set holiday', setHoliday: 'set holiday',
lastMonth:'last month', lastMonth: 'last month',
today:'today', today: 'today',
nextMonth:'next month', nextMonth: 'next month',
work:'work', work: 'work',
rest:'rest', rest: 'rest',
searchDateStartPlaceholder: '开始日期', searchDateStartPlaceholder: '开始日期',
searchDateEndPlaceholder: '结束日期', searchDateEndPlaceholder: '结束日期',
searchTypeLabel: '类型', searchTypeLabel: '类型',
@ -2268,7 +2275,6 @@ export default {
validatorClassificationRequired: 'File classification can not be empty', validatorClassificationRequired: 'File classification can not be empty',
validatorTypeRequired: 'File type can not be empty', validatorTypeRequired: 'File type can not be empty',
validatorStatusRequired: 'File status can not be empty' validatorStatusRequired: 'File status can not be empty'
} }
}, },
@ -2814,8 +2820,8 @@ export default {
validatorTypeRequired: 'Type can not be empty', validatorTypeRequired: 'Type can not be empty',
validatorSampleMethodRequired: 'Sampling method can not be empty', validatorSampleMethodRequired: 'Sampling method can not be empty',
validatorValRequired: 'Value can not be empty', validatorValRequired: 'Value can not be empty',
atIntervals:'at intervals', atIntervals: 'at intervals',
extraction:'extraction', extraction: 'extraction'
}, },
ZjTask: { ZjTask: {
moduleName: 'Inspection Task', moduleName: 'Inspection Task',
@ -3077,7 +3083,8 @@ export default {
readDialogSubmitButtonText: 'Read', readDialogSubmitButtonText: 'Read',
readDialogCancelButton: 'Cancel', readDialogCancelButton: 'Cancel',
readDeviceConfirmMessage: 'Do you want to read device data?', readDeviceConfirmMessage: 'Do you want to read device data?',
readDialogOverwriteTip: 'Reading again will overwrite existing device data and manual parameters', readDialogOverwriteTip:
'Reading again will overwrite existing device data and manual parameters',
detailTabDeviceDataLabel: 'Device Data', detailTabDeviceDataLabel: 'Device Data',
detailTabManualLabel: 'Manual Parameters', detailTabManualLabel: 'Manual Parameters',
detailDevicePointNameColumn: 'Point Name', detailDevicePointNameColumn: 'Point Name',
@ -3090,20 +3097,20 @@ export default {
manualTableReferColumn: 'Reference Value', manualTableReferColumn: 'Reference Value',
manualTableRemarkColumn: 'Remark', manualTableRemarkColumn: 'Remark',
manualTableInputTimeColumn: 'Input Time', manualTableInputTimeColumn: 'Input Time',
manualTableEnteringAction:'Re-enroll', manualTableEnteringAction: 'Re-enroll',
manualFormTitleLabel: 'Re-recorded collected values', manualFormTitleLabel: 'Re-recorded collected values',
nanualFormNameLabel:'Name', nanualFormNameLabel: 'Name',
nanualFormNamePlaceholder:'Please enter name', nanualFormNamePlaceholder: 'Please enter name',
nanualFormDataTypeLabel:'Data Type', nanualFormDataTypeLabel: 'Data Type',
nanualFormDataTypePlaceholder:'Please select data type', nanualFormDataTypePlaceholder: 'Please select data type',
nanualFormDataUnitLabel:'Unit', nanualFormDataUnitLabel: 'Unit',
nanualFormDataUnitPlaceholder:'Please select unit', nanualFormDataUnitPlaceholder: 'Please select unit',
nanualFormReferLabel:'Reference Value', nanualFormReferLabel: 'Reference Value',
nanualFormReferPlaceholder:'Please enter reference value', nanualFormReferPlaceholder: 'Please enter reference value',
nanualFormRemarkLabel:'Remark', nanualFormRemarkLabel: 'Remark',
nanualFormRemarkPlaceholder:'Please enter remark', nanualFormRemarkPlaceholder: 'Please enter remark',
nanualFormInputTimeLabel:'Input Time', nanualFormInputTimeLabel: 'Input Time',
nanualFormInputTimePlaceholder:'Please select input time', nanualFormInputTimePlaceholder: 'Please select input time'
} }
}, },
EnergyManagement: { EnergyManagement: {
@ -3150,7 +3157,7 @@ export default {
validatorUnitRequired: 'Unit can not be empty', validatorUnitRequired: 'Unit can not be empty',
validatorIsEnableRequired: 'Enabled can not be empty', validatorIsEnableRequired: 'Enabled can not be empty',
exportFilename: 'EnergyType.xls', exportFilename: 'EnergyType.xls'
}, },
EnergyDeviceCheck: { EnergyDeviceCheck: {
moduleName: 'Energy Report', moduleName: 'Energy Report',
@ -3566,7 +3573,8 @@ export default {
capacityTypeDataCollection: 'Data Collection Capacity', capacityTypeDataCollection: 'Data Collection Capacity',
inventoryTaskType: 'Inventory Task', inventoryTaskType: 'Inventory Task',
taskNoDetailWarning: 'Task order {code} has no detail data, cannot be selected', taskNoDetailWarning: 'Task order {code} has no detail data, cannot be selected',
partialTaskNoDetailWarning: 'Some task orders have no detail data, selection has been automatically cancelled', partialTaskNoDetailWarning:
'Some task orders have no detail data, selection has been automatically cancelled',
taskItemNeedPrefix: 'Task Order-', taskItemNeedPrefix: 'Task Order-',
detailNoProductWarning: 'Current detail has no related product, cannot set device', detailNoProductWarning: 'Current detail has no related product, cannot set device',
deviceRelationSaved: 'Device relation saved', deviceRelationSaved: 'Device relation saved',
@ -3753,7 +3761,7 @@ export default {
FeedingRecord: { FeedingRecord: {
moduleName: 'Production Feeding', moduleName: 'Production Feeding',
planCode:'production plan', planCode: 'production plan',
searchCodeLabel: 'Record No.', searchCodeLabel: 'Record No.',
searchCodePlaceholder: 'Please enter record No.', searchCodePlaceholder: 'Please enter record No.',
searchPipelineLabel: 'Pulp Line', searchPipelineLabel: 'Pulp Line',
@ -4188,8 +4196,8 @@ export default {
messageSelectDeviceRequired: 'Please select a device', messageSelectDeviceRequired: 'Please select a device',
messageDeviceInfoMissingForRules: 'Device information is missing, unable to load point rules', messageDeviceInfoMissingForRules: 'Device information is missing, unable to load point rules',
gridView:'gridView', gridView: 'gridView',
sudoku:'sudoku', sudoku: 'sudoku'
}, },
RunReport: { RunReport: {
moduleName: 'Device Operation Report', moduleName: 'Device Operation Report',
@ -4281,9 +4289,11 @@ export default {
tableCollectionTimeColumn: 'Collection Time', tableCollectionTimeColumn: 'Collection Time',
tableOperateColumn: 'Operation', tableOperateColumn: 'Operation',
tableActionHistoryLabel: 'History', tableActionHistoryLabel: 'History',
tableActionHistoryAnalyseLabel: 'History Data Analysis',
dialogTitlePrefix: 'History: ', dialogTitlePrefix: 'History: ',
dialogCollectionTimeLabel: 'Collection Time', dialogCollectionTimeLabel: 'Collection Time',
dialogPointFilterLabel: 'Point Filter',
dialogPointFilterPlaceholder: 'Please enter point name',
dialogCollectionTimeStartPlaceholder: 'Start Time', dialogCollectionTimeStartPlaceholder: 'Start Time',
dialogCollectionTimeEndPlaceholder: 'End Time', dialogCollectionTimeEndPlaceholder: 'End Time',
dialogSearchButtonText: 'Search', dialogSearchButtonText: 'Search',

@ -601,40 +601,41 @@ export default {
}, },
home: { home: {
welcome: '欢迎您使用必硕数字化智能中控平台', welcome: '欢迎您使用必硕数字化智能中控平台',
message: '必硕数字化智能中控平台以生产运营为核心,统一整合生产计划、数据采集、仓储、设备、能源、模具、质量、配方及报表分析等关键业务模块,实现从计划到执行、从设备到制品、从数据到决策的全流程数字化管理。', message:
'必硕数字化智能中控平台以生产运营为核心,统一整合生产计划、数据采集、仓储、设备、能源、模具、质量、配方及报表分析等关键业务模块,实现从计划到执行、从设备到制品、从数据到决策的全流程数字化管理。',
productTitle: '整体生产概况', productTitle: '整体生产概况',
placeholderCreateTimeStart: '开始日期', placeholderCreateTimeStart: '开始日期',
placeholderCreateTimeEnd: '结束日期', placeholderCreateTimeEnd: '结束日期',
productionSchedule:'实时生产进度', productionSchedule: '实时生产进度',
productionPlan:'生产计划', productionPlan: '生产计划',
productionName:'生产名称', productionName: '生产名称',
planNumber:'计划数量', planNumber: '计划数量',
productionLine:'生产线', productionLine: '生产线',
planStartTime:'计划开始时间', planStartTime: '计划开始时间',
planEndTime:'计划结束时间', planEndTime: '计划结束时间',
completedQuantity:'完工数量', completedQuantity: '完工数量',
qualifiedQuantity:'合格数量', qualifiedQuantity: '合格数量',
unqualifiedQuantity:'不合格数量', unqualifiedQuantity: '不合格数量',
yieldRate:'合格率', yieldRate: '合格率',
actualEndTime:'实际结束时间', actualEndTime: '实际结束时间',
gtasks:'待办任务', gtasks: '待办任务',
missionNumber:'任务编号', missionNumber: '任务编号',
taskType:'任务类型', taskType: '任务类型',
target:'任务目标', target: '任务目标',
creationTime:'创建时间', creationTime: '创建时间',
equipment:'设备', equipment: '设备',
mould:'模具', mould: '模具',
equipmentTile:'采集设备整体情况', equipmentTile: '采集设备整体情况',
equipmentCount:'设备总数', equipmentCount: '设备总数',
operation:'运行', operation: '运行',
standbyMode:'待机', standbyMode: '待机',
malfunction:'故障中', malfunction: '故障中',
alerting:'报警中', alerting: '报警中',
useRatio:'利用率', useRatio: '利用率',
failureRate:'故障率', failureRate: '故障率',
equipmentTitle:'设备维修数量统计', equipmentTitle: '设备维修数量统计',
equipmentClass:'设备分类统计', equipmentClass: '设备分类统计',
equipmentMessage:'按模具分类统计取模具数量最多的前10项' equipmentMessage: '按模具分类统计取模具数量最多的前10项'
}, },
login: { login: {
welcome: '欢迎使用必硕数字化智能中控平台', welcome: '欢迎使用必硕数字化智能中控平台',
@ -1239,7 +1240,7 @@ export default {
}, },
// 设备台账 // 设备台账
EquipmentLedger: { EquipmentLedger: {
images:'图片', images: '图片',
deviceCode: '编码', deviceCode: '编码',
qrcode: '二维码/条形码', qrcode: '二维码/条形码',
deviceName: '名称', deviceName: '名称',
@ -1248,7 +1249,7 @@ export default {
statusDisabled: '不启用', statusDisabled: '不启用',
deviceType: '类型', deviceType: '类型',
deviceSpec: '规格型号', deviceSpec: '规格型号',
/* deviceModel: '型号',*/ /* deviceModel: '型号',*/
isSchedueld: '是否排产', isSchedueld: '是否排产',
ratedCapacity: '额定产能', ratedCapacity: '额定产能',
yes: '是', yes: '是',
@ -1323,7 +1324,7 @@ export default {
qrcodeEmpty: '暂无二维码', qrcodeEmpty: '暂无二维码',
validatorDeviceCodeRequired: '编码不能为空', validatorDeviceCodeRequired: '编码不能为空',
gjTitle: '选择关键件', gjTitle: '选择关键件',
bjTitle: '选择备件', bjTitle: '选择备件'
}, },
// 设备关键件 // 设备关键件
EquipmentKeyItems: { EquipmentKeyItems: {
@ -1331,7 +1332,7 @@ export default {
code: '编码', code: '编码',
qrcode: '二维码/条形码', qrcode: '二维码/条形码',
name: '名称', name: '名称',
deviceSpec:'规格型号', deviceSpec: '规格型号',
batchDelete: '批量删除', batchDelete: '批量删除',
description: '描述', description: '描述',
remark: '备注', remark: '备注',
@ -1349,7 +1350,7 @@ export default {
qrcodeEmpty: '暂无二维码', qrcodeEmpty: '暂无二维码',
validatorCodeRequired: '编码不能为空', validatorCodeRequired: '编码不能为空',
validatorNameRequired: '名称不能为空', validatorNameRequired: '名称不能为空',
images:'图片' images: '图片'
}, },
// 项目维护 // 项目维护
DvSubject: { DvSubject: {
@ -1357,7 +1358,7 @@ export default {
name: '名称', name: '名称',
inspectionMethod: '作业方式', inspectionMethod: '作业方式',
valueType: '结果类型', valueType: '结果类型',
JobType:'作业类型', JobType: '作业类型',
judgmentCriteria: '作业内容', judgmentCriteria: '作业内容',
upperVal: '上限值', upperVal: '上限值',
lowerVal: '下限值', lowerVal: '下限值',
@ -2322,8 +2323,8 @@ export default {
validatorTypeRequired: '类型不能为空', validatorTypeRequired: '类型不能为空',
validatorSampleMethodRequired: '抽检方式不能为空', validatorSampleMethodRequired: '抽检方式不能为空',
validatorValRequired: '值不能为空', validatorValRequired: '值不能为空',
atIntervals:'每间隔', atIntervals: '每间隔',
extraction:'抽取', extraction: '抽取'
}, },
ZjTask: { ZjTask: {
moduleName: '检验任务', moduleName: '检验任务',
@ -2609,8 +2610,7 @@ export default {
nanualFormReferLabel: '参考值', nanualFormReferLabel: '参考值',
nanualFormReferPlaceholder: '请输入参考值', nanualFormReferPlaceholder: '请输入参考值',
nanualFormRemarkLabel: '备注', nanualFormRemarkLabel: '备注',
nanualFormRemarkPlaceholder: '请输入备注', nanualFormRemarkPlaceholder: '请输入备注'
} }
}, },
FactoryModeling: { FactoryModeling: {
@ -2704,8 +2704,8 @@ export default {
addButtonText: '新增', addButtonText: '新增',
exportButtonText: '导出', exportButtonText: '导出',
expandButtonText: '展开/折叠', expandButtonText: '展开/折叠',
tableCodeColumn: '分类编码', tableCodeColumn: '编码',
tableNameColumn: '分类名称', tableNameColumn: '名称',
tableSortColumn: '排序', tableSortColumn: '排序',
tableStatusColumn: '状态', tableStatusColumn: '状态',
tableCreateTimeColumn: '创建时间', tableCreateTimeColumn: '创建时间',
@ -2734,6 +2734,8 @@ export default {
moduleName: '产品物料信息', moduleName: '产品物料信息',
searchNameLabel: '名称', searchNameLabel: '名称',
searchNamePlaceholder: '请输入名称', searchNamePlaceholder: '请输入名称',
searchCodeLabel: '编码',
searchCodePlaceholder: '请输入编码',
searchButtonText: '搜索', searchButtonText: '搜索',
resetButtonText: '重置', resetButtonText: '重置',
addButtonText: '新增', addButtonText: '新增',
@ -3027,8 +3029,8 @@ export default {
validatorNameRequired: '单位名称不能为空', validatorNameRequired: '单位名称不能为空',
validatorStatusRequired: '单位状态不能为空', validatorStatusRequired: '单位状态不能为空',
validatorPrimaryFlagRequired: '是否主单位不能为空', validatorPrimaryFlagRequired: '是否主单位不能为空',
importButtonText:'导入', importButtonText: '导入',
import:'导入单位', import: '导入单位',
importDragText: '将文件拖到此处,或', importDragText: '将文件拖到此处,或',
importClickText: '点击上传', importClickText: '点击上传',
importUpdateSupport: '是否更新已经存在的用户数据', importUpdateSupport: '是否更新已经存在的用户数据',
@ -3038,17 +3040,17 @@ export default {
importSuccessTip: importSuccessTip:
'上传成功数量:{createCount};更新成功数量:{updateCount};更新失败数量:{failureCount}', '上传成功数量:{createCount};更新成功数量:{updateCount};更新失败数量:{failureCount}',
importFailed: '上传失败,请您重新上传!', importFailed: '上传失败,请您重新上传!',
importFileLimit: '最多只能上传一个文件!', importFileLimit: '最多只能上传一个文件!'
}, },
//假日管理 //假日管理
CalHoliday: { CalHoliday: {
setWorkingDays: '设置工作日', setWorkingDays: '设置工作日',
setHoliday: '设置休息日', setHoliday: '设置休息日',
lastMonth:'上个月', lastMonth: '上个月',
today:'今天', today: '今天',
nextMonth:'下个月', nextMonth: '下个月',
work:'班', work: '班',
rest:'休', rest: '休',
searchDateStartPlaceholder: '开始日期', searchDateStartPlaceholder: '开始日期',
searchDateEndPlaceholder: '结束日期', searchDateEndPlaceholder: '结束日期',
searchTypeLabel: '类型', searchTypeLabel: '类型',
@ -3073,7 +3075,7 @@ export default {
uploadButtonText: '文件上传', uploadButtonText: '文件上传',
addButtonText: '新增', addButtonText: '新增',
exportButtonText: '导出', exportButtonText: '导出',
tableCodeColumn:'文件编码', tableCodeColumn: '文件编码',
tableNameColumn: '文件名称', tableNameColumn: '文件名称',
tableClassificationColumn: '文件分类', tableClassificationColumn: '文件分类',
tableTypeColumn: '文件类型', tableTypeColumn: '文件类型',
@ -3082,7 +3084,7 @@ export default {
tableCreateTimeColumn: '上传时间', tableCreateTimeColumn: '上传时间',
tableOperateColumn: '操作', tableOperateColumn: '操作',
tableEditAction: '编辑', tableEditAction: '编辑',
tableDownloadAction:'下载', tableDownloadAction: '下载',
tableDeleteAction: '删除', tableDeleteAction: '删除',
dialogFileNameLabel: '文件名', dialogFileNameLabel: '文件名',
dialogFileNamePlaceholder: '请输入文件名', dialogFileNamePlaceholder: '请输入文件名',
@ -3091,14 +3093,13 @@ export default {
dialogStatusLabel: '文件状态', dialogStatusLabel: '文件状态',
dialogCancelButton: '取 消', dialogCancelButton: '取 消',
dialogSubmitButton: '确 定', dialogSubmitButton: '确 定',
messageOne:'提示:仅允许导入 ', messageOne: '提示:仅允许导入 ',
messageTwo:' 格式文件!', messageTwo: ' 格式文件!',
messageThree:' 将文件拖到此处,或', messageThree: ' 将文件拖到此处,或',
messageFour:'点击上传', messageFour: '点击上传',
validatorNameRequired: '文件名不能为空', validatorNameRequired: '文件名不能为空',
validatorStatusRequired: '文件状态不能为空' validatorStatusRequired: '文件状态不能为空'
} }
}, },
ProductionPlan: { ProductionPlan: {
Task: { Task: {
@ -3788,7 +3789,7 @@ export default {
exportFilename: '成型记录.xls' exportFilename: '成型记录.xls'
}, },
WorkReportRecord: { WorkReportRecord: {
moduleName: '报工记录', moduleName: '报工记录',
searchBaogongTimeLabel: '报工日期', searchBaogongTimeLabel: '报工日期',
@ -4031,8 +4032,8 @@ export default {
messageSelectDeviceRequired: '请选择一个物联设备', messageSelectDeviceRequired: '请选择一个物联设备',
messageDeviceInfoMissingForRules: '设备信息缺失,无法加载点位规则', messageDeviceInfoMissingForRules: '设备信息缺失,无法加载点位规则',
gridView:'表格视图', gridView: '表格视图',
sudoku:'九宫格', sudoku: '九宫格'
}, },
RunReport: { RunReport: {
moduleName: '设备运行报表', moduleName: '设备运行报表',
@ -4124,9 +4125,11 @@ export default {
tableCollectionTimeColumn: '采集时间', tableCollectionTimeColumn: '采集时间',
tableOperateColumn: '操作', tableOperateColumn: '操作',
tableActionHistoryLabel: '历史记录', tableActionHistoryLabel: '历史记录',
tableActionHistoryAnalyseLabel: '数据分析',
dialogTitlePrefix: '历史记录:', dialogTitlePrefix: '历史记录:',
dialogCollectionTimeLabel: '采集时间', dialogCollectionTimeLabel: '采集时间',
dialogPointFilterLabel: '点位筛选',
dialogPointFilterPlaceholder: '请输入点位名称',
dialogCollectionTimeStartPlaceholder: '开始时间', dialogCollectionTimeStartPlaceholder: '开始时间',
dialogCollectionTimeEndPlaceholder: '结束时间', dialogCollectionTimeEndPlaceholder: '结束时间',
dialogSearchButtonText: '查询', dialogSearchButtonText: '查询',
@ -4496,9 +4499,11 @@ export default {
tableCollectionTimeColumn: '采集时间', tableCollectionTimeColumn: '采集时间',
tableOperateColumn: '操作', tableOperateColumn: '操作',
tableActionHistoryLabel: '历史记录', tableActionHistoryLabel: '历史记录',
tableActionHistoryAnalyseLabel: '数据分析',
dialogTitlePrefix: '历史记录:', dialogTitlePrefix: '历史记录:',
dialogCollectionTimeLabel: '采集时间', dialogCollectionTimeLabel: '采集时间',
dialogPointFilterLabel: '点位筛选',
dialogPointFilterPlaceholder: '请输入点位名称',
dialogCollectionTimeStartPlaceholder: '开始时间', dialogCollectionTimeStartPlaceholder: '开始时间',
dialogCollectionTimeEndPlaceholder: '结束时间', dialogCollectionTimeEndPlaceholder: '结束时间',
dialogSearchButtonText: '查询', dialogSearchButtonText: '查询',
@ -4538,8 +4543,6 @@ export default {
messageDeviceNoParams: '该设备下没有参数', messageDeviceNoParams: '该设备下没有参数',
messageFetchChartFailed: '获取图表数据失败' messageFetchChartFailed: '获取图表数据失败'
} }
} }
}, },
ProductionReport: { ProductionReport: {

@ -35,3 +35,19 @@
border-left-color: var(--el-color-primary); border-left-color: var(--el-color-primary);
} }
} }
/* 全局生效,所有表格的悬停色都会改变 */
.el-table--enable-row-hover .el-table__body tr:hover > td {
background-color: #f0f9eb !important;
}
/* 2. 关键补全:针对展开行内的 hover 状态 */
/* 展开行tr.expanded内部的 td 需要单独设置 */
.el-table__body .el-table__expanded-cell:hover > td {
background-color: #f0f9eb !important;
}
/* 3. 针对展开后触发的 hover-row 类(某些版本生效) */
.el-table__body tr.hover-row > td {
background-color: #f0f9eb !important;
}

@ -2,41 +2,29 @@
<template> <template>
<ContentWrap> <ContentWrap>
<!-- 搜索工作栏 --> <!-- 搜索工作栏 -->
<el-form <el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="auto" @submit.prevent>
class="-mb-15px" <el-form-item :label="t('FactoryModeling.ProductInformation.searchCodeLabel')" prop="barCode">
:model="queryParams" <el-input v-model="queryParams.barCode"
ref="queryFormRef" :placeholder="t('FactoryModeling.ProductInformation.searchCodePlaceholder')" clearable
:inline="true" @keyup.enter="handleQuery" class="!w-240px" />
label-width="120px" </el-form-item>
@submit.prevent
>
<el-form-item :label="t('FactoryModeling.ProductInformation.searchNameLabel')" prop="name"> <el-form-item :label="t('FactoryModeling.ProductInformation.searchNameLabel')" prop="name">
<el-input <el-input v-model="queryParams.name"
v-model="queryParams.name" :placeholder="t('FactoryModeling.ProductInformation.searchNamePlaceholder')" clearable
:placeholder="t('FactoryModeling.ProductInformation.searchNamePlaceholder')" @keyup.enter="handleQuery" class="!w-240px" />
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> {{ t('FactoryModeling.ProductInformation.searchButtonText') }}</el-button> <el-button @click="handleQuery">
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> {{ t('FactoryModeling.ProductInformation.resetButtonText') }}</el-button> <Icon icon="ep:search" class="mr-5px" /> {{ t('FactoryModeling.ProductInformation.searchButtonText') }}
<el-button </el-button>
type="primary" <el-button @click="resetQuery">
plain <Icon icon="ep:refresh" class="mr-5px" /> {{ t('FactoryModeling.ProductInformation.resetButtonText') }}
@click="openForm('create')" </el-button>
v-hasPermi="['erp:product:create']" <el-button type="primary" plain @click="openForm('create')" v-hasPermi="['erp:product:create']">
>
<Icon icon="ep:plus" class="mr-5px" /> {{ t('FactoryModeling.ProductInformation.addButtonText') }} <Icon icon="ep:plus" class="mr-5px" /> {{ t('FactoryModeling.ProductInformation.addButtonText') }}
</el-button> </el-button>
<el-button <el-button type="success" plain @click="handleExport" :loading="exportLoading"
type="success" v-hasPermi="['erp:product:export']">
plain
@click="handleExport"
:loading="exportLoading"
v-hasPermi="['erp:product:export']"
>
<Icon icon="ep:download" class="mr-5px" /> {{ t('FactoryModeling.ProductInformation.exportButtonText') }} <Icon icon="ep:download" class="mr-5px" /> {{ t('FactoryModeling.ProductInformation.exportButtonText') }}
</el-button> </el-button>
</el-form-item> </el-form-item>
@ -47,12 +35,7 @@
<ContentWrap> <ContentWrap>
<el-tabs v-model="activeName" @tab-click="handleTabClick"> <el-tabs v-model="activeName" @tab-click="handleTabClick">
<!-- 使用 v-for 动态生成 el-tab-pane --> <!-- 使用 v-for 动态生成 el-tab-pane -->
<el-tab-pane <el-tab-pane v-for="item in parentList" :key="item.id" :label="item.name" :name="item.id.toString()" />
v-for="item in parentList"
:key="item.id"
:label="item.name"
:name="item.id.toString()"
/>
<!-- <el-tab-pane label="产品" name="2" /> <!-- <el-tab-pane label="产品" name="2" />
<el-tab-pane label="原料" name="1" /> <el-tab-pane label="原料" name="1" />
<el-tab-pane label="备件" name="5" /> <el-tab-pane label="备件" name="5" />
@ -62,60 +45,46 @@
<el-tab-pane label="其他" name="0" /> --> <el-tab-pane label="其他" name="0" /> -->
</el-tabs> </el-tabs>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column :label="t('FactoryModeling.ProductInformation.tableBarCodeColumn')" align="center" prop="barCode" sortable/> <el-table-column :label="t('FactoryModeling.ProductInformation.tableBarCodeColumn')" align="center" prop="barCode"
<el-table-column :label="t('FactoryModeling.ProductInformation.tableNameColumn')" align="left" prop="name" width="220px" sortable/> sortable />
<el-table-column :label="t('FactoryModeling.ProductInformation.tableStandardColumn')" align="center" prop="standard" sortable/> <el-table-column :label="t('FactoryModeling.ProductInformation.tableNameColumn')" align="left" prop="name"
<el-table-column :label="t('FactoryModeling.ProductInformation.tableCategoryColumn')" align="center" prop="subCategoryName" sortable/> width="220px" sortable />
<el-table-column :label="t('FactoryModeling.ProductInformation.tableUnitColumn')" align="center" prop="unitName" sortable/> <el-table-column :label="t('FactoryModeling.ProductInformation.tableStandardColumn')" align="center"
<el-table-column :label="t('FactoryModeling.ProductInformation.tableStatusColumn')" align="center" prop="status" sortable> prop="standard" sortable />
<el-table-column :label="t('FactoryModeling.ProductInformation.tableCategoryColumn')" align="center"
prop="subCategoryName" sortable />
<el-table-column :label="t('FactoryModeling.ProductInformation.tableUnitColumn')" align="center" prop="unitName"
sortable />
<el-table-column :label="t('FactoryModeling.ProductInformation.tableStatusColumn')" align="center" prop="status"
sortable>
<template #default="scope"> <template #default="scope">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" /> <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column :label="t('FactoryModeling.ProductInformation.tableCreateTimeColumn')" align="center"
:label="t('FactoryModeling.ProductInformation.tableCreateTimeColumn')" prop="createTime" :formatter="dateFormatter" width="180px" sortable />
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
sortable
/>
<el-table-column :label="t('FactoryModeling.ProductInformation.tableOperateColumn')" align="center" width="150px"> <el-table-column :label="t('FactoryModeling.ProductInformation.tableOperateColumn')" align="center" width="150px">
<template #default="scope"> <template #default="scope">
<!-- <el-button--> <!-- <el-button-->
<!-- v-if="scope.row.categoryId ===2"--> <!-- v-if="scope.row.categoryId ===2"-->
<!-- link--> <!-- link-->
<!-- type="primary"--> <!-- type="primary"-->
<!-- @click="openBomForm('detail', scope.row.id)"--> <!-- @click="openBomForm('detail', scope.row.id)"-->
<!-- >--> <!-- >-->
<!-- BOM--> <!-- BOM-->
<!-- </el-button>--> <!-- </el-button>-->
<el-button <el-button link type="primary" @click="openForm('update', scope.row.id)" v-hasPermi="['erp:product:update']">
link
type="primary"
@click="openForm('update', scope.row.id)"
v-hasPermi="['erp:product:update']"
>
{{ t('FactoryModeling.ProductInformation.tableEditAction') }} {{ t('FactoryModeling.ProductInformation.tableEditAction') }}
</el-button> </el-button>
<el-button <el-button link type="danger" @click="handleDelete(scope.row.id)" v-hasPermi="['erp:product:delete']">
link
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['erp:product:delete']"
>
{{ t('FactoryModeling.ProductInformation.tableDeleteAction') }} {{ t('FactoryModeling.ProductInformation.tableDeleteAction') }}
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<!-- 分页 --> <!-- 分页 -->
<Pagination <Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
:total="total" @pagination="getList" />
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap> </ContentWrap>
<!-- 表单弹窗添加/修改 --> <!-- 表单弹窗添加/修改 -->
@ -148,6 +117,7 @@ const queryParams = reactive({
pageNo: 1, pageNo: 1,
pageSize: 10, pageSize: 10,
name: undefined, name: undefined,
barCode: undefined,
categoryId: undefined categoryId: undefined
}) })
const queryFormRef = ref() // const queryFormRef = ref() //
@ -195,7 +165,7 @@ const handleDelete = async (id: number) => {
message.success(t('common.delSuccess')) message.success(t('common.delSuccess'))
// //
await getList() await getList()
} catch {} } catch { }
} }
/** 导出按钮操作 */ /** 导出按钮操作 */

@ -43,6 +43,9 @@
<el-option v-for="item in modelList" :key="item.id" :label="item.name" :value="item.id" /> <el-option v-for="item in modelList" :key="item.id" :label="item.name" :value="item.id" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item :label="t('EquipmentManagement.EquipmentLedger.images')" prop="images">
<UploadImg style="height: 100px" v-model="formData.images" />
</el-form-item>
</template> </template>
<!-- <el-form-item label="设备类型" prop="deviceType"> <!-- <el-form-item label="设备类型" prop="deviceType">
<el-select v-model="formData.deviceType" placeholder="请选择设备类型"> <el-select v-model="formData.deviceType" placeholder="请选择设备类型">
@ -134,6 +137,7 @@ const formData = ref({
username: undefined, username: undefined,
password: undefined, password: undefined,
topic: undefined, topic: undefined,
images: undefined,
}) })
const formRules = reactive({ const formRules = reactive({
create: { create: {
@ -188,7 +192,7 @@ const submitForm = async () => {
// //
formLoading.value = true formLoading.value = true
try { try {
const { id, deviceCode, deviceName, deviceModelId, sampleCycle, remark, isEnable, topic } = formData.value as any const { id, deviceCode, deviceName, deviceModelId, sampleCycle, remark, isEnable, topic,images } = formData.value as any
if (formType.value === 'create') { if (formType.value === 'create') {
const data: Partial<DeviceVO> = { const data: Partial<DeviceVO> = {
@ -197,16 +201,17 @@ const submitForm = async () => {
deviceModelId, deviceModelId,
sampleCycle, sampleCycle,
remark, remark,
isEnable: false isEnable: false,
images
} }
await DeviceApi.createDevice(data as DeviceVO) await DeviceApi.createDevice(data as DeviceVO)
message.success(t('common.createSuccess')) message.success(t('common.createSuccess'))
} else if (formType.value === 'update') { } else if (formType.value === 'update') {
const data: any = { id, deviceCode, deviceName, deviceModelId, sampleCycle, isEnable } const data: any = { id, deviceCode, deviceName, deviceModelId, sampleCycle, isEnable,images}
await DeviceApi.updateDevice(data) await DeviceApi.updateDevice(data)
message.success(t('common.updateSuccess')) message.success(t('common.updateSuccess'))
} else { } else {
const data: any = { id, deviceCode, deviceName, deviceModelId, isEnable, topic } const data: any = { id, deviceCode, deviceName, deviceModelId, isEnable, topic,images}
await DeviceApi.updateDevice(data) await DeviceApi.updateDevice(data)
message.success(t('common.updateSuccess')) message.success(t('common.updateSuccess'))
} }
@ -246,3 +251,17 @@ const resetForm = () => {
/** 初始化 **/ /** 初始化 **/
</script> </script>
<style scoped>
:deep(.upload-box[data-v-57417a1d] .upload .el-upload .el-upload-dragger ){
display: flex;
align-items: center;
justify-content: center;
width: 70%;
height: 70%;
padding: 0;
overflow: hidden;
background-color: transparent;
border: 1px dashed var(--el-border-color-darker);
border-radius: var(--57417a1d-borderradius);
}
</style>

File diff suppressed because it is too large Load Diff

@ -210,8 +210,17 @@
<div class="device-card"> <div class="device-card">
<div class="header"> <div class="header">
<div class="device-icon"> <div class="device-icon" @click.stop>
<i>📊</i> <!-- <i>📊</i>-->
<el-image
style="width: 60px; height: 60px"
:src="item.images || 'https://p119-minio-upload.ngsk.tech:7001/besure/998a6fc5d7da4ad079cc5afaa1ca5293917c17080c9805dc791039d3534e9dba.jpeg'"
:preview-src-list="[
item.images || 'https://p119-minio-upload.ngsk.tech:7001/besure/998a6fc5d7da4ad079cc5afaa1ca5293917c17080c9805dc791039d3534e9dba.jpeg'
]"
fit="cover"
preview-teleported
/> <!-- <img src="@/assets/images/device.png" alt="Device Icon" />-->
</div> </div>
<div class="device-info"> <div class="device-info">
<div class="device-name">{{ item.deviceName }}</div> <div class="device-name">{{ item.deviceName }}</div>
@ -320,9 +329,9 @@
</ContentWrap> </ContentWrap>
<!-- 表单弹窗添加/修改 --> <!-- 表单弹窗添加/修改 -->
<DeviceForm ref="formRef" @success="getList" /> <DeviceForm ref="formRef" @success="getListOne" />
<!-- 子表的列表 --> <!-- 子表的列表 -->
<ContentWrap v-if="ifShow"> <!-- <ContentWrap v-if="ifShow">
<template v-if="attributeDeviceId"> <template v-if="attributeDeviceId">
<div class="mb-10px flex items-center justify-between text-sm text-gray-500"> <div class="mb-10px flex items-center justify-between text-sm text-gray-500">
<div> <div>
@ -711,7 +720,7 @@
</el-tabs> </el-tabs>
</template> </template>
<el-empty v-else :description="t('DataCollection.Device.emptyDescription')" /> <el-empty v-else :description="t('DataCollection.Device.emptyDescription')" />
</ContentWrap> </ContentWrap>-->
<Dialog :title="t('DataCollection.Device.alarmHistoryTitle')" v-model="deviceAlarmDialogVisible" width="1200px"> <Dialog :title="t('DataCollection.Device.alarmHistoryTitle')" v-model="deviceAlarmDialogVisible" width="1200px">
<el-form <el-form
@ -836,6 +845,9 @@ import DetailForm from "@/views/iot/device/detailForm.vue";
import {ProductUnitApi, ProductUnitVO} from "@/api/erp/product/unit"; import {ProductUnitApi, ProductUnitVO} from "@/api/erp/product/unit";
const ifShow =ref(true) const ifShow =ref(true)
const currentView = ref('table') // 'table' 'grid' const currentView = ref('table') // 'table' 'grid'
// script setup
const noImageUrl =
'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODAiIGhlaWdodD0iODAiIHZpZXdCb3g9IjAgMCA4MCA4MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iODAiIGhlaWdodD0iODAiIGZpbGw9IiNFOUVDRUYiLz48dGV4dCB4PSI0MCIgeT0iNDUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IiM5OTkiIGZvbnQtc2l6ZT0iMTQiPuWbvueJh+WKoOi9veWksei0pTwvdGV4dD48L3N2Zz4='
// //
const router = useRouter() const router = useRouter()
// //
@ -1004,6 +1016,22 @@ const getList = async (showLoading = true) => {
} }
} }
/** 查询列表 */
const getListOne = async (showLoading = true) => {
if (showLoading) {
loading.value = true
}
try {
const data = await DeviceApi.getDevicePage(queryParams)
list.value = data.list
total.value = data.total
} finally {
if (showLoading) {
loading.value = false
}
}
}
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.pageNo = 1 queryParams.pageNo = 1
@ -1141,6 +1169,14 @@ const deviceRuleTabLabel = computed(() => {
const handleShowAttribute = (row: any) => { const handleShowAttribute = (row: any) => {
attributeDeviceId.value = row?.id attributeDeviceId.value = row?.id
attributeDeviceName.value = row?.deviceName ?? '' attributeDeviceName.value = row?.deviceName ?? ''
router.push({
path: '/iot/pointManagement',
query: {
id: row?.id,
name: row?.deviceName
}
});
} }
const ruleLoading = ref(false) const ruleLoading = ref(false)
@ -2106,17 +2142,30 @@ const handleShowDeviceAlarmHistory = async () => {
} }
.device-icon { .device-icon {
width: 40px; /*width: 40px;
height: 40px; height: 40px;
background-color: #f0f7ff; background-color: #f0f7ff;
border-radius: 8px; border-radius: 8px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
margin-right: 12px; margin-right: 12px;*/
display: flex;
align-items: center;
justify-content: center;
width: 60px;
height: 60px;
background-color: #f0f7ff;
border-radius: 8px;
overflow: hidden;
.el-image {
cursor: pointer;
}
} }
.device-info { .device-info {
margin-left: 6px;
flex: 1; flex: 1;
} }

File diff suppressed because it is too large Load Diff

@ -108,12 +108,15 @@
:label="t('DataCollection.HistoryData.tableOperateColumn')" :label="t('DataCollection.HistoryData.tableOperateColumn')"
align="center" align="center"
fixed="right" fixed="right"
width="150px" width="300px"
> >
<template #default="scope"> <template #default="scope">
<el-button link type="primary" @click="handleSingleView(scope.row)"> <el-button link type="primary" @click="handleSingleView(scope.row)">
{{ t('DataCollection.HistoryData.tableActionHistoryLabel') }} {{ t('DataCollection.HistoryData.tableActionHistoryLabel') }}
</el-button> </el-button>
<el-button link type="primary" @click="handleSingleAnalyseView(scope.row)">
{{ t('DataCollection.HistoryData.tableActionHistoryAnalyseLabel') }}
</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -137,9 +140,10 @@ import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download' import download from '@/utils/download'
import { DeviceApi, LineDeviceVO, LineDevicePageParams } from '@/api/iot/device' import { DeviceApi, LineDeviceVO, LineDevicePageParams } from '@/api/iot/device'
import HistorySingleDeviceDialog from './HistorySingleDeviceDialog.vue' import HistorySingleDeviceDialog from './HistorySingleDeviceDialog.vue'
import {useRouter} from "vue-router";
//
const router = useRouter()
defineOptions({ name: 'HistoryData' }) defineOptions({ name: 'HistoryData' })
const message = useMessage() const message = useMessage()
const { t } = useI18n() const { t } = useI18n()
@ -225,6 +229,27 @@ const handleSingleView = (row: LineDeviceVO) => {
singleDialogVisible.value = true singleDialogVisible.value = true
} }
const handleSingleAnalyseView = (row: LineDeviceVO) => {
const deviceId = (row as any)?.deviceId ?? row?.id
if (deviceId === undefined || deviceId === null || deviceId === '') {
return
}
singleDeviceId.value = deviceId
singleDeviceName.value = row.deviceName || ''
router.push({
path: '/iot/historySingleAnalyse',
query: {
id: deviceId,
name: singleDeviceName.value,
deviceCode: row.deviceCode,
lineNode: row.lineNode,
lineName: row.lineName,
collectionTime: row.collectionTime
}
});
}
onMounted(() => { onMounted(() => {
getList() getList()
}) })

@ -7,15 +7,15 @@
</div> </div>
<ContentWrap v-loading="loading"> <ContentWrap v-loading="loading">
<div v-if="sections.length" class="single-device-dialog__table-grid"> <div v-if="sections.length" class="single-device-dialog__table-grid">
<div v-for="section in sections" :key="section.key" class="single-device-dialog__section"> <!-- <div v-for="section in sections" :key="section.key" class="single-device-dialog__section">
<div class="single-device-dialog__section-title"> <div class="single-device-dialog__section-title">
{{ section.title }} {{ section.title }}
</div> </div>-->
<el-empty <!-- <el-empty
v-if="!section.columns.length" v-if="!section.length"
:description="t('DataCollection.RealTimeMonitoring.emptyDescription')" :description="t('DataCollection.RealTimeMonitoring.emptyDescription')"
/> />-->
<el-table <!-- <el-table
v-else :data="section.rows" :border="true" :header-cell-style="headerCellStyle" v-else :data="section.rows" :border="true" :header-cell-style="headerCellStyle"
:cell-style="bodyCellStyle" size="small"> :cell-style="bodyCellStyle" size="small">
<el-table-column <el-table-column
@ -25,8 +25,22 @@ v-for="col in section.columns" :key="col.prop" :prop="col.prop" :label="col.labe
<span>{{ formatCell(scope.row[col.prop]) }}</span> <span>{{ formatCell(scope.row[col.prop]) }}</span>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>-->
</div> <div v-for="section in sections" :key="section.id" class="form-section">
<h3 v-if="section.title" class="section-title">{{ section.title }}</h3>
<div class="form-grid">
<div
v-for="item in section.items"
:key="item.prop"
class="form-item"
>
<div class="form-label">{{ item.label }}:</div>
<div class="form-value">{{ formatCellTwo(item.value) }}</div>
</div>
</div>
</div>
<!-- </div>-->
</div> </div>
<el-empty <el-empty
v-else v-else
@ -49,10 +63,14 @@ type SectionColumn = {
type SectionRow = Record<string, string | number | null> type SectionRow = Record<string, string | number | null>
type Section = { type Section = {
key: string id: number
title: string title?: string
columns: SectionColumn[] items: FormItem[]
rows: SectionRow[] }
interface FormItem {
prop: string
label: string
value: any
} }
const { t } = useI18n() const { t } = useI18n()
@ -83,8 +101,44 @@ const dialogTitle = computed(() => {
const loading = ref(false) const loading = ref(false)
const deviceName = computed(() => props.deviceName) const deviceName = computed(() => props.deviceName)
const sections = ref<Section[]>([]) const sections = ref<Section[]>([
{
id: 1,
title: '工艺参数监控',
items: [
{ prop: 'addressValue', label: '系统运行', value: 1, unit: '℃' },
{ prop: 'attributeName', label: '烘干湿度', value: 2, unit: '%' },
{ prop: 'attributeName', label: '输送速度', value: 3, unit: 'm/min' },
{ prop: 'attributeName', label: '回风温度', value: 4, unit: '℃' },
{ prop: 'attributeName', label: '排风温度', value: 5, unit: '℃' },
{ prop: 'attributeName', label: '呼叫状态', value: 6 },
{ prop: 'attributeName', label: '在线状态', value: 7 },
{ prop: 'attributeName', label: '运行状态', value: 3 },
{ prop: 'attributeName', label: '主缸速度', value: 0, unit: '%' },
{ prop: 'attributeName', label: '提布辊速度', value: 0 },
{ prop: 'fanSpeed', label: '风机速度', value: '暂无数据' },
{ prop: 'mainLevel', label: '主缸水位', value: 1, unit: '%' },
{ prop: 'mainWater', label: '主缸水量', value: 145, unit: 'L' },
{ prop: 'setTemp', label: '设定温度', value: 0.0, unit: '℃' },
{ prop: 'actualTemp', label: '实际温度', value: 0.0, unit: '℃' },
],
},
])
const formatValue = (item: FormItem) => {
let value = item.value
if (value === null || value === undefined) return '--'
if (typeof value === 'number') {
value = Number.isInteger(value) ? value.toString() : value.toFixed(2)
}
if (item.unit) {
value += ` ${item.unit}`
}
return value
}
const toGroupMap = (value: any): Record<string, any[]> => { const toGroupMap = (value: any): Record<string, any[]> => {
if (!value || typeof value !== 'object' || Array.isArray(value)) { if (!value || typeof value !== 'object' || Array.isArray(value)) {
return {} return {}
@ -136,6 +190,14 @@ const formatCell = (value: string | number | null | undefined) => {
return value return value
} }
const formatCellTwo = (value: any) => {
//
if (typeof value === 'number') {
return value.toFixed(2)
}
return value
}
const buildSectionsFromGroups = (groups: Record<string, any[]>): Section[] => { const buildSectionsFromGroups = (groups: Record<string, any[]>): Section[] => {
const result: Section[] = [] const result: Section[] = []
for (const [key, list] of Object.entries(groups)) { for (const [key, list] of Object.entries(groups)) {
@ -150,12 +212,12 @@ const buildSectionsFromGroups = (groups: Record<string, any[]>): Section[] => {
row[prop] = item?.addressValue ?? '-' row[prop] = item?.addressValue ?? '-'
}) })
} }
result.push({ /* result.push({
key, key,
title: key, title: key,
columns, columns,
rows: columns.length ? [row] : [] rows: columns.length ? [row] : []
}) })*/
} }
return result return result
} }
@ -167,15 +229,16 @@ const fetchList = async () => {
} }
loading.value = true loading.value = true
try { try {
const res: any = await DeviceApi.getSingleDevice({ deviceId: props.deviceId }) const res: any = await DeviceApi.getSingleDeviceFrom({ deviceId: props.deviceId })
const groups = Array.isArray(res) /* const groups = Array.isArray(res)
? { [t('DataCollection.RealTimeMonitoring.defaultGroupName')]: res } ? { [t('DataCollection.RealTimeMonitoring.defaultGroupName')]: res }
: { : {
...toGroupMap(res?.data), ...toGroupMap(res?.data),
...toGroupMap(res?.data?.data), ...toGroupMap(res?.data?.data),
...toGroupMap(res) ...toGroupMap(res)
} }
sections.value = buildSectionsFromGroups(groups) //sections.value = buildSectionsFromGroups(groups)*/
sections.value = res
} finally { } finally {
loading.value = false loading.value = false
} }
@ -222,4 +285,84 @@ watch(
.single-device-dialog__section :deep(.el-table__inner-wrapper) { .single-device-dialog__section :deep(.el-table__inner-wrapper) {
border-radius: 0; border-radius: 0;
} }
/* 表单容器 */
.form-section {
margin-bottom: 24px;
}
/* 标题样式 */
.section-title {
font-size: 13px;
font-weight: bold;
margin-bottom: 12px;
color: var(--el-text-color-primary);
}
/* 三列网格容器 */
.form-grid {
display: grid;
grid-template-columns: repeat(4, 1fr); /* 三列等宽 */
/*gap: 16px;*/ /* 列间距和行间距 */
/* padding: 8px;*/
background-color: #fff;
/* border: 1px solid #dcdfe6; !* 外边框 *!*/
border-radius: 4px;
min-height: 35px;
}
/* 表单项:标签 + 值 */
.form-item {
display: flex;
align-items: center;
justify-content: space-between;
height: 100%;
min-height: 35px;
/* padding: 8px 0;*/
border: 1px solid #dcdfe6;
/* border-right: 1px solid #dcdfe6; !* *!
border-bottom: 1px solid #dcdfe6; !* 下边框 *!*/
}
/* 标签样式 */
.form-label {
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
color: #666;
height: 100%;
min-height: 35px;
width: 150px; /* 固定标签宽度,避免错位 */
background-color: #f5f7fa; /* 背景色 */
}
/* 值样式 */
.form-value {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
height: 100%;
padding: 6px 12px;
background: white;
min-height: 35px;
box-sizing: border-box;
overflow: hidden;
cursor: default;
transition: all 0.2s;
}
/* 响应式:小屏幕下改为两列或单列 */
@media (max-width: 768px) {
.form-grid {
grid-template-columns: repeat(2, 1fr); /* 平板改为两列 */
}
}
@media (max-width: 480px) {
.form-grid {
grid-template-columns: 1fr; /* 手机改为单列 */
}
}
</style> </style>

@ -79,7 +79,7 @@
</el-tooltip> </el-tooltip>
</span> </span>
</template> </template>
<el-select v-model="formData.orgType" @change="handleOrgTypeChange" :placeholder="t('FactoryModeling.FactoryStructure.dialogOrgTypePlaceholder')"> <el-select v-model="formData.orgType" :placeholder="t('FactoryModeling.FactoryStructure.dialogOrgTypePlaceholder')">
<el-option <el-option
v-for="dict in getStrDictOptions(DICT_TYPE.MES_ORG_TYPE)" v-for="dict in getStrDictOptions(DICT_TYPE.MES_ORG_TYPE)"
:key="dict.value" :key="dict.value"
@ -89,25 +89,20 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item :label="t('FactoryModeling.FactoryStructure.dialogMachineLabel')" v-if="formData.orgClass == 'workplace'" prop="machineId"> <el-form-item :label="t('FactoryModeling.FactoryStructure.dialogMachineLabel')" v-if="formData.orgClass == 'workplace'" prop="machineId">
<!-- <el-tree-select <!-- <el-select v-model="formData.machineId" :placeholder="t('FactoryModeling.FactoryStructure.dialogMachinePlaceholder')" clearable>
v-model="formData.machineId"
:data="machineComponentTree"
:props="defaultProps"
check-strictly
default-expand-all
placeholder="请选择机台"
/> -->
<el-select v-model="formData.machineId" :placeholder="t('FactoryModeling.FactoryStructure.dialogMachinePlaceholder')" clearable>
<el-option <el-option
v-for="item in deviceList" v-for="item in deviceList"
:key="item.id" :key="item.id"
:label="item.deviceName" :label="item.deviceName"
:value="item.id" :value="item.id"
/> />
</el-select> </el-select>-->
<el-input :model-value="displayItemDevice" readonly clearable class="device-ledger-selection-input"
:placeholder="t('FactoryModeling.FactoryStructure.dialogMachinePlaceholder')"
@click="openCriticalComponentDialog" />
</el-form-item> </el-form-item>
<el-form-item :label="t('EquipmentManagement.EquipmentLedger.dvName')" prop="dvId" v-if="formData.orgClass == 'workplace'"> <el-form-item :label="t('EquipmentManagement.EquipmentLedger.dvName')" prop="dvId" v-if="formData.orgClass == 'workplace'">
<el-select v-model="formData.dvId" filterable :placeholder="t('EquipmentManagement.EquipmentLedger.dvId')" clearable> <!-- <el-select v-model="formData.dvId" filterable :placeholder="t('EquipmentManagement.EquipmentLedger.dvId')" clearable>
<el-option <el-option
v-for="item in dvList" v-for="item in dvList"
:key="item.id" :key="item.id"
@ -115,7 +110,10 @@
:value="item.id" :value="item.id"
:disabled="item.selected === true" :disabled="item.selected === true"
/> />
</el-select> </el-select>-->
<el-input :model-value="dvIdItemDevice" readonly clearable class="device-ledger-selection-input"
:placeholder="t('EquipmentManagement.EquipmentLedger.dvId')"
@click="openCaijiComponentDialog" />
</el-form-item> </el-form-item>
<!-- <el-form-item label="组织状态" prop="status"> <!-- <el-form-item label="组织状态" prop="status">
<el-radio-group v-model="formData.status"> <el-radio-group v-model="formData.status">
@ -134,6 +132,61 @@
<el-button @click="dialogVisible = false">{{ t('FactoryModeling.FactoryStructure.dialogCancelButton') }}</el-button> <el-button @click="dialogVisible = false">{{ t('FactoryModeling.FactoryStructure.dialogCancelButton') }}</el-button>
</template> </template>
</Dialog> </Dialog>
<TableSelectDialog
ref="deviceSelectDialogRef"
title="选择设备"
:columns="deviceColumns"
:fetch-api="fetchDeviceLedgerPage"
row-key="id"
@confirm="handleDeviceSelectConfirm"
:query-params="mergedQueryParams"
:selection-type="'single'"
:default-selected-keys="ids"
>
<!-- 使用 header 插槽插入查询表单 -->
<template #header>
<el-form ref="searchFormRef" :model="searchParams" :inline="true" >
<el-form-item label="设备编号" prop="deviceCode">
<el-input v-model="searchParams.deviceCode" placeholder="请输入编号" clearable />
</el-form-item>
<el-form-item label="设备名称" prop="deviceName">
<el-input v-model="searchParams.deviceName" placeholder="请输入名称" clearable />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">{{ t('FactoryModeling.ProductInformation.searchButtonText') }}</el-button>
<el-button @click="resetSearch">{{ t('FactoryModeling.ProductInformation.resetButtonText') }}</el-button>
</el-form-item>
</el-form>
</template>
</TableSelectDialog>
<TableSelectDialog
ref="caiJiSelectDialogRef"
title="选择采集设备"
:columns="caiJiColumns"
:fetch-api="fetchCaiJILedgerPage"
row-key="id"
@confirm="handleCaiJISelectConfirm"
:query-params="mergedQueryParams"
:selection-type="'single'"
:default-selected-keys="CJIds"
>
<!-- 使用 header 插槽插入查询表单 -->
<template #header>
<el-form ref="searchFormRef" :model="searchParams" :inline="true" >
<el-form-item label="设备编号" prop="deviceCode">
<el-input v-model="searchParams.deviceCode" placeholder="请输入编号" clearable />
</el-form-item>
<el-form-item label="设备名称" prop="deviceName">
<el-input v-model="searchParams.deviceName" placeholder="请输入名称" clearable />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleCJSearch">{{ t('FactoryModeling.ProductInformation.searchButtonText') }}</el-button>
<el-button @click="resetCJSearch">{{ t('FactoryModeling.ProductInformation.resetButtonText') }}</el-button>
</el-form-item>
</el-form>
</template>
</TableSelectDialog>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { DICT_TYPE, getStrDictOptions } from '@/utils/dict' import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
@ -142,13 +195,14 @@ import { defaultProps, handleTree } from '@/utils/tree'
import {MachineComponentApi} from "@/api/mes/machine"; import {MachineComponentApi} from "@/api/mes/machine";
import {DeviceLedgerApi, DeviceLedgerVO} from "@/api/mes/deviceledger"; import {DeviceLedgerApi, DeviceLedgerVO} from "@/api/mes/deviceledger";
import { DeviceApi,DeviceVO } from '@/api/iot/device' import { DeviceApi,DeviceVO } from '@/api/iot/device'
import TableSelectDialog from '@/components/TableSelectDialog/TableSelectDialog.vue'
/** 产线工位 表单 */ /** 产线工位 表单 */
defineOptions({ name: 'OrganizationForm' }) defineOptions({ name: 'OrganizationForm' })
const loading = ref(true)
const { t } = useI18n() // const { t } = useI18n() //
const message = useMessage() // const message = useMessage() //
const deviceSelectDialogRef = ref(false)
const caiJiSelectDialogRef = ref(false)
const dvList = ref<DeviceVO[]>([]) // const dvList = ref<DeviceVO[]>([]) //
const deviceList = ref<DeviceLedgerVO[]>([]) // const deviceList = ref<DeviceLedgerVO[]>([]) //
const dialogVisible = ref(false) // const dialogVisible = ref(false) //
@ -179,22 +233,70 @@ const formRules = reactive({
parentId: [{ required: true, message: t('FactoryModeling.FactoryStructure.validatorParentRequired'), trigger: 'blur' }], parentId: [{ required: true, message: t('FactoryModeling.FactoryStructure.validatorParentRequired'), trigger: 'blur' }],
orgClass: [{ required: true, message: t('FactoryModeling.FactoryStructure.validatorOrgClassRequired'), trigger: 'blur' }] orgClass: [{ required: true, message: t('FactoryModeling.FactoryStructure.validatorOrgClassRequired'), trigger: 'blur' }]
}) })
const itemList = ref<DeviceLedgerVO[]>([])
const CJList = ref<DeviceVO[]>([]) //
const mergedQueryParams = computed(() => ({ ...searchParams }))
const formRef = ref() // Ref const formRef = ref() // Ref
const organizationTree = ref() // const organizationTree = ref() //
const deviceColumns = [
{ label: '设备编号', prop: 'deviceCode', minWidth: 140 },
{ label: '设备名称', prop: 'deviceName', minWidth: 160 },
{ label: '设备型号', prop: 'deviceModel', minWidth: 140 },
{ label: '所属车间', prop: 'workshop', minWidth: 140 }
]
const caiJiColumns = [
{ label: '设备编号', prop: 'deviceCode', minWidth: 140 },
{ label: '设备名称', prop: 'deviceName', minWidth: 160 },
]
// 1.
const searchParams = reactive({
deviceCode: undefined,
deviceName: undefined,
})
const handleSearch = () => {
//
deviceSelectDialogRef.value?.reload?.()
}
const searchFormRef = ref()
const resetSearch = () => {
searchFormRef.value?.resetFields()
handleSearch()
}
const handleCJSearch = () => {
//
caiJiSelectDialogRef.value?.reload?.()
}
const searchCJFormRef = ref()
const resetCJSearch = () => {
searchFormRef.value?.resetFields()
handleCJSearch()
}
/** 打开弹窗 */ /** 打开弹窗 */
const open = async (type: string, id?: number) => { const open = async (type: string, id?: number) => {
dialogVisible.value = true dialogVisible.value = true
dialogTitle.value = t('action.' + type) dialogTitle.value = t('action.' + type)
formType.value = type formType.value = type
resetForm() resetForm()
initSelectedItems()
// //
if (id) { if (id) {
formLoading.value = true formLoading.value = true
try { try {
formData.value = await OrganizationApi.getOrganization(id) formData.value = await OrganizationApi.getOrganization(id)
ids.value=formData.value.machineId != undefined
? formData.value.machineId.toString().split(",").map(Number)
: []
CJIds.value=formData.value.dvId!=undefined?formData.value.dvId.toString().split(",").map(Number):[]
} finally { } finally {
formLoading.value = false formLoading.value = false
} }
}else{
ids.value=[]
CJIds.value=[]
} }
await getOrganizationTree() await getOrganizationTree()
if (type == 'create' || typeof formData.value.machineId != 'number') { if (type == 'create' || typeof formData.value.machineId != 'number') {
@ -283,8 +385,115 @@ const getMachineComponentTree = async () => {
root.children = handleTree(data, 'id', 'parentId') root.children = handleTree(data, 'id', 'parentId')
machineComponentTree.value.push(root) machineComponentTree.value.push(root)
} }
const ids = ref([])
const CJIds = ref([])
const displayItemDevice = computed( () => {
if (!ids.value.length)return ''
if (!itemList.value.length) {
return formData.value.machineId ? String(formData.value.machineId) : ''
}
const map = new Map(itemList.value.map((item) => [item.id, item.deviceName]))
const names = ids.value
.map((id) => map.get(id))
.filter((name) => name)
return names.join(',')
})
const dvIdItemDevice = computed( () => {
if (!CJIds.value.length)return ''
if (!CJList.value.length) {
return formData.value.dvId ? String(formData.value.dvId) : ''
}
const map = new Map(CJList.value.map((item) => [item.id, item.deviceName]))
const names = CJIds.value
.map((id) => map.get(id))
.filter((name) => name)
return names.join(',')
})
const fetchDeviceLedgerPage = (params: Record<string, any>) => {
return DeviceLedgerApi.getDeviceLedgerPage({
...params,
})
}
const fetchCaiJILedgerPage = (params: Record<string, any>) => {
return DeviceApi.getAvailableListPage({
...params,
})
}
const selectedDeviceRows = ref<any[]>([])
const selectedCaiJiRows = ref<any[]>([])
/*打开关联设备确认*/
const handleDeviceSelectConfirm = (payload: { ids: (number | string)[]; rows: any[] }) => {
formData.value.devices = payload.rows
.map((item) => {
const id = Number(item.id)
if (!Number.isFinite(id)) return undefined
return {
id,
name: item.deviceName || item.name || item.code || `设备ID:${id}`
}
})
.filter((item): item is { id: number; name: string } => Boolean(item))
selectedDeviceRows.value = payload.rows
formData.value.machineId = payload.ids.join(',')
ids.value = payload.ids.map((id) => Number(id))
}
/*打开关联设备*/
const openCriticalComponentDialog = async () => {
searchParams.deviceCode=''
searchParams.deviceName=''
const rows = selectedDeviceRows.value.map((item) => ({ ...item, id: Number(item.id) }))
deviceSelectDialogRef.value?.open(rows)
let initIds= formData.value.machineId!=undefined?formData.value.machineId.toString().split(","):[]
ids.value=initIds.map((id) => Number(id))
}
/*打开采集设备确认*/
const handleCaiJISelectConfirm = (payload: { ids: (number | string)[]; rows: any[] }) => {
formData.value.devices = payload.rows
.map((item) => {
const id = Number(item.id)
if (!Number.isFinite(id)) return undefined
return {
id,
name: item.deviceName || item.name || item.code || `设备ID:${id}`
}
})
.filter((item): item is { id: number; name: string } => Boolean(item))
selectedCaiJiRows.value = payload.rows
formData.value.dvId = payload.ids.join(',')
CJIds.value = payload.ids.map((id) => Number(id))
}
/*打开采集设备*/
const openCaijiComponentDialog = async () => {
searchParams.deviceCode=''
searchParams.deviceName=''
const rows = selectedCaiJiRows.value.map((item) => ({ ...item, id: Number(item.id) }))
caiJiSelectDialogRef.value?.open(rows)
let initIds= formData.value.dvId!=undefined?formData.value.dvId.toString().split(","):[]
CJIds.value=initIds.map((id) => Number(id))
}
const initSelectedItems = async () => {
if (!itemList.value.length) {
loading.value = true
try {
itemList.value = await DeviceLedgerApi.getDeviceLedgerList()
} finally {
loading.value = false
}
}
if (!CJList.value.length) {
loading.value = true
try {
CJList.value = await DeviceApi.getAvailableList()
} finally {
loading.value = false
}
}
}
/** 初始化 **/ /** 初始化 **/
onMounted(async () => { onMounted(async () => {
//console.log("ssss") //console.log("ssss")

@ -34,7 +34,7 @@
</el-form-item> </el-form-item>
<el-form-item :label="t('MoldManagement.MoldInspectionPlan.subjectName')" prop="subjectIds"> <el-form-item :label="t('MoldManagement.MoldInspectionPlan.subjectName')" prop="subjectIds">
<el-select <!-- <el-select
v-model="formData.subjectIds" v-model="formData.subjectIds"
multiple multiple
filterable filterable
@ -43,7 +43,10 @@
class="!w-full" class="!w-full"
> >
<el-option v-for="item in subjectOptions" :key="item.id" :label="item.subjectName" :value="item.id" /> <el-option v-for="item in subjectOptions" :key="item.id" :label="item.subjectName" :value="item.id" />
</el-select> </el-select>-->
<el-input :model-value="displayItemDevice" readonly clearable class="device-ledger-selection-input"
:placeholder="t('MoldManagement.MoldInspectionPlan.placeholderSubjectSelect')"
@click="openCriticalComponentDialog" />
</el-form-item> </el-form-item>
</el-form> </el-form>
@ -52,13 +55,40 @@
<el-button @click="dialogVisible = false">{{ t('common.cancel') }}</el-button> <el-button @click="dialogVisible = false">{{ t('common.cancel') }}</el-button>
</template> </template>
</Dialog> </Dialog>
<TableSelectDialog
ref="deviceSelectDialogRef"
title="选择设备"
:columns="deviceColumns"
:fetch-api="fetchDeviceLedgerPage"
row-key="id"
@confirm="handleDeviceSelectConfirm"
:query-params="mergedQueryParams"
:default-selected-keys="ids"
>
<!-- 使用 header 插槽插入查询表单 -->
<template #header>
<el-form ref="searchFormRef" :model="searchParams" :inline="true" >
<el-form-item label="设备编号" prop="deviceCode">
<el-input v-model="searchParams.deviceCode" placeholder="请输入编号" clearable />
</el-form-item>
<el-form-item label="设备名称" prop="deviceName">
<el-input v-model="searchParams.deviceName" placeholder="请输入名称" clearable />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">{{ t('FactoryModeling.ProductInformation.searchButtonText') }}</el-button>
<el-button @click="resetSearch">{{ t('FactoryModeling.ProductInformation.resetButtonText') }}</el-button>
</el-form-item>
</el-form>
</template>
</TableSelectDialog>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import type { FormRules } from 'element-plus' import type { FormRules } from 'element-plus'
import { DvSubjectApi, DvSubjectVO } from '@/api/mold/inspectionItems' import { DvSubjectApi, DvSubjectVO } from '@/api/mold/inspectionItems'
import { PlanMaintenanceApi, PlanMaintenanceVO } from '@/api/mold/planmaintenance' import { PlanMaintenanceApi, PlanMaintenanceVO } from '@/api/mold/planmaintenance'
import {DeviceLedgerApi, DeviceLedgerVO} from "@/api/mes/deviceledger";
import TableSelectDialog from '@/components/TableSelectDialog/TableSelectDialog.vue'
defineOptions({ name: 'MoldInspectionPlanForm' }) defineOptions({ name: 'MoldInspectionPlanForm' })
const { t } = useI18n() const { t } = useI18n()
@ -74,6 +104,7 @@ const ensureSubjectOptionsLoaded = async () => {
if (subjectOptions.value.length) return if (subjectOptions.value.length) return
const res = await DvSubjectApi.getMoldSubjectAllList() const res = await DvSubjectApi.getMoldSubjectAllList()
const list = Array.isArray(res) ? res : res?.list const list = Array.isArray(res) ? res : res?.list
itemList.value = (list ?? []) as DvSubjectVO[]
subjectOptions.value = (list ?? []) as DvSubjectVO[] subjectOptions.value = (list ?? []) as DvSubjectVO[]
} }
@ -97,7 +128,94 @@ const parseIds = (value: any): Array<number | string> => {
}) })
.filter((v): v is number | string => v !== undefined) .filter((v): v is number | string => v !== undefined)
} }
const ids = ref([])
const itemList = ref<DvSubjectVO[]>([])
const selectedDeviceRows = ref<any[]>([])
const deviceSelectDialogRef = ref(false)
const mergedQueryParams = computed(() => ({ ...searchParams }))
const deviceColumns = [
{ label: '项目编码', prop: 'subjectCode', minWidth: 140 },
{ label: '项目名称', prop: 'subjectName', minWidth: 160 },
{ label: '项目类型', prop: 'subjectType', minWidth: 140 },
{ label: '项目内容', prop: 'subjectContent', minWidth: 140 }
]
const fetchDeviceLedgerPage = async (params: Record<string, any>) => {
const res = await DvSubjectApi.getDvSubjectPage({
...params,
})
const list = res.list || []
//
const selectedMap = new Map(
selectedDeviceRows.value.map((item) => [item.id, item])
)
return {
...res,
list: list.map((item) => ({
...item,
...selectedMap.get(item.id)
}))
}
}
// 1.
const searchParams = reactive({
subjectCode: undefined,
subjectName: undefined,
})
const displayItemDevice = computed( () => {
if (!ids.value.length)return ''
if (!itemList.value.length) {
return formData.value.subjectIds ? String(formData.value.subjectIds) : ''
}
const map = new Map(itemList.value.map((item) => [item.id, item.subjectName]))
const names = ids.value
.map((id) => map.get(id))
.filter((name) => name)
return names.join(',')
})
const openCriticalComponentDialog = async () => {
searchParams.subjectCode=''
searchParams.subjectName=''
const rows = selectedDeviceRows.value.length
//deviceSelectDialogRef.value?.open(rows)
let initIds= formData.value.subjectIds!=undefined?formData.value.subjectIds.toString().split(","):[]
deviceSelectDialogRef.value?.open([], {
defaultSelectedKeys: initIds
})
ids.value=initIds.map((id) => Number(id))
}
const handleDeviceSelectConfirm = (payload: { ids: (number | string)[]; rows: any[] }) => {
formData.value.subjectIds = payload.rows
.map((item) => {
const id = Number(item.id)
if (!Number.isFinite(id)) return undefined
return {
id,
name: item.subjectName || item.name || item.code || `设备ID:${id}`
}
})
.filter((item): item is { id: number; name: string } => Boolean(item))
selectedDeviceRows.value = payload.rows
formData.value.subjectIds = payload.ids.join(',')
ids.value = payload.ids.map((id) => Number(id))
}
const handleSearch = () => {
//
deviceSelectDialogRef.value?.reload?.()
}
const searchFormRef = ref()
const resetSearch = () => {
searchFormRef.value?.resetFields()
handleSearch()
}
const initFormData = () => ({ const initFormData = () => ({
id: undefined as PlanMaintenanceVO['id'], id: undefined as PlanMaintenanceVO['id'],
planName: '' as string, planName: '' as string,
@ -141,6 +259,9 @@ const open = async (type: 'create' | 'update', row?: Partial<PlanMaintenanceVO>)
description: (row.description as any) ?? '', description: (row.description as any) ?? '',
subjectIds: parseIds((row as any).subjectIds ?? (row as any).subjectIdS) subjectIds: parseIds((row as any).subjectIds ?? (row as any).subjectIdS)
} }
ids.value=parseIds((row as any).subjectIds ?? (row as any).subjectIdS)
}else{
ids.value=[]
} }
} }

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save