1完成酸轧轧辊调整
2完成双机架工艺规格串联 3完成双机架计划串联 4完成双机架wip快捷录入检索 5完成双机架实绩串联
This commit is contained in:
@@ -65,7 +65,7 @@
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
<span class="search-label">点位名称:</span>
|
||||
<span class="search-label">参数名称:</span>
|
||||
<el-input
|
||||
v-model="filterName"
|
||||
placeholder="请输入"
|
||||
@@ -80,8 +80,13 @@
|
||||
size="mini"
|
||||
type="primary"
|
||||
icon="el-icon-plus"
|
||||
@click="openParamDialog()"
|
||||
>新建参数</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
icon="el-icon-setting"
|
||||
@click="openPlanDialog()"
|
||||
>新建方案点位</el-button>
|
||||
>管理点位</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
type="success"
|
||||
@@ -90,134 +95,42 @@
|
||||
>模板导入</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 方案点位表 -->
|
||||
<!-- 参数平铺表 -->
|
||||
<el-table
|
||||
v-loading="planLoading"
|
||||
:data="filteredPlans"
|
||||
v-loading="planLoading || allParamLoading"
|
||||
:data="filteredFlatRows"
|
||||
size="small"
|
||||
highlight-current-row
|
||||
@current-change="onPlanSelect"
|
||||
border
|
||||
>
|
||||
<el-table-column label="序号" type="index" align="center" />
|
||||
<el-table-column label="父级名称" show-overflow-tooltip>
|
||||
<template slot-scope="{ row }">{{ segLabel(row.segmentType) }} › {{ row.segmentName || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="点位名称" prop="pointName" show-overflow-tooltip />
|
||||
<el-table-column label="点位编码" prop="pointCode" show-overflow-tooltip />
|
||||
<el-table-column label="操作" align="right">
|
||||
<el-table-column label="段" width="68" align="center" fixed>
|
||||
<template slot-scope="{ row }">
|
||||
<el-button type="text" size="mini" @click.stop="openPlanDialog(row)">编辑</el-button>
|
||||
<el-button type="text" size="mini" @click.stop="openParamDialog(row)">参数</el-button>
|
||||
<el-button type="text" size="mini" class="btn-danger" @click.stop="removePlan(row)">删除</el-button>
|
||||
<span :class="['seg-chip', 'seg-' + (row._segmentType || '').toLowerCase()]">{{ row._segmentLabel }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="参数名称" prop="paramName" min-width="130" show-overflow-tooltip />
|
||||
<el-table-column label="编码" prop="paramCode" width="115" show-overflow-tooltip />
|
||||
<el-table-column label="单位" prop="unit" align="center" width="62" />
|
||||
<el-table-column label="设定值" prop="targetValue" align="right" width="82" />
|
||||
<el-table-column label="下限" prop="lowerLimit" align="right" width="72" />
|
||||
<el-table-column label="上限" prop="upperLimit" align="right" width="72" />
|
||||
<el-table-column label="实际状态" align="center" width="90">
|
||||
<template slot-scope="{ row }">
|
||||
<span v-if="paramAnomalyMap[row.paramCode]" class="anomaly-badge">
|
||||
<i class="el-icon-warning-outline" /> 异常
|
||||
</span>
|
||||
<span v-else-if="row.upperLimit != null || row.lowerLimit != null" class="normal-badge">
|
||||
<i class="el-icon-circle-check" /> 正常
|
||||
</span>
|
||||
<span v-else class="no-data-badge">—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="right" width="100" fixed="right">
|
||||
<template slot-scope="{ row }">
|
||||
<el-button type="text" size="mini" @click="editParamFromFlat(row)">编辑</el-button>
|
||||
<el-button type="text" size="mini" class="btn-danger" @click="deleteParamFromFlat(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 方案参数面板 -->
|
||||
<template v-if="selectedPlan">
|
||||
<div class="param-header">
|
||||
<span>{{ selectedPlan.pointName || selectedPlan.pointCode }} — 参数</span>
|
||||
<el-button size="mini" type="primary" icon="el-icon-plus" @click="openParamDialog()">新建参数</el-button>
|
||||
</div>
|
||||
<el-table v-loading="paramLoading" :data="paramList" size="small">
|
||||
<el-table-column label="参数编码" prop="paramCode" width="110" show-overflow-tooltip />
|
||||
<el-table-column label="参数名称" prop="paramName" show-overflow-tooltip />
|
||||
<el-table-column label="实际值ID" prop="actualSrcId" width="140" show-overflow-tooltip>
|
||||
<template slot-scope="{ row }">{{ row.actualSrcId || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="L1设定值ID" prop="presetSrcId" width="140" show-overflow-tooltip>
|
||||
<template slot-scope="{ row }">{{ row.presetSrcId || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="设定值" prop="targetValue" align="right" width="80" />
|
||||
<el-table-column label="最小值" prop="lowerLimit" align="right" width="80" />
|
||||
<el-table-column label="最大值" prop="upperLimit" align="right" width="80" />
|
||||
<el-table-column label="单位" prop="unit" align="center" width="60" />
|
||||
<el-table-column label="更新时间" align="center" width="136">
|
||||
<template slot-scope="{ row }">{{ (row.updateTime || row.createTime || '').substring(0, 16) || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="实际状态" align="center" width="90">
|
||||
<template slot-scope="{ row }">
|
||||
<span v-if="paramAnomalyMap[row.paramCode]" class="anomaly-badge">
|
||||
<i class="el-icon-warning-outline" /> 异常
|
||||
</span>
|
||||
<span v-else-if="row.upperLimit != null || row.lowerLimit != null" class="normal-badge">
|
||||
<i class="el-icon-circle-check" /> 正常
|
||||
</span>
|
||||
<span v-else class="no-data-badge">—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="right">
|
||||
<template slot-scope="{ row }">
|
||||
<el-button type="text" size="mini" @click="openParamDialog(null, row)">编辑</el-button>
|
||||
<el-button type="text" size="mini" class="btn-danger" @click="removeParam(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 偏差分析区块 -->
|
||||
<template v-if="planAnomalies.length">
|
||||
<div class="anomaly-section-header">
|
||||
<i class="el-icon-warning" style="color:#E6A23C;margin-right:4px" />
|
||||
实际生产偏差分析
|
||||
<el-tag type="warning" size="mini" effect="plain" style="margin-left:8px">{{ planAnomalies.length }} 项异常</el-tag>
|
||||
<el-button type="text" size="mini" style="margin-left:auto" @click="anomalyExpanded = !anomalyExpanded">
|
||||
{{ anomalyExpanded ? '收起' : '展开' }}
|
||||
</el-button>
|
||||
</div>
|
||||
<div v-show="anomalyExpanded">
|
||||
<el-table :data="planAnomalies" size="small" border>
|
||||
<el-table-column label="参数" prop="paramName" width="110" show-overflow-tooltip />
|
||||
<el-table-column label="规程设定值" align="right" width="96">
|
||||
<template slot-scope="{ row }">{{ row.storedTarget != null ? row.storedTarget : '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="规程最大值" align="right" width="96">
|
||||
<template slot-scope="{ row }">{{ row.storedUpper != null ? row.storedUpper : '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="规程最小值" align="right" width="96">
|
||||
<template slot-scope="{ row }">{{ row.storedLower != null ? row.storedLower : '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="实际最大值" align="right" width="96">
|
||||
<template slot-scope="{ row }">
|
||||
<span :class="(row.anomalyType === 'OVER_MAX' || row.anomalyType === 'BOTH') ? 'val-over' : ''">
|
||||
{{ row.actualMax != null ? row.actualMax : '—' }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="实际最小值" align="right" width="96">
|
||||
<template slot-scope="{ row }">
|
||||
<span :class="(row.anomalyType === 'UNDER_MIN' || row.anomalyType === 'BOTH') ? 'val-under' : ''">
|
||||
{{ row.actualMin != null ? row.actualMin : '—' }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="最大偏差" align="right" width="88">
|
||||
<template slot-scope="{ row }">
|
||||
<span v-if="row.deviationMax != null" class="val-over">+{{ row.deviationMax }}</span>
|
||||
<span v-else>—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="最小偏差" align="right" width="88">
|
||||
<template slot-scope="{ row }">
|
||||
<span v-if="row.deviationMin != null" class="val-under">{{ row.deviationMin }}</span>
|
||||
<span v-else>—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="来源钢卷" prop="coilId" show-overflow-tooltip width="120" />
|
||||
<el-table-column label="检测时间" prop="detectedAt" width="140" show-overflow-tooltip>
|
||||
<template slot-scope="{ row }">{{ (row.detectedAt || '').substring(0, 16) || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="异常类型" align="center" width="120">
|
||||
<template slot-scope="{ row }">
|
||||
<el-tag v-if="row.anomalyType === 'OVER_MAX' || row.anomalyType === 'BOTH'" size="mini" type="danger" style="margin:1px">超上限</el-tag>
|
||||
<el-tag v-if="row.anomalyType === 'UNDER_MIN' || row.anomalyType === 'BOTH'" size="mini" type="warning" style="margin:1px">低于下限</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 对比图 -->
|
||||
<div ref="anomalyChart" class="anomaly-chart" />
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -361,6 +274,16 @@
|
||||
<!-- 方案参数 dialog -->
|
||||
<el-dialog :title="paramTitle" :visible.sync="paramOpen" width="480px" append-to-body @close="paramForm = {}">
|
||||
<el-form ref="paramFormRef" :model="paramForm" :rules="paramRules" label-width="90px" size="small">
|
||||
<el-form-item v-if="!paramForm.paramId" label="所属点位" prop="planId">
|
||||
<el-select v-model="paramForm.planId" style="width:100%" placeholder="请选择点位">
|
||||
<el-option
|
||||
v-for="plan in planList"
|
||||
:key="plan.planId"
|
||||
:label="segLabel(plan.segmentType) + ' › ' + plan.pointName"
|
||||
:value="plan.planId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="参数编码" prop="paramCode">
|
||||
<el-input v-model="paramForm.paramCode" maxlength="64" />
|
||||
</el-form-item>
|
||||
@@ -488,7 +411,7 @@ const TEMPLATE_HEADERS = ['段类型', '段名称', '点位名称', '参数名
|
||||
/** 表头字段映射 */
|
||||
const HEADER_MAP = {
|
||||
'段类型': 'segmentType',
|
||||
'段名称': 'segmentName',
|
||||
'段名称': 'segmentName',
|
||||
'点位名称': 'pointName',
|
||||
'参数名称': 'paramName',
|
||||
'设定值': 'targetValue',
|
||||
@@ -514,9 +437,8 @@ export default {
|
||||
appliedFilterName: '',
|
||||
planList: [],
|
||||
planLoading: false,
|
||||
selectedPlan: null,
|
||||
paramList: [],
|
||||
paramLoading: false,
|
||||
allParamList: [],
|
||||
allParamLoading: false,
|
||||
planOpen: false,
|
||||
planTitle: '',
|
||||
planSubmitLoading: false,
|
||||
@@ -531,6 +453,7 @@ export default {
|
||||
paramSubmitLoading: false,
|
||||
paramForm: {},
|
||||
paramRules: {
|
||||
planId: [{ required: true, message: '请选择所属点位', trigger: 'change' }],
|
||||
paramCode: [{ required: true, message: '参数编码不能为空', trigger: 'blur' }],
|
||||
paramName: [{ required: true, message: '参数名称不能为空', trigger: 'blur' }]
|
||||
},
|
||||
@@ -632,12 +555,6 @@ export default {
|
||||
extra.sort((a, b) => String(a.label).localeCompare(String(b.label), 'zh-CN'))
|
||||
return [...SEGMENT_FORM_OPTIONS, ...extra]
|
||||
},
|
||||
/** 当前选中点位下的异常条目 */
|
||||
planAnomalies() {
|
||||
if (!this.selectedPlan) return []
|
||||
return this.allAnomalies.filter(a => a.paramCode === this.selectedPlan.pointCode ||
|
||||
this.paramList.some(p => p.paramCode === a.paramCode))
|
||||
},
|
||||
/** paramCode → anomaly 的快速索引(用于状态列) */
|
||||
paramAnomalyMap() {
|
||||
const map = {}
|
||||
@@ -648,45 +565,42 @@ export default {
|
||||
if (!this.coilAnomalyCoilId) return []
|
||||
return this.allAnomalies.filter(a => a.coilId === this.coilAnomalyCoilId)
|
||||
},
|
||||
filteredPlans() {
|
||||
/** 所有 plan 的参数平铺成一行,带 plan 的段/点位信息 */
|
||||
flatRows() {
|
||||
const SEG_LABELS = { INLET: '入口段', PROCESS: '工艺段', OUTLET: '出口段' }
|
||||
const planMap = {}
|
||||
for (const plan of this.planList) planMap[plan.planId] = plan
|
||||
const rows = []
|
||||
for (const param of this.allParamList) {
|
||||
const plan = planMap[param.planId]
|
||||
if (!plan) continue
|
||||
rows.push({
|
||||
...param,
|
||||
_planId: plan.planId,
|
||||
_segmentType: plan.segmentType,
|
||||
_segmentLabel: SEG_LABELS[plan.segmentType] || plan.segmentName || plan.segmentType,
|
||||
_pointName: plan.pointName,
|
||||
_sortOrder: plan.sortOrder || 0
|
||||
})
|
||||
}
|
||||
rows.sort((a, b) => {
|
||||
const s = (a._sortOrder || 0) - (b._sortOrder || 0)
|
||||
return s !== 0 ? s : (a.paramId || 0) - (b.paramId || 0)
|
||||
})
|
||||
return rows
|
||||
},
|
||||
filteredFlatRows() {
|
||||
const type = this.activeSegmentType
|
||||
const sub = this.activeSegmentName
|
||||
return this.planList.filter(p => {
|
||||
const typeOk =
|
||||
type === '' || type === undefined || type === null
|
||||
? true
|
||||
: String(p.segmentType) === String(type)
|
||||
let nameOkSeg = true
|
||||
if (typeOk && type !== '' && type !== undefined && type !== null) {
|
||||
if (sub === '' || sub === undefined || sub === null) {
|
||||
nameOkSeg = true
|
||||
} else if (sub === '__EMPTY__') {
|
||||
const sn = p.segmentName != null ? String(p.segmentName).trim() : ''
|
||||
nameOkSeg = !sn
|
||||
} else {
|
||||
const sn = p.segmentName != null ? String(p.segmentName).trim() : ''
|
||||
nameOkSeg = sn === String(sub).trim()
|
||||
}
|
||||
}
|
||||
const nameOk = !this.appliedFilterName || (p.pointName || '').includes(this.appliedFilterName)
|
||||
return typeOk && nameOkSeg && nameOk
|
||||
const name = this.appliedFilterName
|
||||
return this.flatRows.filter(r => {
|
||||
const typeOk = !type || String(r._segmentType) === String(type)
|
||||
const nameOk = !name || (r.paramName || '').includes(name) || (r.paramCode || '').includes(name)
|
||||
return typeOk && nameOk
|
||||
})
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
$route: { immediate: true, handler() { this.syncFromRoute() } },
|
||||
planAnomalies: {
|
||||
handler(list) {
|
||||
if (list.length && this.anomalyExpanded) {
|
||||
this.$nextTick(() => this.renderAnomalyChart())
|
||||
}
|
||||
}
|
||||
},
|
||||
anomalyExpanded(val) {
|
||||
if (val && this.planAnomalies.length) {
|
||||
this.$nextTick(() => this.renderAnomalyChart())
|
||||
}
|
||||
},
|
||||
segmentTypeTabOptions: {
|
||||
handler() {
|
||||
const a = this.activeSegmentType
|
||||
@@ -852,22 +766,26 @@ export default {
|
||||
},
|
||||
loadPlans() {
|
||||
this.planLoading = true
|
||||
this.selectedPlan = null
|
||||
this.paramList = []
|
||||
listProcessPlan({ versionId: this.versionId, pageNum: 1, pageSize: 500 }).then(res => {
|
||||
this.planList = res.rows || []
|
||||
this.loadAllParams()
|
||||
}).catch(e => console.error(e)).finally(() => { this.planLoading = false })
|
||||
},
|
||||
loadParams(planId) {
|
||||
this.paramLoading = true
|
||||
listProcessPlanParam({ planId, pageNum: 1, pageSize: 500 }).then(res => {
|
||||
this.paramList = res.rows || []
|
||||
}).catch(e => console.error(e)).finally(() => { this.paramLoading = false })
|
||||
},
|
||||
onPlanSelect(row) {
|
||||
if (!row) return
|
||||
this.selectedPlan = row
|
||||
this.loadParams(row.planId)
|
||||
async loadAllParams() {
|
||||
if (!this.planList.length) { this.allParamList = []; return }
|
||||
this.allParamLoading = true
|
||||
try {
|
||||
const results = await Promise.all(
|
||||
this.planList.map(plan =>
|
||||
listProcessPlanParam({ planId: plan.planId, pageNum: 1, pageSize: 500 })
|
||||
.then(res => res.rows || [])
|
||||
.catch(() => [])
|
||||
)
|
||||
)
|
||||
this.allParamList = results.flat()
|
||||
} finally {
|
||||
this.allParamLoading = false
|
||||
}
|
||||
},
|
||||
applyFilter() { this.appliedFilterName = this.filterName },
|
||||
resetFilter() {
|
||||
@@ -921,28 +839,33 @@ export default {
|
||||
}).catch(() => {})
|
||||
},
|
||||
openParamDialog(planRow, paramRow) {
|
||||
const targetPlan = planRow || this.selectedPlan
|
||||
if (!targetPlan) { this.$message.warning('请先选择一个方案点位'); return }
|
||||
if (!this.selectedPlan || this.selectedPlan.planId !== targetPlan.planId) {
|
||||
this.selectedPlan = targetPlan
|
||||
this.loadParams(targetPlan.planId)
|
||||
}
|
||||
this.paramForm = paramRow
|
||||
? { ...paramRow }
|
||||
: {
|
||||
planId: targetPlan.planId,
|
||||
paramCode: undefined,
|
||||
paramName: undefined,
|
||||
planId: planRow ? planRow.planId : undefined,
|
||||
paramCode: undefined,
|
||||
paramName: undefined,
|
||||
targetValue: undefined,
|
||||
lowerLimit: undefined,
|
||||
upperLimit: undefined,
|
||||
unit: undefined,
|
||||
remark: undefined
|
||||
lowerLimit: undefined,
|
||||
upperLimit: undefined,
|
||||
unit: undefined,
|
||||
remark: undefined
|
||||
}
|
||||
this.paramTitle = paramRow ? '编辑方案参数' : '新建方案参数'
|
||||
this.paramOpen = true
|
||||
this.$nextTick(() => this.$refs.paramFormRef && this.$refs.paramFormRef.clearValidate())
|
||||
},
|
||||
editParamFromFlat(row) {
|
||||
this.openParamDialog(null, row)
|
||||
},
|
||||
deleteParamFromFlat(row) {
|
||||
this.$modal.confirm('确认删除该参数?').then(() => {
|
||||
return delProcessPlanParam(row.paramId)
|
||||
}).then(() => {
|
||||
this.$modal.msgSuccess('删除成功')
|
||||
this.loadAllParams()
|
||||
}).catch(() => {})
|
||||
},
|
||||
submitParam() {
|
||||
this.$refs.paramFormRef.validate(ok => {
|
||||
if (!ok) return
|
||||
@@ -951,18 +874,10 @@ export default {
|
||||
req.then(() => {
|
||||
this.$modal.msgSuccess('保存成功')
|
||||
this.paramOpen = false
|
||||
this.loadParams(this.selectedPlan.planId)
|
||||
this.loadAllParams()
|
||||
}).catch(e => console.error(e)).finally(() => { this.paramSubmitLoading = false })
|
||||
})
|
||||
},
|
||||
removeParam(row) {
|
||||
this.$modal.confirm('确认删除该参数?').then(() => {
|
||||
return delProcessPlanParam(row.paramId)
|
||||
}).then(() => {
|
||||
this.$modal.msgSuccess('删除成功')
|
||||
this.loadParams(this.selectedPlan.planId)
|
||||
}).catch(() => {})
|
||||
},
|
||||
// ===================== 导入相关方法 =====================
|
||||
/**
|
||||
* 打开导入对话框
|
||||
@@ -1218,7 +1133,7 @@ export default {
|
||||
async batchImport() {
|
||||
// <20>照点位名称分组,每个点位创建一条记录,然后添加多个参数
|
||||
const pointGroups = {}
|
||||
|
||||
|
||||
// 分组处理
|
||||
this.tableData.forEach(row => {
|
||||
const pointKey = `${row.segmentType}_${row.segmentName}_${row.pointName}`
|
||||
@@ -1249,12 +1164,12 @@ export default {
|
||||
try {
|
||||
await this.importOnePoint(group)
|
||||
this.importedCount += group.params.length
|
||||
|
||||
|
||||
// 更新进度
|
||||
index += group.params.length
|
||||
const currentProgress = Math.round((index / this.totalCount) * 100)
|
||||
this.progress = currentProgress
|
||||
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 50))
|
||||
} catch (error) {
|
||||
throw new Error(`导入点位"${group.pointName}"失败:${error.message}`)
|
||||
@@ -1275,7 +1190,7 @@ export default {
|
||||
pointCode: group.pointCode,
|
||||
sortOrder: 0
|
||||
}
|
||||
|
||||
|
||||
const planRes = await addProcessPlan(planParams)
|
||||
if (planRes.code !== 200) {
|
||||
throw new Error(`点位创建失败:${planRes.msg || '接口返回异常'}`)
|
||||
@@ -1293,7 +1208,7 @@ export default {
|
||||
lowerLimit: param.lowerLimit ? Number(param.lowerLimit) : null,
|
||||
unit: param.unit
|
||||
}
|
||||
|
||||
|
||||
const paramRes = await addProcessPlanParam(paramParams)
|
||||
if (paramRes.code !== 200) {
|
||||
throw new Error(`参数"${param.paramName}"创建失败:${paramRes.msg || '接口返回异常'}`)
|
||||
@@ -1354,7 +1269,7 @@ export default {
|
||||
// 创建工作簿
|
||||
const wb = XLSX.utils.book_new()
|
||||
const ws = XLSX.utils.aoa_to_sheet(templateData)
|
||||
|
||||
|
||||
// 设置列宽
|
||||
ws['!cols'] = [
|
||||
{ wch: 10 }, // 段类型
|
||||
@@ -1366,7 +1281,7 @@ export default {
|
||||
{ wch: 10 }, // 下限
|
||||
{ wch: 10 } // 单位
|
||||
]
|
||||
|
||||
|
||||
XLSX.utils.book_append_sheet(wb, ws, '导入模板')
|
||||
XLSX.writeFile(wb, '方案点位导入模板.xlsx')
|
||||
},
|
||||
@@ -1558,6 +1473,19 @@ export default {
|
||||
|
||||
.btn-danger { color: #f56c6c; }
|
||||
|
||||
/* ── 段 chip ── */
|
||||
.seg-chip {
|
||||
display: inline-block;
|
||||
padding: 2px 7px;
|
||||
border-radius: 10px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.seg-inlet { background: #ecf5ff; color: #3a6ea8; border: 1px solid #b3d8ff; }
|
||||
.seg-process { background: #f0f9eb; color: #3a7a2a; border: 1px solid #b3e19d; }
|
||||
.seg-outlet { background: #fdf6ec; color: #a86a00; border: 1px solid #f5dab1; }
|
||||
|
||||
/* ── 偏差分析 ── */
|
||||
.anomaly-section-header {
|
||||
display: flex;
|
||||
|
||||
Reference in New Issue
Block a user