Initial commit
This commit is contained in:
25
src/App.vue
Normal file
25
src/App.vue
Normal file
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<router-view/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
html,
|
||||
body,
|
||||
#app {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||||
color: #1f2d3d;
|
||||
background: #f3f5f8;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
376
src/data/default-signal-table.json
Normal file
376
src/data/default-signal-table.json
Normal file
@@ -0,0 +1,376 @@
|
||||
{
|
||||
"devices": [
|
||||
{
|
||||
"id": "dev-001",
|
||||
"deviceName": "放油设备",
|
||||
"deviceModel": "S1500",
|
||||
"deviceIp": "192.168.1.60",
|
||||
"enabled": 1,
|
||||
"stations": [
|
||||
{
|
||||
"id": "station-001",
|
||||
"deviceName": "放油前",
|
||||
"stationName": "ST04",
|
||||
"dataSource": "放油前",
|
||||
"deviceModel": "S1500",
|
||||
"deviceIp": "192.168.1.60",
|
||||
"db": "DB10000",
|
||||
"enabled": 1
|
||||
},
|
||||
{
|
||||
"id": "station-002",
|
||||
"deviceName": "放油后",
|
||||
"stationName": "ST05",
|
||||
"dataSource": "放油后",
|
||||
"deviceModel": "S1500",
|
||||
"deviceIp": "192.168.1.60",
|
||||
"db": "DB10001",
|
||||
"enabled": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "dev-002",
|
||||
"deviceName": "注油设备",
|
||||
"deviceModel": "S1500",
|
||||
"deviceIp": "192.168.1.61",
|
||||
"enabled": 0,
|
||||
"stations": [
|
||||
{
|
||||
"id": "station-003",
|
||||
"deviceName": "注油前",
|
||||
"stationName": "ST06",
|
||||
"dataSource": "注油前",
|
||||
"deviceModel": "S1500",
|
||||
"deviceIp": "192.168.1.61",
|
||||
"db": "DB10002",
|
||||
"enabled": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"variables": [
|
||||
{
|
||||
"tagId": "TAG_0000001",
|
||||
"tagName": "PLC_PC_工件到位",
|
||||
"tagTypeId": 11,
|
||||
"tagLong": 1,
|
||||
"variableType": "Bool",
|
||||
"variableAddress": 0,
|
||||
"variableExtra": ".0",
|
||||
"typeCode": "2",
|
||||
"typeName": "PC不使用,仅做信号显示",
|
||||
"enabled": 1,
|
||||
"monitorGroup": "Monitor",
|
||||
"isMonitor": 1
|
||||
},
|
||||
{
|
||||
"tagId": "TAG_0000002",
|
||||
"tagName": "PLC_PC_读取完成",
|
||||
"tagTypeId": 11,
|
||||
"tagLong": 1,
|
||||
"variableType": "Bool",
|
||||
"variableAddress": 0,
|
||||
"variableExtra": ".1",
|
||||
"typeCode": "100001",
|
||||
"typeName": "PC不使用,仅做信号显示",
|
||||
"enabled": 1,
|
||||
"monitorGroup": "Monitor",
|
||||
"isMonitor": 1
|
||||
},
|
||||
{
|
||||
"tagId": "TAG_0000003",
|
||||
"tagName": "PLC_PC_开始称重",
|
||||
"tagTypeId": 11,
|
||||
"tagLong": 1,
|
||||
"variableType": "Bool",
|
||||
"variableAddress": 0,
|
||||
"variableExtra": ".2",
|
||||
"typeCode": "100002",
|
||||
"typeName": "PC不使用,仅做信号显示",
|
||||
"enabled": 1,
|
||||
"monitorGroup": "Monitor",
|
||||
"isMonitor": 1
|
||||
},
|
||||
{
|
||||
"tagId": "TAG_0000004",
|
||||
"tagName": "PLC_PC_称重完成",
|
||||
"tagTypeId": 11,
|
||||
"tagLong": 1,
|
||||
"variableType": "Bool",
|
||||
"variableAddress": 0,
|
||||
"variableExtra": ".3",
|
||||
"typeCode": "100003",
|
||||
"typeName": "PC不使用,仅做信号显示",
|
||||
"enabled": 1,
|
||||
"monitorGroup": "Monitor",
|
||||
"isMonitor": 1
|
||||
},
|
||||
{
|
||||
"tagId": "TAG_0000005",
|
||||
"tagName": "PLC_PC_等待放行",
|
||||
"tagTypeId": 11,
|
||||
"tagLong": 1,
|
||||
"variableType": "Bool",
|
||||
"variableAddress": 0,
|
||||
"variableExtra": ".4",
|
||||
"typeCode": "100004",
|
||||
"typeName": "PC不使用,仅做信号显示",
|
||||
"enabled": 1,
|
||||
"monitorGroup": "Monitor",
|
||||
"isMonitor": 1
|
||||
},
|
||||
{
|
||||
"tagId": "TAG_0000006",
|
||||
"tagName": "PLC_PC_系统健康",
|
||||
"tagTypeId": 11,
|
||||
"tagLong": 1,
|
||||
"variableType": "Bool",
|
||||
"variableAddress": 2,
|
||||
"variableExtra": ".0",
|
||||
"typeCode": "14",
|
||||
"typeName": "心跳处理:PLC按延时5秒,取反写回PLC,PLC监控PC心跳,如果10秒值不改变报通讯故障。",
|
||||
"enabled": 1,
|
||||
"monitorGroup": "Monitor",
|
||||
"isMonitor": 1
|
||||
},
|
||||
{
|
||||
"tagId": "TAG_0000007",
|
||||
"tagName": "PC_PLC_系统健康",
|
||||
"tagTypeId": 11,
|
||||
"tagLong": 1,
|
||||
"variableType": "Bool",
|
||||
"variableAddress": 2,
|
||||
"variableExtra": ".1",
|
||||
"typeCode": "15",
|
||||
"typeName": "收到PLC心跳信号后,PC按原值回写。",
|
||||
"enabled": 1,
|
||||
"monitorGroup": "NoMonitor",
|
||||
"isMonitor": 0
|
||||
},
|
||||
{
|
||||
"tagId": "TAG_0000008",
|
||||
"tagName": "PLC_PC_请求工作",
|
||||
"tagTypeId": 11,
|
||||
"tagLong": 1,
|
||||
"variableType": "Bool",
|
||||
"variableAddress": 2,
|
||||
"variableExtra": ".2",
|
||||
"typeCode": "44",
|
||||
"typeName": "",
|
||||
"enabled": 1,
|
||||
"monitorGroup": "Monitor",
|
||||
"isMonitor": 1
|
||||
},
|
||||
{
|
||||
"tagId": "TAG_0000009",
|
||||
"tagName": "PC_PLC_允许工作",
|
||||
"tagTypeId": 11,
|
||||
"tagLong": 1,
|
||||
"variableType": "Bool",
|
||||
"variableAddress": 2,
|
||||
"variableExtra": ".3",
|
||||
"typeCode": "66",
|
||||
"typeName": "",
|
||||
"enabled": 1,
|
||||
"monitorGroup": "Monitor",
|
||||
"isMonitor": 1
|
||||
},
|
||||
{
|
||||
"tagId": "TAG_0000010",
|
||||
"tagName": "PLC_PC_请求保存",
|
||||
"tagTypeId": 11,
|
||||
"tagLong": 1,
|
||||
"variableType": "Bool",
|
||||
"variableAddress": 2,
|
||||
"variableExtra": ".4",
|
||||
"typeCode": "19",
|
||||
"typeName": "",
|
||||
"enabled": 1,
|
||||
"monitorGroup": "Monitor",
|
||||
"isMonitor": 1
|
||||
},
|
||||
{
|
||||
"tagId": "TAG_0000011",
|
||||
"tagName": "PC_PLC_保存完成",
|
||||
"tagTypeId": 11,
|
||||
"tagLong": 1,
|
||||
"variableType": "Bool",
|
||||
"variableAddress": 2,
|
||||
"variableExtra": ".5",
|
||||
"typeCode": "20",
|
||||
"typeName": "",
|
||||
"enabled": 1,
|
||||
"monitorGroup": "Monitor",
|
||||
"isMonitor": 1
|
||||
},
|
||||
{
|
||||
"tagId": "TAG_0000012",
|
||||
"tagName": "PC_PLC_扫码枪扫码完成",
|
||||
"tagTypeId": 11,
|
||||
"tagLong": 1,
|
||||
"variableType": "Bool",
|
||||
"variableAddress": 2,
|
||||
"variableExtra": ".6",
|
||||
"typeCode": "100016",
|
||||
"typeName": "有扫码枪的工位,扫码完成PC置位(1秒后PC复位),PLC根据自己逻辑判断适当时候取码值。",
|
||||
"enabled": 1,
|
||||
"monitorGroup": "Monitor",
|
||||
"isMonitor": 1
|
||||
},
|
||||
{
|
||||
"tagId": "TAG_0000013",
|
||||
"tagName": "PC_PLC_称重清零触发",
|
||||
"tagTypeId": 11,
|
||||
"tagLong": 1,
|
||||
"variableType": "Bool",
|
||||
"variableAddress": 2,
|
||||
"variableExtra": ".7",
|
||||
"typeCode": "100017",
|
||||
"typeName": "点击清空按钮置位(1秒后PC复位),PLC根据自己逻辑判断适当时候清除。",
|
||||
"enabled": 1,
|
||||
"monitorGroup": "Monitor",
|
||||
"isMonitor": 1
|
||||
},
|
||||
{
|
||||
"tagId": "TAG_0000014",
|
||||
"tagName": "PC_PLC_报警代码",
|
||||
"tagTypeId": 2,
|
||||
"tagLong": 2,
|
||||
"variableType": "Int",
|
||||
"variableAddress": "10",
|
||||
"variableExtra": "",
|
||||
"typeCode": "112",
|
||||
"typeName": "报警时PC赋值,消除报警时,PLC复位;代码说明:0-无报警,1-获取工厂数据接口调用失败,2-上传工厂数据接口调用失败,3-发动机号错误",
|
||||
"enabled": 1,
|
||||
"monitorGroup": "NoMonitor",
|
||||
"isMonitor": 0
|
||||
},
|
||||
{
|
||||
"tagId": "TAG_0000015",
|
||||
"tagName": "PLC_PC_合格标志",
|
||||
"tagTypeId": 2,
|
||||
"tagLong": 2,
|
||||
"variableType": "Int",
|
||||
"variableAddress": "12",
|
||||
"variableExtra": "",
|
||||
"typeCode": "21",
|
||||
"typeName": "合格标志:0-没有工作;1-合格 ;2-不合格",
|
||||
"enabled": 1,
|
||||
"monitorGroup": "NoMonitor",
|
||||
"isMonitor": 0
|
||||
},
|
||||
{
|
||||
"tagId": "TAG_0000016",
|
||||
"tagName": "PLC_PC_称重重量",
|
||||
"tagTypeId": 4,
|
||||
"tagLong": 4,
|
||||
"variableType": "Real",
|
||||
"variableAddress": "14",
|
||||
"variableExtra": "",
|
||||
"typeCode": "100140",
|
||||
"typeName": "",
|
||||
"enabled": 1,
|
||||
"monitorGroup": "NoMonitor",
|
||||
"isMonitor": 0
|
||||
},
|
||||
{
|
||||
"tagId": "TAG_0000017",
|
||||
"tagName": "PLC_PC_水含量实时值",
|
||||
"tagTypeId": 4,
|
||||
"tagLong": 4,
|
||||
"variableType": "Real",
|
||||
"variableAddress": "18",
|
||||
"variableExtra": "",
|
||||
"typeCode": "100180",
|
||||
"typeName": "PC页面实时显示",
|
||||
"enabled": 1,
|
||||
"monitorGroup": "NoMonitor",
|
||||
"isMonitor": 0
|
||||
},
|
||||
{
|
||||
"tagId": "TAG_0000018",
|
||||
"tagName": "PLC_PC_重量实时值",
|
||||
"tagTypeId": 4,
|
||||
"tagLong": 4,
|
||||
"variableType": "Real",
|
||||
"variableAddress": "22",
|
||||
"variableExtra": "",
|
||||
"typeCode": "100220",
|
||||
"typeName": "PC页面实时显示",
|
||||
"enabled": 1,
|
||||
"monitorGroup": "NoMonitor",
|
||||
"isMonitor": 0
|
||||
},
|
||||
{
|
||||
"tagId": "TAG_0000019",
|
||||
"tagName": "PLC_PC_总成编号",
|
||||
"tagTypeId": 8,
|
||||
"tagLong": 60,
|
||||
"variableType": "String",
|
||||
"variableAddress": "50",
|
||||
"variableExtra": ".58",
|
||||
"typeCode": "10",
|
||||
"typeName": "",
|
||||
"enabled": 1,
|
||||
"monitorGroup": "NoMonitor",
|
||||
"isMonitor": 0
|
||||
},
|
||||
{
|
||||
"tagId": "TAG_0000020",
|
||||
"tagName": "PLC_PC_机型号",
|
||||
"tagTypeId": 8,
|
||||
"tagLong": 60,
|
||||
"variableType": "String",
|
||||
"variableAddress": "110",
|
||||
"variableExtra": ".58",
|
||||
"typeCode": "11",
|
||||
"typeName": "",
|
||||
"enabled": 1,
|
||||
"monitorGroup": "NoMonitor",
|
||||
"isMonitor": 0
|
||||
},
|
||||
{
|
||||
"tagId": "TAG_0000021",
|
||||
"tagName": "PLC_PC_工单号",
|
||||
"tagTypeId": 8,
|
||||
"tagLong": 60,
|
||||
"variableType": "String",
|
||||
"variableAddress": "170",
|
||||
"variableExtra": ".58",
|
||||
"typeCode": "39",
|
||||
"typeName": "",
|
||||
"enabled": 1,
|
||||
"monitorGroup": "NoMonitor",
|
||||
"isMonitor": 0
|
||||
},
|
||||
{
|
||||
"tagId": "TAG_0000022",
|
||||
"tagName": "PLC_PC_托盘号",
|
||||
"tagTypeId": 8,
|
||||
"tagLong": 20,
|
||||
"variableType": "String",
|
||||
"variableAddress": "230",
|
||||
"variableExtra": ".18",
|
||||
"typeCode": "80",
|
||||
"typeName": "",
|
||||
"enabled": 1,
|
||||
"monitorGroup": "NoMonitor",
|
||||
"isMonitor": 0
|
||||
},
|
||||
{
|
||||
"tagId": "TAG_0000023",
|
||||
"tagName": "PC_PLC_扫码枪值",
|
||||
"tagTypeId": 8,
|
||||
"tagLong": 100,
|
||||
"variableType": "String",
|
||||
"variableAddress": "250",
|
||||
"variableExtra": ".98",
|
||||
"typeCode": "102500",
|
||||
"typeName": "扫码枪扫码值",
|
||||
"enabled": 1,
|
||||
"monitorGroup": "NoMonitor",
|
||||
"isMonitor": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
48
src/data/default-variable-templates.json
Normal file
48
src/data/default-variable-templates.json
Normal file
@@ -0,0 +1,48 @@
|
||||
[
|
||||
{
|
||||
"id": "tpl-bool-signal",
|
||||
"templateName": "Bool 信号模板",
|
||||
"description": "常用 Bool 开关量信号",
|
||||
"fields": {
|
||||
"TagTypeID": "11",
|
||||
"TagLong": "1",
|
||||
"变量类型": "Bool",
|
||||
"是否启用": "1",
|
||||
"是否监控": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "tpl-int-signal",
|
||||
"templateName": "Int 数值模板",
|
||||
"description": "常用 Int 数值信号",
|
||||
"fields": {
|
||||
"TagTypeID": "2",
|
||||
"TagLong": "2",
|
||||
"变量类型": "Int",
|
||||
"是否启用": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "tpl-real-signal",
|
||||
"templateName": "Real 浮点模板",
|
||||
"description": "常用 Real 浮点信号",
|
||||
"fields": {
|
||||
"TagTypeID": "4",
|
||||
"TagLong": "4",
|
||||
"变量类型": "Real",
|
||||
"是否启用": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "tpl-string-signal",
|
||||
"templateName": "String 字符串模板",
|
||||
"description": "常用 String 字符串信号",
|
||||
"fields": {
|
||||
"TagTypeID": "8",
|
||||
"TagLong": "60",
|
||||
"变量类型": "String",
|
||||
"变量其它": ".58",
|
||||
"是否启用": "1"
|
||||
}
|
||||
}
|
||||
]
|
||||
13
src/main.js
Normal file
13
src/main.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import Vue from 'vue'
|
||||
import ElementUI from 'element-ui'
|
||||
import 'element-ui/lib/theme-chalk/index.css'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
|
||||
Vue.use(ElementUI)
|
||||
Vue.config.productionTip = false
|
||||
|
||||
new Vue({
|
||||
router,
|
||||
render: h => h(App)
|
||||
}).$mount('#app')
|
||||
15
src/router/index.js
Normal file
15
src/router/index.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import Vue from 'vue'
|
||||
import Router from 'vue-router'
|
||||
import SignalTableGenerator from '@/views/SignalTableGenerator.vue'
|
||||
|
||||
Vue.use(Router)
|
||||
|
||||
export default new Router({
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'SignalTableGenerator',
|
||||
component: SignalTableGenerator
|
||||
}
|
||||
]
|
||||
})
|
||||
35
src/utils/excelWriter.js
Normal file
35
src/utils/excelWriter.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import XLSX from 'xlsx'
|
||||
import { buildWorkbookSheets } from './signalTableBuilder'
|
||||
import { saveBlob } from './filePicker'
|
||||
|
||||
export function createSignalTableWorkbook(data) {
|
||||
const workbook = XLSX.utils.book_new()
|
||||
|
||||
buildWorkbookSheets(data).forEach(sheet => {
|
||||
const worksheet = XLSX.utils.aoa_to_sheet(sheet.rows)
|
||||
worksheet['!cols'] = (sheet.cols || []).map(wch => ({ wch }))
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, sheet.name)
|
||||
})
|
||||
|
||||
return workbook
|
||||
}
|
||||
|
||||
export async function exportSignalTable(data, filename = 'SignalTable_CZ.xlsx') {
|
||||
const workbook = createSignalTableWorkbook(data)
|
||||
const content = XLSX.write(workbook, {
|
||||
bookType: 'xlsx',
|
||||
type: 'array'
|
||||
})
|
||||
const blob = new Blob([content], {
|
||||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
})
|
||||
|
||||
return saveBlob(blob, filename, [
|
||||
{
|
||||
description: 'Excel 工作簿',
|
||||
accept: {
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': ['.xlsx']
|
||||
}
|
||||
}
|
||||
])
|
||||
}
|
||||
57
src/utils/filePicker.js
Normal file
57
src/utils/filePicker.js
Normal file
@@ -0,0 +1,57 @@
|
||||
export function isAbortError(error) {
|
||||
return error && error.name === 'AbortError'
|
||||
}
|
||||
|
||||
export function isSavePickerSupported() {
|
||||
return typeof window !== 'undefined' && typeof window.showSaveFilePicker === 'function'
|
||||
}
|
||||
|
||||
export function isOpenPickerSupported() {
|
||||
return typeof window !== 'undefined' && typeof window.showOpenFilePicker === 'function'
|
||||
}
|
||||
|
||||
function downloadBlob(blob, filename) {
|
||||
const url = URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.download = filename
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
URL.revokeObjectURL(url)
|
||||
}
|
||||
|
||||
export async function saveBlob(blob, filename, types) {
|
||||
if (isSavePickerSupported()) {
|
||||
const handle = await window.showSaveFilePicker({
|
||||
suggestedName: filename,
|
||||
types
|
||||
})
|
||||
const writable = await handle.createWritable()
|
||||
await writable.write(blob)
|
||||
await writable.close()
|
||||
return { method: 'picker' }
|
||||
}
|
||||
|
||||
downloadBlob(blob, filename)
|
||||
return { method: 'download' }
|
||||
}
|
||||
|
||||
export async function openJsonFile() {
|
||||
if (!isOpenPickerSupported()) return null
|
||||
|
||||
const handles = await window.showOpenFilePicker({
|
||||
multiple: false,
|
||||
types: [
|
||||
{
|
||||
description: 'JSON 配置文件',
|
||||
accept: {
|
||||
'application/json': ['.json']
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
if (!handles.length) return null
|
||||
return handles[0].getFile()
|
||||
}
|
||||
125
src/utils/opfsStore.js
Normal file
125
src/utils/opfsStore.js
Normal file
@@ -0,0 +1,125 @@
|
||||
import { saveBlob } from './filePicker'
|
||||
|
||||
const CONFIG_FILE_NAME = 'signal-table-config.json'
|
||||
const LOCAL_STORAGE_KEY = 'signal-table-config'
|
||||
|
||||
export function isOpfsSupported() {
|
||||
return Boolean(navigator.storage && navigator.storage.getDirectory)
|
||||
}
|
||||
|
||||
function wrapConfig(data) {
|
||||
return {
|
||||
version: 2,
|
||||
updatedAt: new Date().toISOString(),
|
||||
devices: Array.isArray(data.devices) ? data.devices : [],
|
||||
variables: Array.isArray(data.variables) ? data.variables : [],
|
||||
variableTemplates: Array.isArray(data.variableTemplates) ? data.variableTemplates : []
|
||||
}
|
||||
}
|
||||
|
||||
async function getConfigFileHandle(create) {
|
||||
const root = await navigator.storage.getDirectory()
|
||||
return root.getFileHandle(CONFIG_FILE_NAME, { create })
|
||||
}
|
||||
|
||||
async function readFromOpfs() {
|
||||
const handle = await getConfigFileHandle(false)
|
||||
const file = await handle.getFile()
|
||||
const text = await file.text()
|
||||
return JSON.parse(text)
|
||||
}
|
||||
|
||||
async function writeToOpfs(data) {
|
||||
const handle = await getConfigFileHandle(true)
|
||||
const writable = await handle.createWritable()
|
||||
await writable.write(JSON.stringify(wrapConfig(data), null, 2))
|
||||
await writable.close()
|
||||
}
|
||||
|
||||
function readFromLocalStorage() {
|
||||
const text = window.localStorage.getItem(LOCAL_STORAGE_KEY)
|
||||
return text ? JSON.parse(text) : null
|
||||
}
|
||||
|
||||
function writeToLocalStorage(data) {
|
||||
window.localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(wrapConfig(data), null, 2))
|
||||
}
|
||||
|
||||
export async function loadConfigFromOpfs() {
|
||||
if (isOpfsSupported()) {
|
||||
try {
|
||||
const data = await readFromOpfs()
|
||||
return { data, storageType: 'OPFS', existed: true }
|
||||
} catch (error) {
|
||||
if (error && error.name !== 'NotFoundError') throw error
|
||||
}
|
||||
}
|
||||
|
||||
const localData = readFromLocalStorage()
|
||||
return {
|
||||
data: localData,
|
||||
storageType: isOpfsSupported() ? 'OPFS' : 'localStorage',
|
||||
existed: Boolean(localData)
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveConfigToOpfs(data) {
|
||||
if (isOpfsSupported()) {
|
||||
await writeToOpfs(data)
|
||||
return { storageType: 'OPFS', updatedAt: new Date().toISOString() }
|
||||
}
|
||||
|
||||
writeToLocalStorage(data)
|
||||
return { storageType: 'localStorage', updatedAt: new Date().toISOString() }
|
||||
}
|
||||
|
||||
export async function resetConfigInOpfs(defaultData) {
|
||||
return saveConfigToOpfs(defaultData)
|
||||
}
|
||||
|
||||
export function createConfigJsonBlob(data) {
|
||||
return new Blob([JSON.stringify(wrapConfig(data), null, 2)], {
|
||||
type: 'application/json;charset=utf-8'
|
||||
})
|
||||
}
|
||||
|
||||
export async function exportConfigJson(data, filename = 'signal-table-config.json') {
|
||||
const blob = createConfigJsonBlob(data)
|
||||
return saveBlob(blob, filename, [
|
||||
{
|
||||
description: 'JSON 配置文件',
|
||||
accept: {
|
||||
'application/json': ['.json']
|
||||
}
|
||||
}
|
||||
])
|
||||
}
|
||||
|
||||
export function downloadConfigJson(data, filename = 'signal-table-config.json') {
|
||||
const blob = new Blob([JSON.stringify(wrapConfig(data), null, 2)], {
|
||||
type: 'application/json;charset=utf-8'
|
||||
})
|
||||
const url = URL.createObjectURL(blob)
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.download = filename
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
URL.revokeObjectURL(url)
|
||||
}
|
||||
|
||||
export function importConfigJson(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader()
|
||||
reader.onload = event => {
|
||||
try {
|
||||
resolve(JSON.parse(event.target.result))
|
||||
} catch (error) {
|
||||
reject(error)
|
||||
}
|
||||
}
|
||||
reader.onerror = () => reject(reader.error)
|
||||
reader.readAsText(file, 'utf-8')
|
||||
})
|
||||
}
|
||||
472
src/utils/signalTableBuilder.js
Normal file
472
src/utils/signalTableBuilder.js
Normal file
@@ -0,0 +1,472 @@
|
||||
export const MES_HEADERS = [
|
||||
'TagID',
|
||||
'TagName',
|
||||
'工位号',
|
||||
'数据来源',
|
||||
'IP',
|
||||
'TagTypeID',
|
||||
'TagLong',
|
||||
'DbName',
|
||||
'变量类型',
|
||||
'变量地址',
|
||||
'变量其它',
|
||||
'演示偏移',
|
||||
'列位置',
|
||||
'测量位置',
|
||||
'测量项目',
|
||||
'测量单位',
|
||||
'变量类型代码',
|
||||
'变量类型名称',
|
||||
'机体类型',
|
||||
'线体类型',
|
||||
'TagAdr',
|
||||
'TagAdrDemo',
|
||||
'是否启用',
|
||||
'监控组',
|
||||
'监控时间间隔',
|
||||
'OpcServer_OPNAME',
|
||||
'变量特点',
|
||||
'是否监控',
|
||||
'变量排序',
|
||||
'工序能力',
|
||||
'监控属性代码',
|
||||
'测量位置短',
|
||||
'上限值',
|
||||
'下限值',
|
||||
'理论值',
|
||||
'IsGoodBad',
|
||||
'IsSaveToSql',
|
||||
'IsWebSocket'
|
||||
]
|
||||
|
||||
export const DEVICE_HEADERS = ['设备IP', '设备类型', 'DB', '是否启用', '工位号', '数据来源']
|
||||
|
||||
export const VARIABLE_FIELD_GROUPS = [
|
||||
{
|
||||
title: '基础信息',
|
||||
fields: ['TagID', 'TagName', 'TagTypeID', 'TagLong', '工位号', '数据来源', 'IP', 'DbName', '是否启用']
|
||||
},
|
||||
{
|
||||
title: '变量定义',
|
||||
fields: ['变量类型', '变量地址', '变量其它', '变量类型代码', '变量类型名称']
|
||||
},
|
||||
{
|
||||
title: '测量与监控',
|
||||
fields: ['演示偏移', '列位置', '测量位置', '测量项目', '测量单位', '监控组', '监控时间间隔', '是否监控', '变量排序']
|
||||
},
|
||||
{
|
||||
title: '扩展字段',
|
||||
fields: ['机体类型', '线体类型', 'TagAdr', 'TagAdrDemo', 'OpcServer_OPNAME', '变量特点', '工序能力', '监控属性代码', '测量位置短']
|
||||
},
|
||||
{
|
||||
title: '阈值与保存',
|
||||
fields: ['上限值', '下限值', '理论值', 'IsGoodBad', 'IsSaveToSql', 'IsWebSocket']
|
||||
}
|
||||
]
|
||||
|
||||
const TEMPLATE_DEFAULTS = {
|
||||
'工位号': 'OPNAME',
|
||||
'数据来源': 'OPNAME',
|
||||
IP: 'IP',
|
||||
DbName: 'DB',
|
||||
'演示偏移': 0,
|
||||
'列位置': 1,
|
||||
'测量位置': '',
|
||||
'测量单位': '',
|
||||
'机体类型': '127',
|
||||
'线体类型': '',
|
||||
TagAdr: '无',
|
||||
TagAdrDemo: '无',
|
||||
'是否启用': 1,
|
||||
'监控组': 'NoMonitor',
|
||||
'监控时间间隔': '100',
|
||||
OpcServer_OPNAME: '0',
|
||||
'变量特点': '0',
|
||||
'是否监控': 0,
|
||||
'工序能力': '0',
|
||||
'监控属性代码': '1',
|
||||
'测量位置短': '0',
|
||||
'上限值': '100',
|
||||
'下限值': '0',
|
||||
'理论值': '50',
|
||||
IsGoodBad: '1',
|
||||
IsSaveToSql: '0',
|
||||
IsWebSocket: '0'
|
||||
}
|
||||
|
||||
const LEGACY_MAP = {
|
||||
tagId: 'TagID',
|
||||
tagName: 'TagName',
|
||||
tagTypeId: 'TagTypeID',
|
||||
tagLong: 'TagLong',
|
||||
variableType: '变量类型',
|
||||
variableAddress: '变量地址',
|
||||
variableExtra: '变量其它',
|
||||
typeCode: '变量类型代码',
|
||||
typeName: '变量类型名称',
|
||||
enabled: '是否启用',
|
||||
monitorGroup: '监控组',
|
||||
monitorInterval: '监控时间间隔',
|
||||
isMonitor: '是否监控',
|
||||
feature: '变量特点',
|
||||
measureItem: '测量项目'
|
||||
}
|
||||
|
||||
export function cloneData(data) {
|
||||
return JSON.parse(JSON.stringify(data || {}))
|
||||
}
|
||||
|
||||
export function nextTagId(variables) {
|
||||
const max = (variables || []).reduce((value, variable) => {
|
||||
const match = String(variable.TagID || variable.tagId || '').match(/TAG_(\d+)/)
|
||||
return match ? Math.max(value, Number(match[1])) : value
|
||||
}, 0)
|
||||
return `TAG_${String(max + 1).padStart(7, '0')}`
|
||||
}
|
||||
|
||||
function normalizeTagIdForStation(tagId, stationName) {
|
||||
const text = String(tagId || '').trim()
|
||||
const station = String(stationName || '').trim()
|
||||
if (!text || !station) return text
|
||||
if (text.startsWith(`${station}_`)) return text
|
||||
|
||||
const tagIndex = text.indexOf('TAG_')
|
||||
const tagPart = tagIndex === -1 ? text : text.slice(tagIndex)
|
||||
return `${station}_${tagPart}`
|
||||
}
|
||||
|
||||
function normalizeVariable(variable = {}, index = 0) {
|
||||
const row = {}
|
||||
MES_HEADERS.forEach(header => {
|
||||
row[header] = Object.prototype.hasOwnProperty.call(TEMPLATE_DEFAULTS, header) ? TEMPLATE_DEFAULTS[header] : ''
|
||||
})
|
||||
|
||||
Object.keys(LEGACY_MAP).forEach(key => {
|
||||
const target = LEGACY_MAP[key]
|
||||
if (Object.prototype.hasOwnProperty.call(variable, key)) row[target] = variable[key]
|
||||
})
|
||||
|
||||
MES_HEADERS.forEach(header => {
|
||||
if (Object.prototype.hasOwnProperty.call(variable, header)) row[header] = variable[header]
|
||||
})
|
||||
|
||||
row.TagID = row.TagID || nextTagId([])
|
||||
row.TagName = row.TagName || ''
|
||||
row['变量排序'] = Object.prototype.hasOwnProperty.call(variable, '变量排序') ? variable['变量排序'] : String(index)
|
||||
row['测量项目'] = row['测量项目'] || row.TagName
|
||||
|
||||
return row
|
||||
}
|
||||
|
||||
function hasMeaningfulVariableData(variable = {}) {
|
||||
const ignoredFields = new Set([
|
||||
'TagID',
|
||||
'工位号',
|
||||
'数据来源',
|
||||
'IP',
|
||||
'DbName',
|
||||
'是否启用',
|
||||
'变量排序'
|
||||
])
|
||||
|
||||
return MES_HEADERS.some(header => {
|
||||
if (ignoredFields.has(header)) return false
|
||||
const value = variable[header]
|
||||
return value !== undefined && value !== null && String(value).trim() !== ''
|
||||
})
|
||||
}
|
||||
|
||||
function normalizeStation(station = {}, index = 0, legacyDevice = {}) {
|
||||
return {
|
||||
id: station.id || `station-${index + 1}`,
|
||||
deviceName: station.deviceName || legacyDevice.deviceName || '',
|
||||
stationName: station.stationName || legacyDevice.stationName || '',
|
||||
dataSource: station.deviceName || station.dataSource || legacyDevice.dataSource || legacyDevice.deviceName || '',
|
||||
deviceModel: station.deviceModel || legacyDevice.deviceModel || '',
|
||||
deviceIp: station.deviceIp || legacyDevice.deviceIp || '',
|
||||
db: station.db || legacyDevice.db || '',
|
||||
enabled: Number(Object.prototype.hasOwnProperty.call(station, 'enabled') ? station.enabled : legacyDevice.enabled) === 0 ? 0 : 1
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeDevice(device = {}, index = 0, templateVariables = []) {
|
||||
const legacyStation = !Array.isArray(device.stations) && (device.stationName || device.db || device.dataSource)
|
||||
? [normalizeStation({}, 0, device)]
|
||||
: []
|
||||
const stations = Array.isArray(device.stations)
|
||||
? device.stations.map((station, stationIndex) => normalizeStation(station, stationIndex, device))
|
||||
: legacyStation
|
||||
const variableSource = Array.isArray(device.variables) ? device.variables : templateVariables
|
||||
|
||||
return {
|
||||
id: device.id || `dev-${index + 1}`,
|
||||
deviceName: device.deviceName || '',
|
||||
deviceModel: device.deviceModel || '',
|
||||
deviceIp: device.deviceIp || '',
|
||||
enabled: Number(device.enabled) === 1 ? 1 : 0,
|
||||
stations,
|
||||
variables: (variableSource || []).map(normalizeVariable)
|
||||
}
|
||||
}
|
||||
|
||||
export function normalizeData(data) {
|
||||
const source = cloneData(data)
|
||||
const templateVariables = Array.isArray(source.variables) ? source.variables.map(normalizeVariable) : []
|
||||
const devices = Array.isArray(source.devices)
|
||||
? source.devices.map((device, index) => normalizeDevice(device, index, templateVariables))
|
||||
: []
|
||||
const fallbackVariables = devices[0] && Array.isArray(devices[0].variables) ? devices[0].variables : []
|
||||
|
||||
return {
|
||||
devices,
|
||||
variables: templateVariables.length ? templateVariables : fallbackVariables.map(normalizeVariable),
|
||||
variableTemplates: Array.isArray(source.variableTemplates) ? cloneData(source.variableTemplates) : []
|
||||
}
|
||||
}
|
||||
|
||||
export function createVariableTemplate(variables, device, station) {
|
||||
const variable = normalizeVariable({
|
||||
TagID: nextTagId(variables),
|
||||
TagName: '',
|
||||
TagTypeID: 11,
|
||||
TagLong: 1,
|
||||
'变量类型': 'Bool',
|
||||
'变量地址': 0,
|
||||
'变量其它': '.0',
|
||||
'变量类型代码': '',
|
||||
'变量类型名称': '',
|
||||
'是否启用': 1,
|
||||
'监控组': 'Monitor',
|
||||
'是否监控': 1
|
||||
}, (variables || []).length)
|
||||
|
||||
return device && station ? applyStationToVariable(variable, device, station) : variable
|
||||
}
|
||||
|
||||
export function createBlankVariableTemplate(variables, device, station) {
|
||||
const variable = {}
|
||||
MES_HEADERS.forEach(header => {
|
||||
variable[header] = ''
|
||||
})
|
||||
|
||||
variable.TagID = nextTagId(variables)
|
||||
variable.TagName = ''
|
||||
variable['是否启用'] = 1
|
||||
|
||||
if (station) {
|
||||
variable['工位号'] = station.stationName || ''
|
||||
variable['数据来源'] = station.deviceName || station.dataSource || ''
|
||||
variable.IP = station.deviceIp || (device && device.deviceIp) || ''
|
||||
variable.DbName = station.db || ''
|
||||
}
|
||||
|
||||
return variable
|
||||
}
|
||||
|
||||
export function createDeviceVariables(variables) {
|
||||
return (variables || []).map(normalizeVariable)
|
||||
}
|
||||
|
||||
export function createStationTemplate(device, index = 0) {
|
||||
const firstStation = device && device.stations && device.stations[0] ? device.stations[0] : {}
|
||||
return {
|
||||
id: `station-${Date.now()}-${index}`,
|
||||
deviceName: firstStation.deviceName || (device && device.deviceName) || '',
|
||||
stationName: '',
|
||||
dataSource: firstStation.deviceName || (device && device.deviceName) || '',
|
||||
deviceModel: firstStation.deviceModel || (device && device.deviceModel) || 'S1500',
|
||||
deviceIp: firstStation.deviceIp || (device && device.deviceIp) || '',
|
||||
db: '',
|
||||
enabled: 1
|
||||
}
|
||||
}
|
||||
|
||||
export function applyStationToVariable(variable, device, station) {
|
||||
const row = normalizeVariable(variable)
|
||||
const stationName = station && station.stationName ? station.stationName : row['工位号']
|
||||
|
||||
return {
|
||||
...row,
|
||||
TagID: normalizeTagIdForStation(row.TagID, stationName),
|
||||
'工位号': stationName || '',
|
||||
'数据来源': station && (station.deviceName || station.dataSource) ? (station.deviceName || station.dataSource) : row['数据来源'],
|
||||
IP: station && station.deviceIp ? station.deviceIp : (device && device.deviceIp ? device.deviceIp : row.IP),
|
||||
DbName: station && station.db ? station.db : row.DbName
|
||||
}
|
||||
}
|
||||
|
||||
export function variablesForDisplay(device, station) {
|
||||
if (!device) return []
|
||||
return (device.variables || []).map(variable => {
|
||||
return station ? applyStationToVariable(variable, device, station) : normalizeVariable(variable)
|
||||
})
|
||||
}
|
||||
|
||||
export function expandedVariablesForDisplay(device) {
|
||||
if (!device) return []
|
||||
const stations = Array.isArray(device.stations) ? device.stations : []
|
||||
const variables = Array.isArray(device.variables) ? device.variables.filter(hasMeaningfulVariableData) : []
|
||||
if (!stations.length) return variables.map(normalizeVariable)
|
||||
|
||||
return stations.flatMap(station => {
|
||||
return variables.map((variable, index) => ({
|
||||
...applyStationToVariable(variable, device, station),
|
||||
_templateIndex: index,
|
||||
_stationId: station.id
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
export function buildDeviceRows(data) {
|
||||
const rows = [DEVICE_HEADERS]
|
||||
normalizeData(data).devices.forEach(device => {
|
||||
;(device.stations || []).forEach(station => {
|
||||
rows.push([
|
||||
station.deviceIp || device.deviceIp || '',
|
||||
station.deviceModel || device.deviceModel || '',
|
||||
station.db || '',
|
||||
Number(station.enabled) === 1 ? 1 : 0,
|
||||
station.stationName || '',
|
||||
station.deviceName || station.dataSource || ''
|
||||
])
|
||||
})
|
||||
})
|
||||
return rows
|
||||
}
|
||||
|
||||
export function buildMesRows(data) {
|
||||
const rows = [MES_HEADERS]
|
||||
normalizeData(data).devices.forEach(device => {
|
||||
;(device.stations || []).forEach(station => {
|
||||
;(device.variables || []).filter(hasMeaningfulVariableData).forEach(variable => {
|
||||
const row = applyStationToVariable(variable, device, station)
|
||||
rows.push(MES_HEADERS.map(header => row[header]))
|
||||
})
|
||||
})
|
||||
})
|
||||
return rows
|
||||
}
|
||||
|
||||
function removeEmptyColumns(rows, cols = []) {
|
||||
if (!rows.length) return { rows, cols }
|
||||
const dataRows = rows.slice(1)
|
||||
if (!dataRows.length) return { rows, cols }
|
||||
|
||||
const keepIndexes = rows[0]
|
||||
.map((header, index) => ({ header, index }))
|
||||
.filter(({ index }) => {
|
||||
return dataRows.some(row => {
|
||||
const value = row[index]
|
||||
return value !== undefined && value !== null && String(value).trim() !== ''
|
||||
})
|
||||
})
|
||||
.map(item => item.index)
|
||||
|
||||
return {
|
||||
rows: rows.map(row => keepIndexes.map(index => row[index])),
|
||||
cols: keepIndexes.map(index => cols[index]).filter(width => width !== undefined)
|
||||
}
|
||||
}
|
||||
|
||||
export function buildWorkbookSheets(data) {
|
||||
const mesSheet = removeEmptyColumns(
|
||||
buildMesRows(data),
|
||||
[18, 28, 16, 16, 12, 12, 9, 12, 12, 10, 10, 8, 8, 12, 24, 10, 14, 58, 8, 8, 10, 8, 10, 20, 10, 14, 12, 12, 12, 10, 12, 10, 10, 10, 10, 12, 12, 12]
|
||||
)
|
||||
|
||||
return [
|
||||
{
|
||||
name: 'MES_基本变量原始数据',
|
||||
rows: mesSheet.rows,
|
||||
cols: mesSheet.cols
|
||||
},
|
||||
{
|
||||
name: '设备类型对应表',
|
||||
rows: buildDeviceRows(data),
|
||||
cols: [18, 14, 12, 12, 12, 18]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export function buildSingleDeviceExportData(data, selectedDeviceId) {
|
||||
const normalized = normalizeData(data)
|
||||
const selectedDevice = normalized.devices.find(device => device.id === selectedDeviceId)
|
||||
return selectedDevice
|
||||
? {
|
||||
devices: [cloneData(selectedDevice)]
|
||||
}
|
||||
: {
|
||||
devices: []
|
||||
}
|
||||
}
|
||||
|
||||
function validateDeviceModule(device, errors) {
|
||||
const label = device.deviceName || '当前设备模块'
|
||||
const stationSet = new Set()
|
||||
const dbSet = new Set()
|
||||
|
||||
if (!String(device.deviceName || '').trim()) errors.push(`${label} 的设备名称不能为空。`)
|
||||
if (!String(device.deviceModel || '').trim()) errors.push(`${label} 的设备型号不能为空。`)
|
||||
if (!String(device.deviceIp || '').trim()) errors.push(`${label} 的设备IP不能为空。`)
|
||||
if (!device.stations.length) errors.push(`${label} 至少需要一个工位。`)
|
||||
device.stations.forEach((station, stationIndex) => {
|
||||
const stationLabel = station.stationName || `${label} 的第 ${stationIndex + 1} 个工位`
|
||||
if (!String(station.deviceName || '').trim()) errors.push(`${stationLabel} 的设备名称不能为空。`)
|
||||
if (!String(station.stationName || '').trim()) errors.push(`${stationLabel} 的工位名称不能为空。`)
|
||||
if (!String(station.deviceModel || '').trim()) errors.push(`${stationLabel} 的设备型号不能为空。`)
|
||||
if (!String(station.deviceIp || '').trim()) errors.push(`${stationLabel} 的设备IP不能为空。`)
|
||||
if (!String(station.dataSource || '').trim()) errors.push(`${stationLabel} 的数据来源不能为空。`)
|
||||
if (!String(station.db || '').trim()) errors.push(`${stationLabel} 的DB不能为空。`)
|
||||
|
||||
const stationKey = String(station.stationName || '').trim().toLowerCase()
|
||||
if (stationKey && stationSet.has(stationKey)) errors.push(`${label} 下工位名称重复:${station.stationName}。`)
|
||||
if (stationKey) stationSet.add(stationKey)
|
||||
|
||||
const dbKey = String(station.db || '').trim().toLowerCase()
|
||||
if (dbKey && dbSet.has(dbKey)) errors.push(`${label} 下DB重复:${station.db}。`)
|
||||
if (dbKey) dbSet.add(dbKey)
|
||||
})
|
||||
|
||||
device.variables.forEach((variable, index) => {
|
||||
const variableLabel = `${label} 的第 ${index + 1} 条变量`
|
||||
if (!String(variable.TagID || '').trim()) errors.push(`${variableLabel} 缺少 TagID。`)
|
||||
if (!String(variable.TagName || '').trim()) errors.push(`${variableLabel} 缺少 TagName。`)
|
||||
})
|
||||
}
|
||||
|
||||
export function validateSingleDeviceExportData(data, selectedDeviceId) {
|
||||
const exportData = buildSingleDeviceExportData(data, selectedDeviceId)
|
||||
const device = exportData.devices[0]
|
||||
const errors = []
|
||||
|
||||
if (!device) {
|
||||
errors.push('请先选择要导出的设备模块。')
|
||||
return {
|
||||
valid: false,
|
||||
errors,
|
||||
warnings: []
|
||||
}
|
||||
}
|
||||
|
||||
validateDeviceModule(device, errors)
|
||||
|
||||
return {
|
||||
valid: errors.length === 0,
|
||||
errors,
|
||||
warnings: ['本次只导出当前选中设备模块及其全部工位。']
|
||||
}
|
||||
}
|
||||
|
||||
export function validateSignalTableData(data) {
|
||||
const normalized = normalizeData(data)
|
||||
const errors = []
|
||||
|
||||
if (!normalized.devices.length) errors.push('至少需要一个设备模块。')
|
||||
normalized.devices.forEach(device => validateDeviceModule(device, errors))
|
||||
|
||||
return {
|
||||
valid: errors.length === 0,
|
||||
errors,
|
||||
warnings: ['请确认 App.config 中 Station1OpName / Station2OpName 与启用工位名称一致。']
|
||||
}
|
||||
}
|
||||
102
src/utils/templateBuilder.js
Normal file
102
src/utils/templateBuilder.js
Normal file
@@ -0,0 +1,102 @@
|
||||
import { cloneData, MES_HEADERS } from './signalTableBuilder'
|
||||
|
||||
const SOURCE_FIELDS = new Set(['工位号', '数据来源', 'IP', 'DbName'])
|
||||
const NON_TEMPLATE_FIELDS = new Set(['TagID', 'TagName', ...SOURCE_FIELDS])
|
||||
|
||||
export function normalizeVariableTemplates(templates, fallbackTemplates = []) {
|
||||
const source = Array.isArray(templates) && templates.length ? templates : fallbackTemplates
|
||||
|
||||
return (source || []).map((template, index) => {
|
||||
const fields = {}
|
||||
const rawFields = template && template.fields && typeof template.fields === 'object' ? template.fields : {}
|
||||
|
||||
MES_HEADERS.forEach(header => {
|
||||
if (Object.prototype.hasOwnProperty.call(rawFields, header)) {
|
||||
fields[header] = rawFields[header]
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
id: template.id || `tpl-${Date.now()}-${index}`,
|
||||
templateName: template.templateName || `变量模板${index + 1}`,
|
||||
description: template.description || '',
|
||||
fields
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function createEmptyTemplate() {
|
||||
return {
|
||||
id: '',
|
||||
templateName: '',
|
||||
description: '',
|
||||
fields: {}
|
||||
}
|
||||
}
|
||||
|
||||
export function createTemplateFromVariable(variable, templateName, description = '') {
|
||||
const fields = {}
|
||||
|
||||
MES_HEADERS.forEach(header => {
|
||||
if (NON_TEMPLATE_FIELDS.has(header)) return
|
||||
const value = variable ? variable[header] : ''
|
||||
if (value !== undefined && value !== null && String(value).trim() !== '') {
|
||||
fields[header] = value
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
id: `tpl-${Date.now()}`,
|
||||
templateName,
|
||||
description,
|
||||
fields
|
||||
}
|
||||
}
|
||||
|
||||
export function applyTemplateToVariableForm(form, template, options = {}) {
|
||||
const next = cloneData(form)
|
||||
const overwrite = Boolean(options.overwrite)
|
||||
const fields = template && template.fields ? template.fields : {}
|
||||
|
||||
Object.keys(fields).forEach(field => {
|
||||
if (!MES_HEADERS.includes(field)) return
|
||||
if (!overwrite) {
|
||||
const value = next[field]
|
||||
if (value !== undefined && value !== null && String(value).trim() !== '') return
|
||||
}
|
||||
next[field] = fields[field]
|
||||
})
|
||||
|
||||
return next
|
||||
}
|
||||
|
||||
export function templateHasFields(template) {
|
||||
return Boolean(template && template.fields && Object.keys(template.fields).length)
|
||||
}
|
||||
|
||||
export function templateNameExists(templates, name, ignoreId = '') {
|
||||
const target = String(name || '').trim().toLowerCase()
|
||||
return (templates || []).some(template => {
|
||||
return template.id !== ignoreId && String(template.templateName || '').trim().toLowerCase() === target
|
||||
})
|
||||
}
|
||||
|
||||
export function sanitizeTemplateForSave(template) {
|
||||
const next = createEmptyTemplate()
|
||||
next.id = template.id || `tpl-${Date.now()}`
|
||||
next.templateName = String(template.templateName || '').trim()
|
||||
next.description = String(template.description || '').trim()
|
||||
|
||||
const rawFields = template.fields || {}
|
||||
MES_HEADERS.forEach(header => {
|
||||
if (Object.prototype.hasOwnProperty.call(rawFields, header)) {
|
||||
const value = rawFields[header]
|
||||
if (value !== undefined && value !== null && String(value).trim() !== '') {
|
||||
next.fields[header] = value
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return next
|
||||
}
|
||||
|
||||
1380
src/views/SignalTableGenerator.vue
Normal file
1380
src/views/SignalTableGenerator.vue
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user