@@ -0,0 +1,953 @@
< template >
< div class = "coil-bind-page" >
<!-- 筛选 + 操作一体栏 -- >
< div class = "filter-bar" >
< el-form :inline = "true" :model = "query" size = "small" class = "filter-form" >
< el-form-item label = "出口卷号" >
< el-input
v-model = "query.coilId"
placeholder = "模糊搜索"
clearable
style = "width:150px"
@keyup.enter.native ="handleSearch"
/ >
< / el-form-item >
< el-form-item label = "入口卷号" >
< el-input
v-model = "query.enCoilId"
placeholder = "模糊搜索"
clearable
style = "width:150px"
@keyup.enter.native ="handleSearch"
/ >
< / el-form-item >
< el-form-item label = "异常" >
< el-select v-model = "query.hasAnomaly" placeholder="全部" clearable style="width:88px" >
< el -option label = "有异常" :value = "1" / >
< el-option label = "正常" :value = "0" / >
< / el-select >
< / el-form-item >
< el-form-item >
< el-button type = "primary" icon = "el-icon-search" :loading = "loading" @click ="handleSearch" > 查询 < / el -button >
< el-button icon = "el-icon-refresh" @click ="handleReset" > 重置 < / el -button >
< / el-form-item >
< / el-form >
<!-- 右侧 : 始终可见的操作区 -- >
< div class = "toolbar-action" >
< transition name = "sel-fade" >
< span v-if = "selected.length" class="sel-badge" >
已选 < b > { { selected . length } } < / b > 条
< el-button type = "text" size = "mini" style = "color:#909399;padding:0 4px" @click ="$refs.recordTable.clearSelection()" > 清除 < / el -button >
< / span >
< / transition >
< el-button
type = "primary"
size = "small"
icon = "el-icon-connection"
:disabled = "!selected.length"
@click ="openBindDialog"
> 重新绑定规程方案 < / el-button >
< / div >
< / div >
<!-- 表格 -- >
< div class = "table-wrap" v-loading = "loading" >
< el -table
ref = "recordTable"
:data = "rows"
size = "small"
border
:height = "tableHeight"
@ selection -change = " onSelectionChange "
>
< el-table-column type = "selection" width = "44" fixed / >
< el-table-column type = "index" width = "44" label = "序" align = "center" fixed / >
< el-table-column prop = "coilId" label = "出口卷号" width = "160" fixed show -overflow -tooltip / >
< el-table-column prop = "enCoilId" label = "入口卷号" width = "160" show -overflow -tooltip >
< template slot -scope = " { row } " > { { row . enCoilId || '—' } } < / template >
< / el-table-column >
< el-table-column label = "当前规程方案" min -width = " 220 " show -overflow -tooltip >
< template slot -scope = " { row } " >
< template v-if = "row.specName || row.specCode" >
< span class = "tag-spec" > { { row . specCode || '—' } } < / span >
< span class = "tag-spec-name" > { { row . specName } } < / span >
< / template >
< span v-else class = "dim" > — < / span >
< / template >
< / el-table-column >
< el-table-column label = "版本" width = "110" >
< template slot -scope = " { row } " >
< template v-if = "row.versionCode" >
< span class = "tag-version" > { { row . versionCode } } < / span >
< el-tag
v-if = "row.isActive === 1"
type = "success"
size = "mini"
effect = "dark"
style = "border-radius:8px;margin-left:4px"
> 生效 < / el-tag >
< / template >
< span v-else class = "dim" > — < / span >
< / template >
< / el-table-column >
< el-table-column label = "异常" width = "76" align = "center" >
< template slot -scope = " { row } " >
< el-tag
v-if = "row.hasAnomaly === 1"
type = "danger"
size = "mini"
effect = "plain"
disable -transitions
> { { row . anomalyCnt } } 项 < / el-tag >
< el-tag v-else type = "success" size = "mini" effect = "plain" disable -transitions > 正常 < / el-tag >
< / template >
< / el-table-column >
< el-table-column label = "检测时间" width = "152" >
< template slot -scope = " { row } " > { { fmtTime ( row . processTime ) } } < / template >
< / el-table-column >
< el-table-column label = "记录时间" width = "152" >
< template slot -scope = " { row } " > { { fmtTime ( row . createTime ) } } < / template >
< / el-table-column >
< el-table-column prop = "remark" label = "备注" min -width = " 120 " show -overflow -tooltip >
< template slot -scope = " { row } " > { { row . remark || '—' } } < / template >
< / el-table-column >
< el-table-column label = "操作" width = "72" align = "center" fixed = "right" >
< template slot -scope = " { row } " >
< el-button type = "text" size = "mini" @click.stop ="openDetail(row)" > 详情 < / el -button >
< / template >
< / el-table-column >
< / el-table >
< / div >
<!-- 底部分页栏 -- >
< div class = "bottom-bar" >
< el-pagination
small
layout = "total, sizes, prev, pager, next"
:total = "total"
:page-size = "query.pageSize"
: page -sizes = " [ 20 , 50 , 100 ] "
:current-page = "query.pageNum"
@ size -change = " v = > { query . pageSize = v ; query . pageNum = 1 ; loadRows ( ) } "
@current-change=" v => { query . pageNum = v ; loadRows ( ) } "
/>
</div>
<!-- ══════════════════════════════════════
钢卷参数详情 抽屉
══════════════════════════════════════ -->
<el-drawer
:visible.sync=" detailDrawerVisible "
:title=" detailRow ? ( '钢卷参数详情 · ' + detailRow . coilId ) : '钢卷参数详情' "
direction=" rtl "
size=" 780 px "
append-to-body
@close=" onDetailClose "
>
<div class=" detail - wrap " v-loading=" detailLoading ">
<!-- 基本信息卡 -->
<div class=" detail - info - card " v-if=" detailRow ">
<div class=" dic - row ">
<span class=" dic - label ">出口卷号</span>
<span class=" dic - val ">{{ detailRow.coilId || '—' }}</span>
<span class=" dic - label ">入口卷号</span>
<span class=" dic - val ">{{ detailRow.enCoilId || '—' }}</span>
</div>
<div class=" dic - row ">
<span class=" dic - label ">规程方案</span>
<span class=" dic - val ">
<span v-if=" detailRow . specCode " class=" tag - spec ">{{ detailRow.specCode }}</span>
<span v-if=" detailRow . specName " class=" tag - spec - name ">{{ detailRow.specName }}</span>
<span v-if=" ! detailRow . specCode && ! detailRow . specName " class=" dim ">—</span>
</span>
<span class=" dic - label ">版本</span>
<span class=" dic - val ">
<span v-if=" detailRow . versionCode " class=" tag - version ">{{ detailRow.versionCode }}</span>
<span v-else class=" dim ">—</span>
</span>
</div>
<div class=" dic - row ">
<span class=" dic - label ">检测时间</span>
<span class=" dic - val ">{{ fmtTime(detailRow.processTime) }}</span>
<span class=" dic - label ">异常状态</span>
<span class=" dic - val ">
<el-tag v-if=" detailRow . hasAnomaly === 1 " type=" danger " size=" mini " effect=" plain ">{{ detailRow.anomalyCnt }} 项异常</el-tag>
<el-tag v-else type=" success " size=" mini " effect=" plain ">正常</el-tag>
</span>
</div>
</div>
<!-- 参数对比表 -->
<div v-if=" ! detailLoading && detailParams . length === 0 " class=" detail - empty ">
暂无参数数据
</div>
<template v-else>
<div
v-for=" group in detailParamGroups "
:key=" group . planId "
class=" param - group "
>
<div class=" param - group - hd ">
<span class=" pg - segment ">{{ group.segmentName || group.segmentType || '—' }}</span>
<span class=" pg - point ">{{ group.pointName }}
<span v-if=" group . pointCode " class=" pg - code ">{{ group.pointCode }}</span>
</span>
</div>
<el-table
:data=" group . params "
size=" mini "
border
:row-class-name=" paramRowClass "
>
<el-table-column label=" 参数名称 " prop=" paramName " min-width=" 120 " show-overflow-tooltip />
<el-table-column label=" 编码 " prop=" paramCode " width=" 90 " show-overflow-tooltip />
<el-table-column label=" 单位 " prop=" unit " width=" 56 " align=" center ">
<template slot-scope=" { row } ">{{ row.unit || '—' }}</template>
</el-table-column>
<el-table-column label=" 设定目标 " width=" 84 " align=" right ">
<template slot-scope=" { row } ">
<span :class=" row . _hasAnomaly ? 'val-anomaly' : '' ">{{ fmtNum(row.targetValue) }}</span>
</template>
</el-table-column>
<el-table-column label=" 下限 " width=" 72 " align=" right ">
<template slot-scope=" { row } ">{{ fmtNum(row.lowerLimit) }}</template>
</el-table-column>
<el-table-column label=" 上限 " width=" 72 " align=" right ">
<template slot-scope=" { row } ">{{ fmtNum(row.upperLimit) }}</template>
</el-table-column>
<el-table-column label=" 实际最大 " width=" 84 " align=" right ">
<template slot-scope=" { row } ">
<span v-if=" row . _hasAnomaly " :class=" row . _anomalyType === 'UPPER' ? 'val-danger' : 'val-ok' ">{{ fmtNum(row._actualMax) }}</span>
<span v-else class=" dim ">—</span>
</template>
</el-table-column>
<el-table-column label=" 实际最小 " width=" 84 " align=" right ">
<template slot-scope=" { row } ">
<span v-if=" row . _hasAnomaly " :class=" row . _anomalyType === 'LOWER' ? 'val-danger' : 'val-ok' ">{{ fmtNum(row._actualMin) }}</span>
<span v-else class=" dim ">—</span>
</template>
</el-table-column>
<el-table-column label=" 偏差 " width=" 72 " align=" right ">
<template slot-scope=" { row } ">
<span v-if=" row . _hasAnomaly " class=" val - danger ">
{{ fmtNum(row._anomalyType === 'UPPER' ? row._deviationMax : row._deviationMin) }}
</span>
<span v-else class=" dim ">—</span>
</template>
</el-table-column>
<el-table-column label=" 状态 " width=" 72 " align=" center ">
<template slot-scope=" { row } ">
<el-tag v-if=" row . _hasAnomaly " type=" danger " size=" mini " effect=" plain ">
{{ row._anomalyType === 'UPPER' ? '超上限' : row._anomalyType === 'LOWER' ? '低于下限' : '异常' }}
</el-tag>
<el-tag v-else type=" success " size=" mini " effect=" plain ">正常</el-tag>
</template>
</el-table-column>
</el-table>
</div>
</template>
</div>
</el-drawer>
<!-- ══════════════════════════════════════
重新绑定 对话框
══════════════════════════════════════ -->
<el-dialog
title=" 重新绑定规程方案 "
:visible.sync=" bindDialogVisible "
width=" 960 px "
append-to-body
:close-on-click-modal=" false "
@close=" resetBindDialog "
>
<!-- ① 摘要条 — 始终可见 -->
<div :class=" [ 'summary-bar' , selectedVersion ? 'summary-bar--ready' : 'summary-bar--idle' ] ">
<template v-if=" selectedVersion ">
<i class=" el - icon - success summary - icon " />
<span>
将把
<b class=" s - count ">{{ selected.length }}</b>
条记录重新绑定至规程
<span class=" s - spec ">{{ selectedSpec.specName }}</span>
<span class=" s - sep ">› </span>
版本
<span class=" s - version ">{{ selectedVersion.versionCode }}</span>
</span>
<div class=" summary - remove - opt ">
<el-checkbox v-model=" removeOldRecord " :disabled=" ! singleSourceVersion ">
同时从原版本移除
<span v-if=" ! singleSourceVersion " class=" dim " style=" margin - left : 4 px ">(选中记录来自多个版本时不可用)</span>
</el-checkbox>
</div>
</template>
<template v-else>
<i class=" el - icon - info summary - icon summary - icon -- idle " />
<span class=" s - idle ">请先在下方选择目标规程,再选择版本后点击「确认绑定」</span>
</template>
</div>
<!-- ② 左右选择区 -->
<div class=" bind - body ">
<!-- 左:规程列表 -->
<div class=" spec - col ">
<div class=" col - hd ">规程列表</div>
<div class=" spec - filter ">
<el-input
v-model=" specKeyword "
placeholder=" 搜索名称 / 编码 "
size=" mini "
clearable
prefix-icon=" el - icon - search "
@input=" filterSpecs "
/>
</div>
<div class=" spec - list " v-loading=" specLoading ">
<div
v-for=" spec in filteredSpecList "
:key=" spec . specId "
:class=" [ 'spec-item' , { 'spec-item--active' : selectedSpec && selectedSpec . specId === spec . specId } ] "
@click=" selectSpec ( spec ) "
>
<div class=" spec - item _ _name ">{{ spec.specName }}</div>
<div class=" spec - item _ _code ">{{ spec.specCode }}</div>
</div>
<div v-if=" ! specLoading && ! filteredSpecList . length " class=" list - empty ">暂无规程</div>
</div>
</div>
<div class=" col - divider " />
<!-- 右:版本列表 -->
<div class=" version - col ">
<template v-if=" ! selectedSpec ">
<div class=" version - placeholder ">
<i class=" el - icon - back " style=" font - size : 18 px ; color : # c0c4cc ; margin - right : 6 px " />
请在左侧选择规程
</div>
</template>
<template v-else>
<div class=" col - hd ">
<span class=" spec - name - label ">{{ selectedSpec.specName }}</span>
<span class=" spec - code - chip ">{{ selectedSpec.specCode }}</span>
</div>
<div v-if=" selectedSpec . remark " class=" spec - remark ">{{ selectedSpec.remark }}</div>
<div class=" version - list " v-loading=" versionLoading ">
<div v-if=" ! versionLoading && ! versionList . length " class=" list - empty ">
该规程暂无版本,请先在规程管理中创建
</div>
<div
v-for=" ver in versionList "
:key=" ver . versionId "
:class=" [ 'version-card' , {
'version-card--selected' : selectedVersion && selectedVersion . versionId === ver . versionId ,
'version-card--obsolete' : ver . status === 'OBSOLETE'
} ] "
@click=" ver . status !== 'OBSOLETE' && selectVersion ( ver ) "
>
<div class=" vc - body ">
<div class=" vc - row1 ">
<span class=" vc - code ">{{ ver.versionCode }}</span>
<el-tag :type=" versionStatusType ( ver . status ) " size=" mini " effect=" plain " style=" border - radius : 10 px ; margin - left : 6 px ">
{{ versionStatusLabel(ver.status) }}
</el-tag>
<el-tag v-if=" ver . isActive === 1 " type=" success " size=" mini " effect=" dark " style=" border - radius : 10 px ; margin - left : 4 px ">
当前生效
</el-tag>
<span v-if=" ver . status === 'OBSOLETE' " class=" obsolete - note ">(已作废,不可选)</span>
</div>
<div class=" vc - row2 ">
创建于 {{ (ver.createTime || '').substring(0, 16) || '—' }}
<template v-if=" ver . updateTime && ver . updateTime !== ver . createTime ">
· 更新于 {{ (ver.updateTime || '').substring(0, 16) }}
</template>
</div>
<div v-if=" ver . remark " class=" vc - remark ">{{ ver.remark }}</div>
</div>
<div class=" vc - check ">
<i v-if=" selectedVersion && selectedVersion . versionId === ver . versionId " class=" el - icon - circle - check " />
</div>
</div>
</div>
</template>
</div>
</div>
<div slot=" footer " class=" bind - footer ">
<el-button size=" small " @click=" bindDialogVisible = false ">取消</el-button>
<el-button
size=" small "
type=" primary "
:disabled=" ! selectedVersion "
:loading=" bindLoading "
@click=" confirmBind "
>确认绑定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listProcessCoilRecord, batchRebindCoilRecord } from '@/api/wms/processCoilRecord'
import { listProcessSpec } from '@/api/wms/processSpec'
import { listProcessSpecVersion } from '@/api/wms/processSpecVersion'
import { listProcessPlan } from '@/api/wms/processPlan'
import { listProcessPlanParam } from '@/api/wms/processPlanParam'
import { listAllProcessAnomaly } from '@/api/wms/processAnomaly'
const VERSION_STATUS = [
{ value: 'DRAFT', label: '草稿' },
{ value: 'PUBLISHED', label: '已发布' },
{ value: 'OBSOLETE', label: '已作废' }
]
export default {
name: 'CoilSpecBind',
data() {
return {
loading: false,
rows: [],
total: 0,
selected: [],
query: {
pageNum: 1,
pageSize: 20,
coilId: '',
enCoilId: '',
hasAnomaly: null
},
tableHeight: 'calc(100vh - 218px)',
detailDrawerVisible: false,
detailLoading: false,
detailRow: null,
detailParams: [],
bindDialogVisible: false,
removeOldRecord: false,
specLoading: false,
specList: [],
filteredSpecList: [],
specKeyword: '',
selectedSpec: null,
versionLoading: false,
versionList: [],
selectedVersion: null,
bindLoading: false
}
},
computed: {
detailParamGroups() {
const planMap = {}
for (const p of this.detailParams) {
const key = p._planId
if (!planMap[key]) {
planMap[key] = {
planId: p._planId,
segmentType: p._segmentType,
segmentName: p._segmentName,
pointName: p._pointName,
pointCode: p._pointCode,
sortOrder: p._sortOrder,
params: []
}
}
planMap[key].params.push(p)
}
return Object.values(planMap).sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0))
},
// 选中行是否全来自同一个版本(决定是否可以" 移除旧记录 " )
singleSourceVersion ( ) {
if ( ! this . selected . length ) return false
const vids = [ ... new Set ( this . selected . map ( r => r . versionId ) ) ]
return vids . length === 1
}
} ,
created ( ) {
this . loadRows ( )
} ,
methods : {
async loadRows ( ) {
this . loading = true
try {
const params = { ... this . query }
if ( ! params . coilId ) delete params . coilId
if ( ! params . enCoilId ) delete params . enCoilId
if ( params . hasAnomaly === null ) delete params . hasAnomaly
const res = await listProcessCoilRecord ( params )
this . rows = res . rows || [ ]
this . total = res . total || 0
} finally {
this . loading = false
}
} ,
handleSearch ( ) {
this . query . pageNum = 1
this . loadRows ( )
} ,
handleReset ( ) {
this . query = { pageNum : 1 , pageSize : 20 , coilId : '' , enCoilId : '' , hasAnomaly : null }
this . loadRows ( )
} ,
onSelectionChange ( val ) {
this . selected = val
if ( ! this . singleSourceVersion ) this . removeOldRecord = false
} ,
// ── 绑定对话框 ──────────────────────────────
openBindDialog ( ) {
this . specKeyword = ''
this . selectedSpec = null
this . selectedVersion = null
this . versionList = [ ]
this . removeOldRecord = false
this . bindDialogVisible = true
this . loadSpecs ( )
} ,
resetBindDialog ( ) {
this . selectedSpec = null
this . selectedVersion = null
this . versionList = [ ]
this . specKeyword = ''
this . removeOldRecord = false
} ,
async loadSpecs ( ) {
this . specLoading = true
try {
const res = await listProcessSpec ( { pageNum : 1 , pageSize : 200 } )
this . specList = res . rows || [ ]
this . filteredSpecList = this . specList
} finally {
this . specLoading = false
}
} ,
filterSpecs ( ) {
const kw = ( this . specKeyword || '' ) . trim ( ) . toLowerCase ( )
this . filteredSpecList = kw
? this . specList . filter ( s =>
( s . specName || '' ) . toLowerCase ( ) . includes ( kw ) ||
( s . specCode || '' ) . toLowerCase ( ) . includes ( kw )
)
: this . specList
} ,
selectSpec ( spec ) {
this . selectedSpec = spec
this . selectedVersion = null
this . versionList = [ ]
this . loadVersions ( spec . specId )
} ,
async loadVersions ( specId ) {
this . versionLoading = true
try {
const res = await listProcessSpecVersion ( { specId , pageNum : 1 , pageSize : 200 } )
this . versionList = res . rows || [ ]
} finally {
this . versionLoading = false
}
} ,
selectVersion ( ver ) {
this . selectedVersion = ver
} ,
async confirmBind ( ) {
if ( ! this . selectedVersion || ! this . selected . length ) return
this . bindLoading = true
try {
const coilIds = this . selected . map ( r => r . coilId )
const oldVersionId = ( this . removeOldRecord && this . singleSourceVersion )
? this . selected [ 0 ] . versionId
: null
await batchRebindCoilRecord ( {
coilIds ,
newVersionId : this . selectedVersion . versionId ,
oldVersionId
} )
this . $modal . msgSuccess (
` 已成功将 ${ this . selected . length } 条记录绑定至【 ${ this . selectedSpec . specName } / ${ this . selectedVersion . versionCode } 】 `
)
this . bindDialogVisible = false
this . $refs . recordTable . clearSelection ( )
this . loadRows ( )
} catch ( e ) {
console . error ( e )
} finally {
this . bindLoading = false
}
} ,
// ── 详情抽屉 ──────────────────────────────────
openDetail ( row ) {
this . detailRow = row
this . detailParams = [ ]
this . detailDrawerVisible = true
this . loadDetailData ( row )
} ,
onDetailClose ( ) {
this . detailRow = null
this . detailParams = [ ]
} ,
async loadDetailData ( row ) {
if ( ! row . versionId ) return
this . detailLoading = true
try {
const [ plansRes , anomalyRes ] = await Promise . all ( [
listProcessPlan ( { versionId : row . versionId , pageNum : 1 , pageSize : 200 } ) ,
listAllProcessAnomaly ( { coilId : row . coilId , versionId : row . versionId } )
] )
const plans = plansRes . rows || [ ]
const anomalyList = anomalyRes . data || [ ]
const anomalyMap = { }
for ( const a of anomalyList ) {
anomalyMap [ a . paramCode ] = a
}
const allParams = [ ]
const paramPromises = plans . map ( plan =>
listProcessPlanParam ( { planId : plan . planId , pageNum : 1 , pageSize : 500 } )
. then ( res => {
const params = res . rows || [ ]
for ( const p of params ) {
const anomaly = anomalyMap [ p . paramCode ] || null
allParams . push ( {
... p ,
_planId : plan . planId ,
_segmentType : plan . segmentType ,
_segmentName : plan . segmentName ,
_pointName : plan . pointName ,
_pointCode : plan . pointCode ,
_sortOrder : plan . sortOrder ,
_hasAnomaly : ! ! anomaly ,
_anomalyType : anomaly ? anomaly . anomalyType : null ,
_actualMax : anomaly ? anomaly . actualMax : null ,
_actualMin : anomaly ? anomaly . actualMin : null ,
_deviationMax : anomaly ? anomaly . deviationMax : null ,
_deviationMin : anomaly ? anomaly . deviationMin : null
} )
}
} )
)
await Promise . all ( paramPromises )
this . detailParams = allParams
} finally {
this . detailLoading = false
}
} ,
paramRowClass ( { row } ) {
return row . _hasAnomaly ? 'row-anomaly' : ''
} ,
fmtNum ( val ) {
if ( val === null || val === undefined || val === '' ) return '—'
const n = Number ( val )
return isNaN ( n ) ? val : n . toFixed ( 3 ) . replace ( /\.?0+$/ , '' )
} ,
// ── 工具 ──────────────────────────────────
fmtTime ( val ) {
if ( ! val ) return '—'
return String ( val ) . substring ( 0 , 16 )
} ,
versionStatusType ( status ) {
return { DRAFT : '' , PUBLISHED : 'success' , OBSOLETE : 'info' } [ status ] || ''
} ,
versionStatusLabel ( status ) {
return ( VERSION _STATUS . find ( s => s . value === status ) || { } ) . label || status || '—'
}
}
}
< / script >
< style scoped >
/* ── 页面 ── */
. coil - bind - page {
padding : 12 px 16 px ;
height : 100 % ;
box - sizing : border - box ;
display : flex ;
flex - direction : column ;
background : # fff ;
}
/* ── 筛选 + 操作一体栏 ── */
. filter - bar {
flex - shrink : 0 ;
display : flex ;
align - items : flex - start ;
justify - content : space - between ;
gap : 12 px ;
margin - bottom : 6 px ;
padding - bottom : 8 px ;
border - bottom : 1 px solid # f0f2f5 ;
}
. filter - form { flex : 1 ; min - width : 0 ; }
. filter - bar . el - form - item { margin - bottom : 6 px ; }
. toolbar - action {
flex - shrink : 0 ;
display : flex ;
align - items : center ;
gap : 10 px ;
padding - top : 2 px ;
}
. sel - badge {
font - size : 13 px ;
color : # 606266 ;
white - space : nowrap ;
}
. sel - badge b { color : # 5 F7BA0 ; font - size : 15 px ; margin : 0 2 px ; }
. sel - fade - enter - active , . sel - fade - leave - active { transition : opacity .2 s ; }
. sel - fade - enter , . sel - fade - leave - to { opacity : 0 ; }
/* ── 表格 ── */
. table - wrap {
flex : 1 ;
min - height : 0 ;
border : 1 px solid # ebeef5 ;
border - radius : 3 px ;
overflow : hidden ;
}
. tag - spec {
display : inline - block ;
padding : 1 px 7 px ;
border - radius : 3 px 0 0 3 px ;
background : # ecf5ff ;
color : # 3 a6ea8 ;
font - size : 11 px ;
border : 1 px solid # b3d8ff ;
}
. tag - spec - name {
display : inline - block ;
padding : 1 px 7 px ;
border - radius : 0 3 px 3 px 0 ;
background : # f5f7fa ;
color : # 606266 ;
font - size : 11 px ;
border : 1 px solid # dcdfe6 ;
border - left : none ;
}
. tag - version {
display : inline - block ;
padding : 1 px 8 px ;
background : # f0f9eb ;
color : # 4 a8a2a ;
font - size : 11 px ;
border : 1 px solid # c2e7b0 ;
border - radius : 3 px ;
}
. dim { font - size : 12 px ; color : # c0c4cc ; }
/* ── 底部分页 ── */
. bottom - bar {
flex - shrink : 0 ;
display : flex ;
justify - content : flex - end ;
padding : 8 px 0 0 ;
}
/* ── 摘要条 ── */
. summary - bar {
display : flex ;
align - items : flex - start ;
gap : 8 px ;
padding : 10 px 16 px ;
border - radius : 4 px ;
margin - bottom : 12 px ;
font - size : 13 px ;
line - height : 1.6 ;
transition : background .2 s , border - color .2 s ;
flex - wrap : wrap ;
}
. summary - bar -- idle { background : # f4f4f5 ; border : 1 px solid # e9e9eb ; color : # 909399 ; }
. summary - bar -- ready { background : # ecf5ff ; border : 1 px solid # b3d8ff ; color : # 303133 ; }
. summary - icon { font - size : 16 px ; flex - shrink : 0 ; color : # 5 F7BA0 ; margin - top : 2 px ; }
. summary - icon -- idle { color : # c0c4cc ; }
. s - count { font - size : 15 px ; color : # 5 F7BA0 ; margin : 0 2 px ; }
. s - spec { font - weight : 600 ; color : # 303133 ; margin : 0 3 px ; }
. s - sep { color : # c0c4cc ; margin : 0 3 px ; }
. s - version { font - weight : 600 ; color : # 5 F7BA0 ; }
. s - idle { color : # 909399 ; }
. summary - remove - opt {
margin - left : auto ;
flex - shrink : 0 ;
font - size : 12 px ;
color : # 606266 ;
}
/* ── 对话框主体 ── */
. bind - body {
display : flex ;
height : 460 px ;
overflow : hidden ;
border : 1 px solid # ebeef5 ;
border - radius : 4 px ;
}
. col - hd {
padding : 10 px 14 px 8 px ;
font - size : 13 px ;
font - weight : 600 ;
color : # 303133 ;
border - bottom : 1 px solid # f0f2f5 ;
background : # fafafa ;
display : flex ;
align - items : center ;
gap : 8 px ;
flex - shrink : 0 ;
}
. col - divider { width : 1 px ; background : # ebeef5 ; flex - shrink : 0 ; }
/* 左:规程列 */
. spec - col {
width : 256 px ;
flex - shrink : 0 ;
display : flex ;
flex - direction : column ;
overflow : hidden ;
}
. spec - filter { padding : 8 px 10 px ; border - bottom : 1 px solid # f0f2f5 ; flex - shrink : 0 ; }
. spec - list { flex : 1 ; overflow - y : auto ; padding : 4 px 0 ; }
. spec - item {
padding : 9 px 14 px ;
cursor : pointer ;
border - left : 3 px solid transparent ;
transition : background .12 s ;
}
. spec - item : hover { background : # f5f7fa ; }
. spec - item -- active { background : # ecf5ff ! important ; border - left - color : # 5 F7BA0 ; }
. spec - item _ _name { font - size : 13 px ; font - weight : 500 ; color : # 303133 ; line - height : 1.4 ; }
. spec - item _ _code { font - size : 11 px ; color : # 909399 ; margin - top : 2 px ; }
/* 右:版本列 */
. version - col {
flex : 1 ;
min - width : 0 ;
display : flex ;
flex - direction : column ;
overflow : hidden ;
}
. spec - name - label { font - size : 13 px ; font - weight : 600 ; color : # 303133 ; }
. spec - code - chip {
font - size : 11 px ; color : # 909399 ;
background : # f0f2f5 ; padding : 1 px 8 px ; border - radius : 10 px ;
}
. spec - remark {
font - size : 12 px ; color : # 909399 ;
padding : 5 px 14 px ; background : # fafbfc ;
border - bottom : 1 px solid # f0f2f5 ; flex - shrink : 0 ;
}
. version - placeholder {
flex : 1 ; display : flex ; align - items : center ; justify - content : center ;
color : # c0c4cc ; font - size : 13 px ;
}
. version - list {
flex : 1 ; overflow - y : auto ; padding : 10 px 12 px ;
display : flex ; flex - direction : column ; gap : 8 px ;
}
/* 版本卡片 */
. version - card {
display : flex ;
align - items : flex - start ;
justify - content : space - between ;
padding : 12 px 14 px ;
border : 1 px solid # ebeef5 ;
border - radius : 4 px ;
cursor : pointer ;
transition : border - color .15 s , box - shadow .15 s , background .15 s ;
background : # fff ;
}
. version - card : hover : not ( . version - card -- obsolete ) {
border - color : # 5 F7BA0 ;
box - shadow : 0 2 px 8 px rgba ( 95 , 123 , 160 , .12 ) ;
}
. version - card -- selected {
border - color : # 5 F7BA0 ! important ;
background : # f0f7ff ! important ;
box - shadow : 0 0 0 2 px rgba ( 95 , 123 , 160 , .18 ) ! important ;
}
. version - card -- obsolete { opacity : .5 ; cursor : not - allowed ; background : # fafafa ; }
. vc - body { flex : 1 ; min - width : 0 ; }
. vc - row1 { display : flex ; align - items : center ; flex - wrap : wrap ; gap : 4 px ; margin - bottom : 5 px ; }
. vc - code { font - size : 14 px ; font - weight : 600 ; color : # 303133 ; }
. obsolete - note { font - size : 11 px ; color : # c0c4cc ; margin - left : 4 px ; }
. vc - row2 { font - size : 11 px ; color : # c0c4cc ; margin - bottom : 3 px ; }
. vc - remark { font - size : 12 px ; color : # 909399 ; margin - top : 4 px ; word - break : break - all ; }
. vc - check { font - size : 18 px ; color : # 5 F7BA0 ; flex - shrink : 0 ; margin - left : 10 px ; align - self : center ; }
/* 底部 */
. bind - footer { display : flex ; justify - content : flex - end ; gap : 8 px ; }
. list - empty { padding : 32 px 0 ; text - align : center ; font - size : 13 px ; color : # c0c4cc ; }
/* ── 详情抽屉 ── */
. detail - wrap {
padding : 16 px 20 px 32 px ;
height : 100 % ;
overflow - y : auto ;
box - sizing : border - box ;
}
. detail - info - card {
background : # f8f9fb ;
border : 1 px solid # ebeef5 ;
border - radius : 4 px ;
padding : 12 px 16 px ;
margin - bottom : 16 px ;
}
. dic - row {
display : flex ;
align - items : center ;
gap : 8 px ;
margin - bottom : 8 px ;
}
. dic - row : last - child { margin - bottom : 0 ; }
. dic - label {
font - size : 12 px ;
color : # 909399 ;
flex - shrink : 0 ;
width : 56 px ;
text - align : right ;
}
. dic - val {
font - size : 13 px ;
color : # 303133 ;
min - width : 0 ;
flex : 1 ;
}
. param - group { margin - bottom : 20 px ; }
. param - group - hd {
display : flex ;
align - items : center ;
gap : 8 px ;
margin - bottom : 6 px ;
padding : 0 2 px ;
}
. pg - segment {
font - size : 11 px ;
color : # fff ;
background : # 5 F7BA0 ;
padding : 1 px 9 px ;
border - radius : 10 px ;
}
. pg - point { font - size : 13 px ; font - weight : 600 ; color : # 303133 ; }
. pg - code { font - size : 11 px ; color : # 909399 ; margin - left : 5 px ; }
. val - danger { color : # f56c6c ; font - weight : 600 ; }
. val - ok { color : # 67 c23a ; }
. val - anomaly { color : # e6a23c ; font - weight : 600 ; }
. detail - empty {
padding : 48 px 0 ;
text - align : center ;
font - size : 13 px ;
color : # c0c4cc ;
}
: : v - deep . row - anomaly td { background : # fff8f8 ! important ; }
: : v - deep . el - drawer _ _header { padding : 16 px 20 px 12 px ; font - size : 15 px ; font - weight : 600 ; color : # 303133 ; margin - bottom : 0 ; border - bottom : 1 px solid # f0f2f5 ; }
: : v - deep . el - drawer _ _body { overflow : hidden ; }
/* 覆盖 */
: : v - deep . el - button -- primary {
color : # fff ! important ; background : # 5 F7BA0 ! important ; border - color : # 5 F7BA0 ! important ;
}
: : v - deep . el - button -- primary : hover ,
: : v - deep . el - button -- primary : focus {
background : # 4 d6a8e ! important ; border - color : # 4 d6a8e ! important ;
}
: : v - deep . el - button -- primary . is - disabled { opacity : .5 ; }
: : v - deep . el - table . el - table _ _row : hover td { background : # f5f9ff ! important ; }
< / style >