|
|
@@ -0,0 +1,865 @@
|
|
|
+<template>
|
|
|
+ <div>
|
|
|
+ <ele-modal
|
|
|
+ width="98vw"
|
|
|
+ :visible.sync="visible"
|
|
|
+ v-if="visible"
|
|
|
+ :close-on-click-modal="false"
|
|
|
+ custom-class="ele-dialog-form client9-kitting-modal"
|
|
|
+ title="齐套性检查"
|
|
|
+ :maxable="true"
|
|
|
+ append-to-body
|
|
|
+ >
|
|
|
+ <div v-loading="loading" class="client9-shell">
|
|
|
+ <section class="client9-overview">
|
|
|
+ <div class="overview-main">
|
|
|
+ <div class="overview-kicker">主材料齐套</div>
|
|
|
+ <div class="overview-title">
|
|
|
+ {{ displayValue(planInfo.productName) }}
|
|
|
+ </div>
|
|
|
+ <div class="overview-code">{{ displayValue(planCode) }}</div>
|
|
|
+ </div>
|
|
|
+ <div class="overview-metrics">
|
|
|
+ <div
|
|
|
+ v-for="item in summaryCards"
|
|
|
+ :key="item.label"
|
|
|
+ class="metric-item"
|
|
|
+ :class="item.type"
|
|
|
+ >
|
|
|
+ <span class="metric-label">{{ item.label }}</span>
|
|
|
+ <strong class="metric-value">{{ item.value }}</strong>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </section>
|
|
|
+
|
|
|
+ <section class="client9-info-grid">
|
|
|
+ <div v-for="item in planFields" :key="item.label" class="info-item">
|
|
|
+ <span class="info-label">{{ item.label }}</span>
|
|
|
+ <span class="info-value">{{ item.value }}</span>
|
|
|
+ </div>
|
|
|
+ </section>
|
|
|
+
|
|
|
+ <section class="result-insight" :class="resultStateClass">
|
|
|
+ <div class="result-status">
|
|
|
+ <span class="result-label">齐套结果</span>
|
|
|
+ <strong>{{
|
|
|
+ displayValue(primaryResult.finalFilledShortState)
|
|
|
+ }}</strong>
|
|
|
+ </div>
|
|
|
+ <div class="result-progress">
|
|
|
+ <div class="progress-meta">
|
|
|
+ <span>可用覆盖率</span>
|
|
|
+ <strong>{{ availabilityRate }}</strong>
|
|
|
+ </div>
|
|
|
+ <div class="progress-track">
|
|
|
+ <div
|
|
|
+ class="progress-bar"
|
|
|
+ :style="{ width: availabilityRate }"
|
|
|
+ ></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="result-numbers">
|
|
|
+ <div
|
|
|
+ v-for="item in resultNumbers"
|
|
|
+ :key="item.label"
|
|
|
+ class="result-number"
|
|
|
+ >
|
|
|
+ <span>{{ item.label }}</span>
|
|
|
+ <strong>{{ item.value }}</strong>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </section>
|
|
|
+
|
|
|
+ <div class="form-wrapper table-panel">
|
|
|
+ <div
|
|
|
+ class="left-tree"
|
|
|
+ v-show="leftShow"
|
|
|
+ :style="{ width: leftWidth }"
|
|
|
+ >
|
|
|
+ <el-tree
|
|
|
+ ref="treeRef"
|
|
|
+ :expand-on-click-node="false"
|
|
|
+ :data="cardList"
|
|
|
+ :props="treeProps"
|
|
|
+ node-key="id"
|
|
|
+ highlight-current
|
|
|
+ @node-click="handleNodeClick"
|
|
|
+ ></el-tree>
|
|
|
+ </div>
|
|
|
+ <div :style="{ width: rightWidth }">
|
|
|
+ <ele-pro-table
|
|
|
+ ref="table"
|
|
|
+ :needPage="false"
|
|
|
+ :columns="columns"
|
|
|
+ :init-load="false"
|
|
|
+ :datasource="datasourceList"
|
|
|
+ row-key="jnKittingRowId"
|
|
|
+ :cache-key="`ProductionPlanClient9Table`"
|
|
|
+ border
|
|
|
+ height="calc(100vh - 530px)"
|
|
|
+ :selection.sync="selection"
|
|
|
+ >
|
|
|
+ <template v-slot:default>
|
|
|
+ <div class="detail-head">
|
|
|
+ <div>
|
|
|
+ <span class="detail-title">齐套明细</span>
|
|
|
+ <span class="detail-subtitle">
|
|
|
+ 共 {{ datasourceAllList.length }} 条
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ <span class="detail-bom">{{
|
|
|
+ displayValue(currentBomText)
|
|
|
+ }}</span>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <template v-slot:finalFilledShortState="{ row }">
|
|
|
+ <span
|
|
|
+ class="status-pill"
|
|
|
+ :class="getFinalStateClass(row.finalFilledShortState)"
|
|
|
+ >
|
|
|
+ {{ displayValue(row.finalFilledShortState) }}
|
|
|
+ </span>
|
|
|
+ </template>
|
|
|
+ </ele-pro-table>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div slot="footer">
|
|
|
+ <el-button plain @click="cancel">取消</el-button>
|
|
|
+ </div>
|
|
|
+ </ele-modal>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+ import { getJNProductKitting } from '@/api/productionPlan/index.js';
|
|
|
+
|
|
|
+ export default {
|
|
|
+ computed: {
|
|
|
+ planCode() {
|
|
|
+ return this.planInfo.productionPlanCode || this.planInfo.code || '';
|
|
|
+ },
|
|
|
+ planFields() {
|
|
|
+ return [
|
|
|
+ {
|
|
|
+ label: '规格',
|
|
|
+ value: this.displayValue(this.planInfo.specification)
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '型号',
|
|
|
+ value: this.displayValue(this.planInfo.model)
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '编码',
|
|
|
+ value: this.displayValue(this.planInfo.productCode)
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '牌号',
|
|
|
+ value: this.displayValue(this.planInfo.brandNo)
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '计划数量',
|
|
|
+ value: this.formatQuantityWithUnit(
|
|
|
+ this.planInfo.requiredFormingNum,
|
|
|
+ this.planInfo.measuringUnit || this.planInfo.unit
|
|
|
+ )
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: 'BOM',
|
|
|
+ value: this.displayValue(this.currentBomText)
|
|
|
+ }
|
|
|
+ ];
|
|
|
+ },
|
|
|
+ currentBomText() {
|
|
|
+ const bomName =
|
|
|
+ this.planInfo.bomName || this.planInfo.bomCategoryName || '';
|
|
|
+ const version =
|
|
|
+ this.planInfo.bomCategoryVersions || this.planInfo.versions || '';
|
|
|
+ const versionText = version
|
|
|
+ ? String(version).startsWith('V')
|
|
|
+ ? String(version)
|
|
|
+ : `V${version}.0`
|
|
|
+ : '';
|
|
|
+ if (bomName && versionText) return `${bomName} / ${versionText}`;
|
|
|
+ return bomName || versionText;
|
|
|
+ },
|
|
|
+ readyCount() {
|
|
|
+ return this.datasourceAllList.filter(
|
|
|
+ (item) => item.finalFilledShortState == '齐套'
|
|
|
+ ).length;
|
|
|
+ },
|
|
|
+ shortageCount() {
|
|
|
+ return this.datasourceAllList.filter(
|
|
|
+ (item) => item.finalFilledShortState == '缺料'
|
|
|
+ ).length;
|
|
|
+ },
|
|
|
+ readyRate() {
|
|
|
+ const total = this.datasourceAllList.length;
|
|
|
+ if (!total) return '0%';
|
|
|
+ return `${Math.round((this.readyCount / total) * 100)}%`;
|
|
|
+ },
|
|
|
+ summaryCards() {
|
|
|
+ return [
|
|
|
+ {
|
|
|
+ label: '物料项数',
|
|
|
+ value: this.datasourceAllList.length,
|
|
|
+ type: 'metric-total'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '齐套项',
|
|
|
+ value: this.readyCount,
|
|
|
+ type: 'metric-ready'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '缺料项',
|
|
|
+ value: this.shortageCount,
|
|
|
+ type: 'metric-risk'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '齐套率',
|
|
|
+ value: this.readyRate,
|
|
|
+ type: 'metric-rate'
|
|
|
+ }
|
|
|
+ ];
|
|
|
+ },
|
|
|
+ primaryResult() {
|
|
|
+ return this.datasourceAllList[0] || {};
|
|
|
+ },
|
|
|
+ resultNumbers() {
|
|
|
+ return [
|
|
|
+ {
|
|
|
+ label: '需求数量',
|
|
|
+ value: this.displayValue(this.primaryResult.needQuantity)
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '可用数量',
|
|
|
+ value: this.displayValue(this.primaryResult.normalQuantity)
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '库存数量',
|
|
|
+ value: this.displayValue(this.primaryResult.inventoryQuantity)
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '最终缺料',
|
|
|
+ value: this.displayValue(this.primaryResult.finalFilledShortCount)
|
|
|
+ }
|
|
|
+ ];
|
|
|
+ },
|
|
|
+ availabilityRate() {
|
|
|
+ const needQuantity = Number(this.primaryResult.needQuantity || 0);
|
|
|
+ const normalQuantity = Number(this.primaryResult.normalQuantity || 0);
|
|
|
+ if (!needQuantity) return '0%';
|
|
|
+ const rate = Math.min(
|
|
|
+ 100,
|
|
|
+ Math.max(0, Math.round((normalQuantity / needQuantity) * 100))
|
|
|
+ );
|
|
|
+ return `${rate}%`;
|
|
|
+ },
|
|
|
+ resultStateClass() {
|
|
|
+ if (this.primaryResult.finalFilledShortState == '缺料') {
|
|
|
+ return 'result-risk';
|
|
|
+ }
|
|
|
+ if (this.primaryResult.finalFilledShortState == '齐套') {
|
|
|
+ return 'result-ready';
|
|
|
+ }
|
|
|
+ return 'result-neutral';
|
|
|
+ },
|
|
|
+ columns() {
|
|
|
+ return [
|
|
|
+ {
|
|
|
+ width: 45,
|
|
|
+ type: 'selection',
|
|
|
+ columnKey: 'selection',
|
|
|
+ align: 'center',
|
|
|
+ fixed: 'left'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ columnKey: 'index',
|
|
|
+ label: '序号',
|
|
|
+ type: 'index',
|
|
|
+ width: 55,
|
|
|
+ align: 'center',
|
|
|
+ fixed: 'left'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'needQuantity',
|
|
|
+ label: '需求数量',
|
|
|
+ align: 'center',
|
|
|
+ showOverflowTooltip: true
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'normalQuantity',
|
|
|
+ label: '可用数量',
|
|
|
+ align: 'center',
|
|
|
+ showOverflowTooltip: true
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'inventoryQuantity',
|
|
|
+ label: '库存数量',
|
|
|
+ align: 'center',
|
|
|
+ showOverflowTooltip: true
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'finalFilledShortCount',
|
|
|
+ label: '最终缺料数量',
|
|
|
+ align: 'center',
|
|
|
+ showOverflowTooltip: true
|
|
|
+ },
|
|
|
+ {
|
|
|
+ slot: 'finalFilledShortState',
|
|
|
+ prop: 'finalFilledShortState',
|
|
|
+ label: '最终缺料状态',
|
|
|
+ align: 'center',
|
|
|
+ minWidth: 80,
|
|
|
+ fixed: 'right'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'message',
|
|
|
+ label: '消息内容',
|
|
|
+ align: 'center',
|
|
|
+ minWidth: 180,
|
|
|
+ showOverflowTooltip: true
|
|
|
+ }
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ visible: false,
|
|
|
+ loading: false,
|
|
|
+ planInfo: {
|
|
|
+ salesCode: ''
|
|
|
+ },
|
|
|
+ cardList: [],
|
|
|
+ treeProps: {
|
|
|
+ label: 'code',
|
|
|
+ children: 'children'
|
|
|
+ },
|
|
|
+ leftShow: false,
|
|
|
+ leftWidth: '0',
|
|
|
+ rightWidth: '100%',
|
|
|
+ datasourceAllList: [],
|
|
|
+ datasourceList: [],
|
|
|
+ planId: '',
|
|
|
+ selectNodeId: '',
|
|
|
+ produceType: 1,
|
|
|
+ selection: []
|
|
|
+ };
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
+ leftShow(newVal) {
|
|
|
+ if (newVal) {
|
|
|
+ this.leftWidth = '230px';
|
|
|
+ this.rightWidth = 'calc(100% - 240px)';
|
|
|
+ } else {
|
|
|
+ this.leftWidth = '0';
|
|
|
+ this.rightWidth = '100%';
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ async open(dataList, produceType) {
|
|
|
+ this.clearData();
|
|
|
+ this.produceType = produceType || 1;
|
|
|
+ this.visible = true;
|
|
|
+ this.cardList = Array.isArray(dataList) ? dataList : [];
|
|
|
+
|
|
|
+ this.$nextTick(() => {
|
|
|
+ if (this.$refs.table && this.$refs.table.reRenderTable) {
|
|
|
+ this.$refs.table.reRenderTable();
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ if (!this.cardList.length) return;
|
|
|
+
|
|
|
+ if (this.cardList.length === 1) {
|
|
|
+ this.leftShow = false;
|
|
|
+ } else {
|
|
|
+ this.leftShow = true;
|
|
|
+ const firstNodeKey = this.cardList[0].id;
|
|
|
+ this.$nextTick(() => {
|
|
|
+ if (this.$refs.treeRef) {
|
|
|
+ this.$refs.treeRef.setCurrentKey(firstNodeKey);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ await this.handleNodeClick(this.cardList[0]);
|
|
|
+ },
|
|
|
+ async handleNodeClick(data) {
|
|
|
+ this.planInfo = data || {};
|
|
|
+ this.selectNodeId = this.planInfo.id || '';
|
|
|
+ this.planId = this.planInfo.productionPlanId || this.planInfo.id;
|
|
|
+ await this.getMaterialData();
|
|
|
+ },
|
|
|
+ async getMaterialData() {
|
|
|
+ if (!this.planId) {
|
|
|
+ this.resultProcess([]);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.loading = true;
|
|
|
+ try {
|
|
|
+ const result = await getJNProductKitting({ planId: this.planId });
|
|
|
+ this.resultProcess(result);
|
|
|
+ } catch (err) {
|
|
|
+ this.$message.error(err.message || '获取齐套数据失败');
|
|
|
+ } finally {
|
|
|
+ this.loading = false;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ resultProcess(result) {
|
|
|
+ if (!Array.isArray(result) || result.length === 0) {
|
|
|
+ this.datasourceAllList = [];
|
|
|
+ this.datasourceList = [];
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.datasourceAllList = result.map((item, index) => ({
|
|
|
+ ...item,
|
|
|
+ jnKittingRowId: `${this.planId}-${index}`
|
|
|
+ }));
|
|
|
+ this.datasourceList = this.datasourceAllList;
|
|
|
+ },
|
|
|
+ displayValue(value) {
|
|
|
+ if (value === 0) return 0;
|
|
|
+ return value || '-';
|
|
|
+ },
|
|
|
+ formatQuantityWithUnit(value, unit) {
|
|
|
+ const quantity = this.displayValue(value);
|
|
|
+ if (quantity === '-') return quantity;
|
|
|
+ return unit ? `${quantity} ${unit}` : quantity;
|
|
|
+ },
|
|
|
+ getFinalStateClass(value) {
|
|
|
+ if (value == '缺料') return 'statusRed';
|
|
|
+ if (value == '齐套') return 'statusGreen';
|
|
|
+ return 'statusNeutral';
|
|
|
+ },
|
|
|
+ cancel() {
|
|
|
+ this.clearData();
|
|
|
+ this.visible = false;
|
|
|
+ },
|
|
|
+ clearData() {
|
|
|
+ this.planInfo = {
|
|
|
+ salesCode: ''
|
|
|
+ };
|
|
|
+ this.datasourceList = [];
|
|
|
+ this.datasourceAllList = [];
|
|
|
+ this.selection = [];
|
|
|
+ this.cardList = [];
|
|
|
+ this.planId = '';
|
|
|
+ this.selectNodeId = '';
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+ :deep(.client9-kitting-modal) {
|
|
|
+ margin: 3vh auto 0 !important;
|
|
|
+ height: 94vh;
|
|
|
+ max-height: 94vh;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.client9-kitting-modal .el-dialog__body) {
|
|
|
+ flex: 1;
|
|
|
+ min-height: 0;
|
|
|
+ background: #f6f8fb;
|
|
|
+ padding: 14px 24px 12px;
|
|
|
+ overflow: hidden;
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.client9-kitting-modal .el-dialog__footer) {
|
|
|
+ flex-shrink: 0;
|
|
|
+ padding: 10px 24px 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .client9-shell {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ height: 100%;
|
|
|
+ min-height: 0;
|
|
|
+ color: #1f2937;
|
|
|
+ }
|
|
|
+
|
|
|
+ .client9-overview {
|
|
|
+ display: flex;
|
|
|
+ align-items: stretch;
|
|
|
+ justify-content: space-between;
|
|
|
+ gap: 16px;
|
|
|
+ flex-shrink: 0;
|
|
|
+ padding: 14px 18px;
|
|
|
+ border: 1px solid #e5eef8;
|
|
|
+ border-radius: 8px;
|
|
|
+ background: linear-gradient(
|
|
|
+ 135deg,
|
|
|
+ rgba(37, 99, 235, 0.08),
|
|
|
+ transparent 38%
|
|
|
+ ),
|
|
|
+ linear-gradient(90deg, #ffffff, #f8fbff);
|
|
|
+ box-shadow: 0 10px 26px rgba(15, 23, 42, 0.06);
|
|
|
+ }
|
|
|
+
|
|
|
+ .overview-main {
|
|
|
+ position: relative;
|
|
|
+ min-width: 240px;
|
|
|
+ padding-left: 14px;
|
|
|
+
|
|
|
+ &::before {
|
|
|
+ position: absolute;
|
|
|
+ top: 2px;
|
|
|
+ bottom: 2px;
|
|
|
+ left: 0;
|
|
|
+ width: 4px;
|
|
|
+ border-radius: 4px;
|
|
|
+ background: linear-gradient(180deg, #2563eb, #14b8a6);
|
|
|
+ content: '';
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .overview-kicker {
|
|
|
+ color: #2563eb;
|
|
|
+ font-size: 13px;
|
|
|
+ font-weight: 600;
|
|
|
+ line-height: 20px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .overview-title {
|
|
|
+ margin-top: 4px;
|
|
|
+ color: #111827;
|
|
|
+ font-size: 22px;
|
|
|
+ font-weight: 700;
|
|
|
+ line-height: 30px;
|
|
|
+ overflow-wrap: anywhere;
|
|
|
+ }
|
|
|
+
|
|
|
+ .overview-code {
|
|
|
+ margin-top: 6px;
|
|
|
+ color: #6b7280;
|
|
|
+ font-size: 13px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .overview-metrics {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(4, minmax(112px, 1fr));
|
|
|
+ gap: 10px;
|
|
|
+ width: min(640px, 56%);
|
|
|
+ }
|
|
|
+
|
|
|
+ .metric-item {
|
|
|
+ min-height: 64px;
|
|
|
+ padding: 10px 14px;
|
|
|
+ border: 1px solid #e5edf5;
|
|
|
+ border-radius: 8px;
|
|
|
+ background: rgba(255, 255, 255, 0.86);
|
|
|
+ }
|
|
|
+
|
|
|
+ .metric-label {
|
|
|
+ display: block;
|
|
|
+ color: #6b7280;
|
|
|
+ font-size: 12px;
|
|
|
+ line-height: 18px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .metric-value {
|
|
|
+ display: block;
|
|
|
+ margin-top: 6px;
|
|
|
+ color: #111827;
|
|
|
+ font-size: 24px;
|
|
|
+ line-height: 28px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .metric-ready .metric-value {
|
|
|
+ color: #0f766e;
|
|
|
+ }
|
|
|
+
|
|
|
+ .metric-risk .metric-value {
|
|
|
+ color: #dc2626;
|
|
|
+ }
|
|
|
+
|
|
|
+ .metric-rate .metric-value {
|
|
|
+ color: #2563eb;
|
|
|
+ }
|
|
|
+
|
|
|
+ .client9-info-grid {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(6, minmax(130px, 1fr));
|
|
|
+ flex-shrink: 0;
|
|
|
+ gap: 8px;
|
|
|
+ margin: 10px 0 10px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .info-item {
|
|
|
+ min-height: 52px;
|
|
|
+ padding: 8px 12px;
|
|
|
+ border: 1px solid #e8edf3;
|
|
|
+ border-radius: 8px;
|
|
|
+ background: #fff;
|
|
|
+ }
|
|
|
+
|
|
|
+ .info-label {
|
|
|
+ display: block;
|
|
|
+ color: #7b8494;
|
|
|
+ font-size: 12px;
|
|
|
+ line-height: 18px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .info-value {
|
|
|
+ display: block;
|
|
|
+ margin-top: 4px;
|
|
|
+ color: #273142;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 600;
|
|
|
+ line-height: 20px;
|
|
|
+ overflow-wrap: anywhere;
|
|
|
+ }
|
|
|
+
|
|
|
+ .form-wrapper {
|
|
|
+ display: flex;
|
|
|
+ }
|
|
|
+
|
|
|
+ .result-insight {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: 160px minmax(220px, 1fr) repeat(4, minmax(96px, 1fr));
|
|
|
+ align-items: center;
|
|
|
+ gap: 12px;
|
|
|
+ flex-shrink: 0;
|
|
|
+ min-height: 66px;
|
|
|
+ margin-bottom: 10px;
|
|
|
+ padding: 12px 16px;
|
|
|
+ border: 1px solid #e5edf5;
|
|
|
+ border-radius: 8px;
|
|
|
+ background: #fff;
|
|
|
+ }
|
|
|
+
|
|
|
+ .result-status {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 4px;
|
|
|
+
|
|
|
+ strong {
|
|
|
+ font-size: 20px;
|
|
|
+ line-height: 24px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .result-label,
|
|
|
+ .progress-meta span,
|
|
|
+ .result-number span {
|
|
|
+ color: #7b8494;
|
|
|
+ font-size: 12px;
|
|
|
+ line-height: 18px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .result-progress {
|
|
|
+ min-width: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .progress-meta {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ margin-bottom: 8px;
|
|
|
+
|
|
|
+ strong {
|
|
|
+ color: #2563eb;
|
|
|
+ font-size: 18px;
|
|
|
+ line-height: 22px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .progress-track {
|
|
|
+ height: 8px;
|
|
|
+ overflow: hidden;
|
|
|
+ border-radius: 8px;
|
|
|
+ background: #e5eaf2;
|
|
|
+ }
|
|
|
+
|
|
|
+ .progress-bar {
|
|
|
+ height: 100%;
|
|
|
+ border-radius: inherit;
|
|
|
+ background: linear-gradient(90deg, #2563eb, #14b8a6);
|
|
|
+ transition: width 0.2s ease;
|
|
|
+ }
|
|
|
+
|
|
|
+ .result-numbers {
|
|
|
+ display: contents;
|
|
|
+ }
|
|
|
+
|
|
|
+ .result-number {
|
|
|
+ min-width: 0;
|
|
|
+ padding-left: 12px;
|
|
|
+ border-left: 1px solid #edf2f7;
|
|
|
+
|
|
|
+ strong {
|
|
|
+ display: block;
|
|
|
+ margin-top: 4px;
|
|
|
+ color: #273142;
|
|
|
+ font-size: 18px;
|
|
|
+ line-height: 22px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .result-ready {
|
|
|
+ border-color: #bfefe3;
|
|
|
+ background: linear-gradient(90deg, #f0fdfa, #ffffff 48%);
|
|
|
+
|
|
|
+ .result-status strong {
|
|
|
+ color: #0f766e;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .result-risk {
|
|
|
+ border-color: #fecdd3;
|
|
|
+ background: linear-gradient(90deg, #fff1f2, #ffffff 48%);
|
|
|
+
|
|
|
+ .result-status strong {
|
|
|
+ color: #dc2626;
|
|
|
+ }
|
|
|
+
|
|
|
+ .progress-bar {
|
|
|
+ background: linear-gradient(90deg, #f97316, #dc2626);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .result-neutral {
|
|
|
+ .result-status strong {
|
|
|
+ color: #64748b;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .table-panel {
|
|
|
+ flex: 1;
|
|
|
+ min-height: 0;
|
|
|
+ padding: 0;
|
|
|
+ border: 1px solid #e6edf5;
|
|
|
+ border-radius: 8px;
|
|
|
+ background: #fff;
|
|
|
+ overflow: hidden;
|
|
|
+ }
|
|
|
+
|
|
|
+ .detail-head {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ gap: 12px;
|
|
|
+ padding: 10px 14px;
|
|
|
+ border-bottom: 1px solid #edf2f7;
|
|
|
+ background: linear-gradient(180deg, #fbfdff, #f8fafc);
|
|
|
+ }
|
|
|
+
|
|
|
+ .detail-title {
|
|
|
+ color: #1f2937;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 700;
|
|
|
+ }
|
|
|
+
|
|
|
+ .detail-subtitle,
|
|
|
+ .detail-bom {
|
|
|
+ margin-left: 8px;
|
|
|
+ color: #8a94a6;
|
|
|
+ font-size: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .detail-bom {
|
|
|
+ margin-left: 0;
|
|
|
+ font-weight: 600;
|
|
|
+ }
|
|
|
+
|
|
|
+ .left-tree {
|
|
|
+ height: 100%;
|
|
|
+ max-height: 100%;
|
|
|
+ min-height: 0;
|
|
|
+ overflow: auto;
|
|
|
+ border-right: 1px solid #e6edf5;
|
|
|
+ background: #fbfdff;
|
|
|
+ margin-right: 10px;
|
|
|
+ padding: 10px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .status-pill {
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ min-width: 48px;
|
|
|
+ height: 24px;
|
|
|
+ padding: 0 10px;
|
|
|
+ border-radius: 12px;
|
|
|
+ font-size: 12px;
|
|
|
+ font-weight: 600;
|
|
|
+ }
|
|
|
+
|
|
|
+ .statusRed {
|
|
|
+ color: #dc2626;
|
|
|
+ background: #fff1f2;
|
|
|
+ border: 1px solid #fecdd3;
|
|
|
+ }
|
|
|
+
|
|
|
+ .statusGreen {
|
|
|
+ color: #0f766e;
|
|
|
+ background: #ecfdf5;
|
|
|
+ border: 1px solid #bbf7d0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .statusNeutral {
|
|
|
+ color: #64748b;
|
|
|
+ background: #f8fafc;
|
|
|
+ border: 1px solid #e2e8f0;
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.ele-pro-table .el-table th) {
|
|
|
+ background: #f8fafc;
|
|
|
+ color: #2f3a4a;
|
|
|
+ font-weight: 700;
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.ele-pro-table .el-table__row:hover > td) {
|
|
|
+ background: #f0f7ff;
|
|
|
+ }
|
|
|
+
|
|
|
+ @media (max-width: 1360px) {
|
|
|
+ .client9-overview {
|
|
|
+ flex-direction: column;
|
|
|
+ }
|
|
|
+
|
|
|
+ .overview-metrics {
|
|
|
+ width: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .client9-info-grid {
|
|
|
+ grid-template-columns: repeat(3, minmax(160px, 1fr));
|
|
|
+ }
|
|
|
+
|
|
|
+ .result-insight {
|
|
|
+ grid-template-columns: 150px minmax(200px, 1fr) repeat(
|
|
|
+ 2,
|
|
|
+ minmax(110px, 1fr)
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ .result-numbers {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(4, minmax(96px, 1fr));
|
|
|
+ grid-column: 1 / -1;
|
|
|
+ gap: 10px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @media (max-width: 900px) {
|
|
|
+ .overview-metrics,
|
|
|
+ .client9-info-grid {
|
|
|
+ grid-template-columns: repeat(2, minmax(140px, 1fr));
|
|
|
+ }
|
|
|
+
|
|
|
+ .result-insight {
|
|
|
+ grid-template-columns: 1fr;
|
|
|
+ }
|
|
|
+
|
|
|
+ .result-numbers {
|
|
|
+ grid-template-columns: repeat(2, minmax(120px, 1fr));
|
|
|
+ }
|
|
|
+ }
|
|
|
+</style>
|