From f0cf0d494bf195b593aa6d3c60fb6a988ed2eb52 Mon Sep 17 00:00:00 2001
From: yeg <1347747024@qq.com>
Date: 星期三, 09 九月 2026 11:06:11 +0800
Subject: [PATCH] 将工步列表两项电压统一为文件中值

---
 tests/test-console-data-import.test.mjs              |   14 ++++++-
 src/api/tests.ts                                     |    4 +-
 src/utils/consoleDataImport.ts                       |    5 +-
 src/views/DataRevisionDetail.vue                     |    2 
 tests/test-ticket23-console-step-regression.test.mjs |    2 
 src/components/ConsoleDataListPanel.vue              |   28 +++++++-------
 tests/test-console-data-import-browser.mjs           |   21 ++++++++++
 7 files changed, 53 insertions(+), 23 deletions(-)

diff --git a/src/api/tests.ts b/src/api/tests.ts
index 7c36894..fcb8daa 100644
--- a/src/api/tests.ts
+++ b/src/api/tests.ts
@@ -382,8 +382,8 @@
   stepDuration: number | string | null
   startVoltage: number | string | null
   endVoltage: number | string | null
-  chargePeakVoltage: number | string | null
-  dischargeValleyVoltage: number | string | null
+  chargeMedianVoltage: number | string | null
+  dischargeMedianVoltage: number | string | null
 }
 
 export interface PersistedTestCycle extends PersistedTestRecord {
diff --git a/src/components/ConsoleDataListPanel.vue b/src/components/ConsoleDataListPanel.vue
index d32600e..461e9d8 100644
--- a/src/components/ConsoleDataListPanel.vue
+++ b/src/components/ConsoleDataListPanel.vue
@@ -40,8 +40,8 @@
   stepDuration: string
   startVoltage: string
   endVoltage: string
-  chargePeakVoltage: string
-  dischargeValleyVoltage: string
+  chargeMedianVoltage: string
+  dischargeMedianVoltage: string
   voltage: string
   current: string
   capacity: string
@@ -102,8 +102,8 @@
           { label: '能量(mWh)', value: 'energy' },
           { label: '起始电压(V)', value: 'startVoltage' },
           { label: '终止电压(V)', value: 'endVoltage' },
-          { label: '充电峰值电压(V)', value: 'chargePeakVoltage' },
-          { label: '放电谷值电压(V)', value: 'dischargeValleyVoltage' },
+          { label: '充电中值电压(V)', value: 'chargeMedianVoltage' },
+          { label: '放电中值电压(V)', value: 'dischargeMedianVoltage' },
         ]
       : [
           { label: '充电容量(mAh)', value: 'chargeCapacity' },
@@ -114,7 +114,7 @@
         ],
 )
 function emptyRow(base: Partial<DataRow>): DataRow {
-  return { sourceIdentity: '-', collectorDeviceTime: '-', id: '', sourceType: 'CABINET', metricCode: '-', metricValue: '-', unit: '-', collectorCode: '-', channelCode: '-', recordNo: '', batteryNo: '', cycleNo: 0, stepNo: 0, stepName: '', actualStepLabel: '-', sequenceId: 0, recordedAt: '', absoluteTime: '', serverReceivedAt: '', stepDuration: '-', startVoltage: '-', endVoltage: '-', chargePeakVoltage: '-', dischargeValleyVoltage: '-', voltage: '-', current: '-', capacity: '-', energy: '-', temperature: '-', positivePotential: '-', negativePotential: '-', extensionMeasurements: '-', chargeCapacity: '-', dischargeCapacity: '-', efficiency: '-', chargeEnergy: '-', dischargeEnergy: '-', ...base }
+  return { sourceIdentity: '-', collectorDeviceTime: '-', id: '', sourceType: 'CABINET', metricCode: '-', metricValue: '-', unit: '-', collectorCode: '-', channelCode: '-', recordNo: '', batteryNo: '', cycleNo: 0, stepNo: 0, stepName: '', actualStepLabel: '-', sequenceId: 0, recordedAt: '', absoluteTime: '', serverReceivedAt: '', stepDuration: '-', startVoltage: '-', endVoltage: '-', chargeMedianVoltage: '-', dischargeMedianVoltage: '-', voltage: '-', current: '-', capacity: '-', energy: '-', temperature: '-', positivePotential: '-', negativePotential: '-', extensionMeasurements: '-', chargeCapacity: '-', dischargeCapacity: '-', efficiency: '-', chargeEnergy: '-', dischargeEnergy: '-', ...base }
 }
 function mapRecord(record: PersistedTestRecord): DataRow {
   const deviceTime = formatTime(record.deviceTime ?? record.recordedAt)
@@ -136,7 +136,7 @@
 function mapStep(record: PersistedTestStep): DataRow {
   const startedAt = formatTime(record.recordedAt)
   const identity = record.stableIdentity || `step:${[record.adapterId, record.devtype, record.deviceId, record.subdevid, record.channelId, record.cycleNo, record.stepNo].join(':')}`
-  return emptyRow({ id: identity, recordNo: record.recordNo, batteryNo: record.batteryNo, cycleNo: Number(record.cycleNo), stepNo: Number(record.stepNo), stepName: record.stepName || '-', actualStepLabel: record.actualStepLabel || `${record.stepNo} · ${record.stepName || '-'}`, sequenceId: Number(record.sequenceId || 0), recordedAt: startedAt, absoluteTime: formatTime(record.absoluteTime || record.recordedAt), serverReceivedAt: formatTime(record.serverReceivedAt), stepDuration: record.stepDuration == null ? '-' : String(record.stepDuration), capacity: formatTestDisplayValue('capacity', record.capacity), energy: formatTestDisplayValue('energy', record.energy), startVoltage: record.startVoltage == null ? '-' : Number(record.startVoltage).toFixed(4), endVoltage: record.endVoltage == null ? '-' : Number(record.endVoltage).toFixed(4), chargePeakVoltage: record.chargePeakVoltage == null ? '-' : Number(record.chargePeakVoltage).toFixed(4), dischargeValleyVoltage: record.dischargeValleyVoltage == null ? '-' : Number(record.dischargeValleyVoltage).toFixed(4) })
+  return emptyRow({ id: identity, recordNo: record.recordNo, batteryNo: record.batteryNo, cycleNo: Number(record.cycleNo), stepNo: Number(record.stepNo), stepName: record.stepName || '-', actualStepLabel: record.actualStepLabel || `${record.stepNo} · ${record.stepName || '-'}`, sequenceId: Number(record.sequenceId || 0), recordedAt: startedAt, absoluteTime: formatTime(record.absoluteTime || record.recordedAt), serverReceivedAt: formatTime(record.serverReceivedAt), stepDuration: record.stepDuration == null ? '-' : String(record.stepDuration), capacity: formatTestDisplayValue('capacity', record.capacity), energy: formatTestDisplayValue('energy', record.energy), startVoltage: record.startVoltage == null ? '-' : Number(record.startVoltage).toFixed(4), endVoltage: record.endVoltage == null ? '-' : Number(record.endVoltage).toFixed(4), chargeMedianVoltage: record.chargeMedianVoltage == null ? '-' : Number(record.chargeMedianVoltage).toFixed(4), dischargeMedianVoltage: record.dischargeMedianVoltage == null ? '-' : Number(record.dischargeMedianVoltage).toFixed(4) })
 }
 function mapCycle(record: PersistedTestCycle): DataRow {
   const startedAt = formatTime(record.recordedAt)
@@ -344,8 +344,8 @@
     ElMessage.warning('没有可下载的数据')
     return
   }
-  const headers = activeTab.value === 'cycle' ? ['记录编号', '电池', '循环号', '充电容量(mAh)', '放电容量(mAh)', '充放电效率(%)', '充电能量(mWh)', '放电能量(mWh)'] : activeTab.value === 'step' ? ['记录编号', '电池', '循环号', '工步序号', '工步', '工步时间', '容量(mAh)', '能量(mWh)', '起始电压(V)', '终止电压(V)', '充电峰值(V)', '放电谷值(V)'] : ['记录编号', '电池', '循环号', '实际工步号', '实际工步名称', '记录序号', '设备时间', '服务器接收时间', '电压(V)', '电流(mA)', '容量(mAh)', '能量(mWh)', '温度(℃)', '正电位(V)', '负电位(V)']
-  const records = activeTab.value === 'cycle' ? filteredRows.value.map(row => [row.recordNo, row.batteryNo, row.cycleNo, row.chargeCapacity, row.dischargeCapacity, row.efficiency, row.chargeEnergy, row.dischargeEnergy]) : activeTab.value === 'step' ? filteredRows.value.map(row => [row.recordNo, row.batteryNo, row.cycleNo, row.stepNo, row.stepName, row.stepDuration, row.capacity, row.energy, row.startVoltage, row.endVoltage, row.chargePeakVoltage, row.dischargeValleyVoltage]) : filteredRows.value.map(row => [row.recordNo, row.batteryNo, row.cycleNo, row.stepNo, row.stepName, row.sequenceId, row.recordedAt, row.serverReceivedAt, row.voltage, row.current, row.capacity, row.energy, row.temperature, row.positivePotential, row.negativePotential])
+  const headers = activeTab.value === 'cycle' ? ['记录编号', '电池', '循环号', '充电容量(mAh)', '放电容量(mAh)', '充放电效率(%)', '充电能量(mWh)', '放电能量(mWh)'] : activeTab.value === 'step' ? ['记录编号', '电池', '循环号', '工步序号', '工步', '工步时间', '容量(mAh)', '能量(mWh)', '起始电压(V)', '终止电压(V)', '充电中值电压(V)', '放电中值电压(V)'] : ['记录编号', '电池', '循环号', '实际工步号', '实际工步名称', '记录序号', '设备时间', '服务器接收时间', '电压(V)', '电流(mA)', '容量(mAh)', '能量(mWh)', '温度(℃)', '正电位(V)', '负电位(V)']
+  const records = activeTab.value === 'cycle' ? filteredRows.value.map(row => [row.recordNo, row.batteryNo, row.cycleNo, row.chargeCapacity, row.dischargeCapacity, row.efficiency, row.chargeEnergy, row.dischargeEnergy]) : activeTab.value === 'step' ? filteredRows.value.map(row => [row.recordNo, row.batteryNo, row.cycleNo, row.stepNo, row.stepName, row.stepDuration, row.capacity, row.energy, row.startVoltage, row.endVoltage, row.chargeMedianVoltage, row.dischargeMedianVoltage]) : filteredRows.value.map(row => [row.recordNo, row.batteryNo, row.cycleNo, row.stepNo, row.stepName, row.sequenceId, row.recordedAt, row.serverReceivedAt, row.voltage, row.current, row.capacity, row.energy, row.temperature, row.positivePotential, row.negativePotential])
   const blob = new Blob([`\uFEFF${[headers, ...records].map(item => item.map(escapeCsv).join(',')).join('\n')}`], { type: 'text/csv;charset=utf-8' })
   const url = URL.createObjectURL(blob)
   const anchor = document.createElement('a')
@@ -457,8 +457,8 @@
             <th>能量(mWh)</th>
             <th>起始电压(V)</th>
             <th>终止电压(V)</th>
-            <th>充电峰值(V)</th>
-            <th>放电谷值(V)</th>
+            <th>充电中值电压(V)</th>
+            <th>放电中值电压(V)</th>
             <th class="action-column">操作</th>
           </tr>
           <tr v-else>
@@ -549,12 +549,12 @@
                 <span v-else>{{ row.endVoltage }}</span>
               </td>
               <td>
-                <el-input v-if="editing && draftRows[row.id]" v-model="draftRows[row.id].chargePeakVoltage" size="small" />
-                <span v-else>{{ row.chargePeakVoltage }}</span>
+                <el-input v-if="editing && draftRows[row.id]" v-model="draftRows[row.id].chargeMedianVoltage" size="small" />
+                <span v-else>{{ row.chargeMedianVoltage }}</span>
               </td>
               <td>
-                <el-input v-if="editing && draftRows[row.id]" v-model="draftRows[row.id].dischargeValleyVoltage" size="small" />
-                <span v-else>{{ row.dischargeValleyVoltage }}</span>
+                <el-input v-if="editing && draftRows[row.id]" v-model="draftRows[row.id].dischargeMedianVoltage" size="small" />
+                <span v-else>{{ row.dischargeMedianVoltage }}</span>
               </td>
               <td class="action-column">
                 <div class="row-actions"><button type="button" @click="openRowEditor(row)">编辑</button><button class="delete-action" type="button" @click="deleteSelected([row.id])">删除</button></div>
diff --git a/src/utils/consoleDataImport.ts b/src/utils/consoleDataImport.ts
index e99ed38..dc77e33 100644
--- a/src/utils/consoleDataImport.ts
+++ b/src/utils/consoleDataImport.ts
@@ -3,7 +3,7 @@
 
 export const consoleImportColumns = {
   REALTIME: { recordNo: '记录编号', batteryNo: '电池号', recordedAt: '设备时间', cycleNo: '循环序号', stepNo: '工步序号', stepName: '工步名称', sequenceId: '记录序号', voltage: '电压(V)', current: '电流(mA)', capacity: '容量(mAh)', energy: '能量(mWh)', temperature: '温度(℃)', positivePotential: '正电位(V)', negativePotential: '负电位(V)' },
-  STEP: { recordNo: '记录编号', batteryNo: '电池号', recordedAt: '设备时间', cycleNo: '循环序号', stepNo: '工步序号', stepName: '工步', stepDuration: '工步时间', capacity: '容量(mAh)', energy: '能量(mWh)', startVoltage: '起始电压(V)', endVoltage: '终止电压(V)', chargePeakVoltage: '充电峰值电压(V)', dischargeValleyVoltage: '放电谷值电压(V)' },
+  STEP: { recordNo: '记录编号', batteryNo: '电池号', recordedAt: '设备时间', cycleNo: '循环序号', stepNo: '工步序号', stepName: '工步', stepDuration: '工步时间', capacity: '容量(mAh)', energy: '能量(mWh)', startVoltage: '起始电压(V)', endVoltage: '终止电压(V)', chargeMedianVoltage: '充电中值电压(V)', dischargeMedianVoltage: '放电中值电压(V)' },
   CYCLE: { recordNo: '记录编号', batteryNo: '电池号', recordedAt: '设备时间', cycleNo: '循环序号', chargeCapacity: '充电容量(mAh)', dischargeCapacity: '放电容量(mAh)', efficiency: '充放电效率(%)', chargeEnergy: '充电能量(mWh)', dischargeEnergy: '放电能量(mWh)' },
 } satisfies Record<ConsoleDataType, Record<string, string>>
 
@@ -100,7 +100,8 @@
   }
   const columns: Record<string, string> = consoleImportColumns[type]
   const missing = Object.entries(columns).filter(([field, header]) => {
-    if (party && ['recordNo', 'batteryNo', 'temperature', 'positivePotential', 'negativePotential', 'chargePeakVoltage', 'dischargeValleyVoltage'].includes(field)) return false
+    // 中值只读取对应列;缺列可为空,不借用旧峰值或谷值。
+    if (party && ['recordNo', 'batteryNo', 'temperature', 'positivePotential', 'negativePotential', 'chargeMedianVoltage', 'dischargeMedianVoltage'].includes(field)) return false
     if (party && type !== 'REALTIME' && field === 'recordedAt') return false
     return !headers.includes(headerFor(field, header))
   }).map(([field, header]) => headerFor(field, header))
diff --git a/src/views/DataRevisionDetail.vue b/src/views/DataRevisionDetail.vue
index e3d1b3b..941ab95 100644
--- a/src/views/DataRevisionDetail.vue
+++ b/src/views/DataRevisionDetail.vue
@@ -6,7 +6,7 @@
 const route = useRoute(); const router = useRouter(); const record = ref<DataRevisionAction | null>(null); const loading = ref(true); const loadError = ref('')
 const dataTypeLabels = revisionDataTypeLabels; const operationLabels = revisionOperationLabels
 // 后端修订摘要保留 A/Ah/Wh,仅展示时换算,不改写已保存的修订事实。
-const fieldLabels: Record<string, string> = { voltage: '电压(V)', current: '电流(mA)', capacity: '容量(mAh)', energy: '能量(mWh)', stepDuration: '工步时间(s)', startVoltage: '起始电压(V)', endVoltage: '终止电压(V)', chargePeakVoltage: '充电峰值电压(V)', dischargeValleyVoltage: '放电谷值电压(V)', chargeCapacity: '充电容量(mAh)', dischargeCapacity: '放电容量(mAh)', efficiency: '效率(%)', chargeEnergy: '充电能量(mWh)', dischargeEnergy: '放电能量(mWh)' }
+const fieldLabels: Record<string, string> = { voltage: '电压(V)', current: '电流(mA)', capacity: '容量(mAh)', energy: '能量(mWh)', stepDuration: '工步时间(s)', startVoltage: '起始电压(V)', endVoltage: '终止电压(V)', chargeMedianVoltage: '充电中值电压(V)', dischargeMedianVoltage: '放电中值电压(V)', chargePeakVoltage: '充电峰值电压(V)', dischargeValleyVoltage: '放电谷值电压(V)', chargeCapacity: '充电容量(mAh)', dischargeCapacity: '放电容量(mAh)', efficiency: '效率(%)', chargeEnergy: '充电能量(mWh)', dischargeEnergy: '放电能量(mWh)' }
 function parseArray(value: string | null) { if (!value) return [] as Array<Record<string, unknown> | string>; try { const parsed = JSON.parse(value); return Array.isArray(parsed) ? parsed : [] } catch { return [] as Array<Record<string, unknown> | string> } }
 const identities = computed(() => parseArray(record.value?.identitySummary || null).map(item => String(item))); const changeRows = computed(() => parseArray(record.value?.changesSummary || null).map((item, index) => { if (!item || typeof item !== 'object') return { identity: identities.value[index] || '-', changes: [] as Array<{ field: string; before: unknown; after: unknown }> }; const row = item as Record<string, unknown>; const before = row.before && typeof row.before === 'object' ? row.before as Record<string, unknown> : {}; const after = row.after && typeof row.after === 'object' ? row.after as Record<string, unknown> : row; const changes = Object.keys(after).filter(key => before[key] !== after[key] && fieldLabels[key]).map(key => ({ field: fieldLabels[key], before: formatTestDisplayValue(key, before[key]), after: formatTestDisplayValue(key, after[key]) })); return { identity: String(row.identity || row.recordNo || identities.value[index] || '-'), changes } }))
 const formatTime = formatRevisionTime; const formatScope = formatRevisionScope; function goBack() { router.push('/data-revision') }; function returnToTest() { if (record.value) router.push({ path: `/test-list/${record.value.testId}/console`, query: { tab: 'data-list', dataType: record.value.dataType, ...(record.value.testReceptionStatus === 'ENDED' ? { state: 'ended' } : {}) } }) }
diff --git a/tests/test-console-data-import-browser.mjs b/tests/test-console-data-import-browser.mjs
index 5301ecd..cec62f7 100644
--- a/tests/test-console-data-import-browser.mjs
+++ b/tests/test-console-data-import-browser.mjs
@@ -18,7 +18,7 @@
 
 function standardFile(type, invalid = false) {
   const fields = consoleImportColumns[type]
-  const row = { recordNo: '17', batteryNo: 'BAT-1', recordedAt: '2023-05-08 11:42:11', cycleNo: 1, stepNo: 1, stepName: '恒流放电', sequenceId: 17, capacity: 8.5122, temperature: 25, positivePotential: 1.5, negativePotential: -0.1 }
+  const row = { recordNo: '17', batteryNo: 'BAT-1', recordedAt: '2023-05-08 11:42:11', cycleNo: 1, stepNo: 1, stepName: '恒流放电', sequenceId: 17, capacity: 8.5122, temperature: 25, positivePotential: 1.5, negativePotential: -0.1, chargeMedianVoltage: 0, dischargeMedianVoltage: 1.1709 }
   const rows = [Object.values(fields), Object.keys(fields).map(field => row[field] ?? '')]
   if (invalid) {
     while (rows.length < 19) rows.push([])
@@ -81,6 +81,12 @@
           source[XLSX.utils.encode_cell({ r: row + 1, c: column + 10 })]?.v ?? null))
       }
     }
+  }
+  if (process.env.PARTY_EXCEL) {
+    const imported = JSON.parse(calls.find(call => call.dataType === 'STEP').changesSummary)
+    const source = XLSX.utils.sheet_to_json(XLSX.readFile(process.env.PARTY_EXCEL).Sheets.step)
+    assert.deepEqual(imported.map(row => [row.chargeMedianVoltage, row.dischargeMedianVoltage]),
+      source.map(row => [row['充电中值电压(V)'], row['放电中值电压(V)']]))
   }
   await page.goto(`${origin}/test-list/1/console/data/import?dataType=REALTIME`)
   await page.locator('input[type=file]').setInputFiles(standardFile('REALTIME', true))
@@ -163,6 +169,19 @@
     await expect(page.locator('.table-wrap tbody')).toContainText('BAT-1')
     await page.reload()
     await expect(page.locator('.table-wrap tbody')).toContainText('BAT-1')
+    if (type === 'STEP') {
+      await expect(page.locator('.table-wrap thead')).toContainText('充电中值电压(V)')
+      await expect(page.locator('.table-wrap thead')).toContainText('放电中值电压(V)')
+      await expect(page.locator('.table-wrap tbody')).toContainText('0.0000')
+      await expect(page.locator('.table-wrap tbody')).toContainText('1.1709')
+      const csvEvent = page.waitForEvent('download')
+      await page.getByRole('button', { name: '下载', exact: true }).click()
+      const csv = await readFile(await (await csvEvent).path(), 'utf8')
+      assert.match(csv, /充电中值电压\(V\)/)
+      assert.match(csv, /放电中值电压\(V\)/)
+      assert.match(csv, /"0\.0000","1\.1709"/)
+      assert.doesNotMatch(csv, /峰值|谷值/)
+    }
   }
   assert.deepEqual(errors, [])
   console.log('Ticket03/04 浏览器:独立模板、一键模板、三 Sheet 预览、错误下载和阻断、单次提交原因与批次、子页刷新及窄屏通过;导入未请求实时监控')
diff --git a/tests/test-console-data-import.test.mjs b/tests/test-console-data-import.test.mjs
index 3316b70..778e5f5 100644
--- a/tests/test-console-data-import.test.mjs
+++ b/tests/test-console-data-import.test.mjs
@@ -106,8 +106,18 @@
   assert.equal(step.data.recordNo, 'step:1:1')
   assert.equal(step.data.recordedAt, 1683521083000)
   assert.equal(step.data.stepDuration, 3752)
-  assert.equal(step.data.chargePeakVoltage, null)
-  assert.equal(step.data.dischargeValleyVoltage, null)
+  assert.equal(step.data.chargeMedianVoltage, 0)
+  assert.equal(step.data.dischargeMedianVoltage, 1.1709)
+  // 中值空格和缺列不变成零,也不回退读取同一文件的峰谷值。
+  XLSX.utils.sheet_add_aoa(workbook.Sheets.step, [['', '', '充电峰值电压(V)', '放电谷值电压(V)'], ['', '', 1.8, 0.8]], { origin: 'I1' })
+  const absent = parseConsoleImport(workbook, 'STEP', ['BAT-1'])[0]
+  assert.deepEqual(absent.errors, [])
+  assert.equal(absent.data.chargeMedianVoltage, null)
+  assert.equal(absent.data.dischargeMedianVoltage, null)
+  XLSX.utils.sheet_add_aoa(workbook.Sheets.step, [['充电中值电压(V)', '放电中值电压(V)'], ['', '不是数字']], { origin: 'I1' })
+  const invalid = parseConsoleImport(workbook, 'STEP', ['BAT-1'])[0]
+  assert.equal(invalid.data.chargeMedianVoltage, null)
+  assert.match(invalid.errors.join(), /放电中值电压.*必须是数字或空值/)
   const cycle = parseConsoleImport(workbook, 'CYCLE', ['BAT-1'])[0]
   assert.deepEqual(cycle.errors, [])
   assert.equal(cycle.data.recordNo, 'cycle:1')
diff --git a/tests/test-ticket23-console-step-regression.test.mjs b/tests/test-ticket23-console-step-regression.test.mjs
index 46d0965..037b7f7 100644
--- a/tests/test-ticket23-console-step-regression.test.mjs
+++ b/tests/test-ticket23-console-step-regression.test.mjs
@@ -23,7 +23,7 @@
 assert.match(view, /const physical = physicalSteps\.find\(item => item\.stepNo === row\.stepNo && item\.stepName === row\.stepName\) \|\| physicalSteps\.find\(item => item\.stepNo === row\.stepNo\)/)
 assert.match(view, /displayRows\.push\(\{ no: mapping\?\.logicalNo \|\| row\.stepNo/)
 assert.match(dataList, /起始电压\(V\)/)
-assert.match(dataList, /放电谷值\(V\)/)
+assert.match(dataList, /放电中值电压\(V\)/)
 assert.match(dataList, /<th>工步<\/th>/)
 assert.doesNotMatch(dataList, /<th>逻辑工步<\/th>/)
 assert.doesNotMatch(dataList, /<th>实际工步<\/th>/)

--
Gitblit v1.7.1