Răsfoiți Sursa

fix:调整故障录波参数设置通道参数设置显示方式,同时并排展示

Jerry Liang 2 săptămâni în urmă
părinte
comite
0ac8d537b7
2 a modificat fișierele cu 110 adăugiri și 54 ștergeri
  1. 1 1
      docs/fault-record/ui-design.md
  2. 109 53
      src/views/fault-record/param-write/index.vue

+ 1 - 1
docs/fault-record/ui-design.md

@@ -152,7 +152,7 @@
 ```
 
 - **读取当前值**改用 `cmd=0`:5 取 code=2 的采样频率,6 取 code=1 的可配采样通道;可勾选读取哪些项,默认全选;切换设备时清空。
-- **可配采样通道**:索引 1 对应 code=1 可配采样通道列表的第 1 项。默认方式和代码方式按同一格式解析(类型低字节的高 4 位为设置方式、低 4 位为地址类型,数据 4 字节;代码方式取前 2 字节为代码),后续出问题再改。
+- **可配采样通道**:读取后设备有几个通道就并列显示几行编辑框(每行:索引、方式、地址类型、地址/代码、setVal 预览、设备当前值),修改过的行高亮并标“已修改”,索引重复的行标红;可逐行“加入”,也可“全部加入待提交”或“加入已修改”一键导入多行,无效行跳过并提示;可“新增一行”或删除行。索引 1 对应 code=1 可配采样通道列表的第 1 项。默认方式和代码方式按同一格式解析(类型低字节的高 4 位为设置方式、低 4 位为地址类型,数据 4 字节;代码方式取前 2 字节为代码),后续出问题再改。
 - **数据类型选项**统一首字母大写:Empty、Uint16、Int16、Float32、Uint32_HWord、Uint32_LWord、Uint32。
 - **提交**前弹窗确认;生产服的确认按钮用警示色。
 - **复读核对**同样用 `cmd=0`:5 比对采样频率;6 按索引取列表对应项,拼成 setVal 后比对。

+ 109 - 53
src/views/fault-record/param-write/index.vue

@@ -30,39 +30,60 @@
             </NInputNumber>
           </ParamSection>
 
-          <!-- 6 可配采样通道 -->
-          <ParamSection :id="6" :preview="cfgHex" :disabled="!cfgValid" @add="add(6, cfgHex)">
+          <!-- 6 可配采样通道:读到几个通道就并列几行,可逐行加入或一键全部加入 -->
+          <div class="flex flex-col gap-2">
             <div class="flex flex-wrap gap-2 items-center">
-              <NInputNumber v-model:value="cfg.index" :min="1" :max="255" :show-button="false" class="w-24">
-                <template #prefix>
-                  索引
-                </template>
-              </NInputNumber>
-              <NSelect v-model:value="cfg.mode" :options="modeOptions" class="w-40" />
-              <NSelect v-if="cfg.mode === 0" v-model:value="cfg.addrType" :options="ADDR_TYPES" class="w-36" />
-              <NInput
-                v-model:value="cfg.value"
-                class="w-40"
-                :placeholder="cfg.mode === 0 ? '地址 8 位 hex' : '代码 4 位 hex'"
-                :status="cfg.value && !cfgValid ? 'error' : undefined"
-              />
+              <span class="font-bold">{{ functionName(6) }} (6)</span>
+              <span class="text-xs text-gray-500">索引 1 对应设备列表第 1 项</span>
+              <div class="flex-1" />
+              <NButton size="small" @click="addCfgRow">新增一行</NButton>
+              <NButton size="small" :disabled="changedRows.length === 0" @click="addRows(changedRows)">
+                加入已修改({{ changedRows.length }})
+              </NButton>
+              <NButton size="small" type="primary" :disabled="validRows.length === 0" @click="addRows(cfgRows)">
+                全部加入待提交({{ validRows.length }})
+              </NButton>
             </div>
-          </ParamSection>
-          <div v-if="deviceCfgs" class="text-xs">
-            <div class="text-gray-500 mb-1">设备当前可配采样通道(点击带入表单):</div>
-            <div class="flex flex-wrap gap-2">
-              <NTag
-                v-for="(c, i) in deviceCfgs"
-                :key="i"
-                size="small"
-                :type="cfg.index === i + 1 ? 'info' : 'default'"
-                class="cursor-pointer"
-                @click="loadCfg(i + 1)"
+            <!-- 每个通道两行:上面一行为输入框(不换行,宽度不足时横向滚动),下面一行为 setVal 预览和设备当前值 -->
+            <div class="overflow-x-auto">
+              <div
+                v-for="(row, i) in cfgRows"
+                :key="row.key"
+                class="mb-1 p-2 rounded min-w-full w-max"
+                :class="rowState(row).changed ? 'bg-blue-500/10' : 'bg-gray-500/5'"
               >
-                索引 {{ i + 1 }}:{{ configDesc(c) }}
-              </NTag>
-              <span v-if="deviceCfgs.length === 0" class="text-gray-500">无</span>
+                <div class="flex flex-nowrap gap-2 items-center">
+                  <NInputNumber v-model:value="row.index" :min="1" :max="255" :show-button="false" class="shrink-0 w-24">
+                    <template #prefix>
+                      索引
+                    </template>
+                  </NInputNumber>
+                  <NSelect v-model:value="row.mode" :options="modeOptions" class="shrink-0 w-40" />
+                  <NSelect v-if="row.mode === 0" v-model:value="row.addrType" :options="ADDR_TYPES" class="shrink-0 w-36" />
+                  <!-- 代码方式没有地址类型,占位保持各行列对齐 -->
+                  <div v-else class="shrink-0 w-36" />
+                  <NInput
+                    v-model:value="row.value"
+                    class="shrink-0"
+                    style="width: 9rem"
+                    :placeholder="row.mode === 0 ? '地址8位(16进制)' : '代码4位(16进制)'"
+                    :status="row.value && !rowState(row).valid ? 'error' : undefined"
+                  />
+                  <div class="shrink-0 w-16">
+                    <NTag v-if="rowState(row).duplicate" size="small" type="error">索引重复</NTag>
+                    <NTag v-else-if="row.original && rowState(row).changed" size="small" type="info">已修改</NTag>
+                  </div>
+                  <div class="flex-1" />
+                  <NButton size="small" class="shrink-0" :disabled="!rowState(row).valid" @click="addRows([row])">加入</NButton>
+                  <NButton size="small" class="shrink-0" quaternary @click="cfgRows.splice(i, 1)">删除</NButton>
+                </div>
+                <div class="text-xs text-gray-500 mt-1 flex flex-wrap gap-x-6">
+                  <span class="font-mono">setVal = {{ rowHex(row) || '-' }}</span>
+                  <span v-if="row.original">设备当前:{{ describeSetVal(6, row.original) }}</span>
+                </div>
+              </div>
             </div>
+            <div v-if="cfgRows.length === 0" class="text-xs text-gray-500">无可配采样通道,可点“新增一行”</div>
           </div>
 
           <!-- 原始模式 -->
@@ -70,7 +91,7 @@
             <NCollapseItem title="高级:原始模式(直接输入十六进制 setVal)" name="raw">
               <div class="flex gap-2 items-center">
                 <NSelect v-model:value="raw.id" :options="codeOptions" class="w-48" />
-                <NInput v-model:value="raw.val" placeholder="setVal 十六进制" class="w-64" :status="raw.val && !isHex(raw.val) ? 'error' : undefined" />
+                <NInput v-model:value="raw.val" placeholder="setVal 十六进制" style="width: 16rem" :status="raw.val && !isHex(raw.val) ? 'error' : undefined" />
                 <NButton :disabled="!raw.val || !isHex(raw.val)" @click="add(raw.id, raw.val.toUpperCase())">加入</NButton>
               </div>
             </NCollapseItem>
@@ -205,17 +226,49 @@ const modeOptions = [
   { value: 0, label: '默认方式(地址)' },
   { value: 1, label: '代码方式' },
 ]
-const cfg = reactive({ index: 1 as number | null, mode: 0, addrType: 1, value: '' })
-const cfgValid = computed(() => {
-  if (!cfg.index) return false
-  return cfg.mode === 0 ? HEX8_RE.test(cfg.value) : HEX4_RE.test(cfg.value)
-})
-const cfgHex = computed(() => {
-  if (!cfgValid.value || !cfg.index) return ''
-  const typeByte = (cfg.mode << 4) | (cfg.mode === 0 ? cfg.addrType : 0)
-  const data = cfg.mode === 0 ? cfg.value : `${cfg.value}0000`
-  return (hex(cfg.index, 1) + hex(typeByte, 1) + data).toUpperCase()
-})
+/** 可配采样通道编辑行;original 为设备当前值(setVal 格式),用于判断是否修改 */
+interface CfgRow {
+  key: number
+  index: number | null
+  mode: number
+  addrType: number
+  value: string
+  original?: string
+}
+let cfgRowKey = 0
+const cfgRows = ref<CfgRow[]>([{ key: ++cfgRowKey, index: 1, mode: 0, addrType: 1, value: '' }])
+
+function rowHex(row: CfgRow) {
+  const valid = row.index && (row.mode === 0 ? HEX8_RE.test(row.value) : HEX4_RE.test(row.value))
+  if (!valid || !row.index) return ''
+  const typeByte = (row.mode << 4) | (row.mode === 0 ? row.addrType : 0)
+  const data = row.mode === 0 ? row.value : `${row.value}0000`
+  return (hex(row.index, 1) + hex(typeByte, 1) + data).toUpperCase()
+}
+
+function rowState(row: CfgRow) {
+  const h = rowHex(row)
+  const duplicate = row.index != null && cfgRows.value.filter((r) => r.index === row.index).length > 1
+  return { valid: h !== '' && !duplicate, duplicate, changed: h !== '' && h !== row.original }
+}
+
+const validRows = computed(() => cfgRows.value.filter((r) => rowState(r).valid))
+const changedRows = computed(() => validRows.value.filter((r) => rowState(r).changed))
+
+function addCfgRow() {
+  const next = Math.max(0, ...cfgRows.value.map((r) => r.index ?? 0)) + 1
+  cfgRows.value.push({ key: ++cfgRowKey, index: Math.min(next, 255), mode: 0, addrType: 1, value: '' })
+}
+
+/** 把多行加入待提交;无效行跳过并提示 */
+function addRows(rows: CfgRow[]) {
+  let skipped = 0
+  for (const r of rows) {
+    if (rowState(r).valid) add(6, rowHex(r))
+    else skipped++
+  }
+  if (skipped > 0) message.warning(`${skipped} 行内容无效或索引重复,未加入`)
+}
 
 const codeOptions = FUNCTION_CODES.filter((f) => f.page === 'param').map((f) => ({ value: f.id, label: `${f.id} ${f.name}` }))
 const raw = reactive({ id: 5, val: '' })
@@ -255,19 +308,18 @@ const readIds = ref<number[]>([5, 6]) // 默认全选
 const deviceFreq = ref<number>()
 const deviceCfgs = ref<FaultRecordConfigurableChannel[]>()
 
-/** 索引 1 对应列表第 1 项 */
-function loadCfg(index: number) {
-  const c = deviceCfgs.value?.[index - 1]
-  cfg.index = index
-  if (!c) return
+/** 设备通道 → 编辑行;索引 1 对应列表第 1 项 */
+function rowFromDevice(index: number, c: FaultRecordConfigurableChannel): CfgRow {
   const { mode, addrType } = cfgParts(c)
-  cfg.mode = mode
-  if (mode === 1) {
-    cfg.value = hex(c.address >>> 16, 2)
-  } else {
-    cfg.addrType = addrType
-    cfg.value = hex(c.address, 4)
+  const row: CfgRow = {
+    key: ++cfgRowKey,
+    index,
+    mode,
+    addrType: mode === 0 ? addrType : 1,
+    value: mode === 1 ? hex(c.address >>> 16, 2) : hex(c.address, 4),
   }
+  row.original = rowHex(row)
+  return row
 }
 
 /** 从 cmd=0 结果中取出采样频率和可配采样通道 */
@@ -289,7 +341,10 @@ function applyCurrent(d: FaultRecordOpDetail) {
   }
   if (cur.cfgs) {
     deviceCfgs.value = cur.cfgs
-    loadCfg(cfg.index ?? 1)
+    // 读到几个通道就并列几行;没有通道时保留一行空白可手动填写
+    cfgRows.value = cur.cfgs.length > 0
+      ? cur.cfgs.map((c, i) => rowFromDevice(i + 1, c))
+      : [{ key: ++cfgRowKey, index: 1, mode: 0, addrType: 1, value: '' }]
   }
 }
 
@@ -327,6 +382,7 @@ watch(
   () => {
     deviceFreq.value = undefined
     deviceCfgs.value = undefined
+    cfgRows.value = [{ key: ++cfgRowKey, index: 1, mode: 0, addrType: 1, value: '' }]
   },
 )