Просмотр исходного кода

feat: 仓储首页看板配色与布局调整,移除待办及待我审批明细页

lihaiting 1 час назад
Родитель
Сommit
c4116869a2

+ 16 - 14
src/views/warehouseManagement/inboundRequests/components/search.vue

@@ -2,25 +2,25 @@
 <template>
   <el-form label-width="90px" class="ele-form-search" @keyup.enter.native="search" @submit.native.prevent>
     <el-row :gutter="15">
-      <el-col v-bind="styleResponsive ? { lg: 6, md: 10 } : { span: 6 }">
-        <el-form-item label="所属工厂" prop="factoryId">
+      <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
+        <el-form-item label="所属工厂:" prop="factoryId">
           <el-select filterable placeholder="请选择" v-model="where.factoryId" clearable class="w100">
             <el-option v-for="item in factoryList" :key="item.id" :label="item.name" :value="item.id"></el-option>
           </el-select>
         </el-form-item>
       </el-col>
-      <el-col v-if="activeName != '出入库明细表'" v-bind="styleResponsive ? { lg: 6, md: 10 } : { span: 6 }">
+      <el-col v-if="activeName != '出入库明细表'" v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
         <el-form-item label="编码:">
           <el-input clearable v-model="where.code" placeholder="请输入" />
         </el-form-item>
       </el-col>
-      <el-col v-bind="styleResponsive ? { lg: 6, md: 10 } : { span: 6 }">
+      <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
         <el-form-item label="申请日期:">
           <el-date-picker v-model="where.time" type="daterange" value-format="yyyy-MM-dd"
             range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" class="w100" />
         </el-form-item>
       </el-col>
-      <el-col v-bind="styleResponsive ? { lg: 6, md: 10 } : { span: 6 }">
+      <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
         <el-form-item label="状态:">
           <el-select v-model="where.status" placeholder="全部" clearable class="w100">
             <el-option :value="0" label="待处理" />
@@ -30,19 +30,19 @@
         </el-form-item>
       </el-col>
 
-      <el-col v-bind="styleResponsive ? { lg: 6, md: 10 } : { span: 6 }">
+      <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
         <el-form-item label="物品编码:">
           <el-input clearable v-model="where.categoryCode" placeholder="请输入" />
         </el-form-item>
       </el-col>
 
-      <el-col v-bind="styleResponsive ? { lg: 6, md: 10 } : { span: 6 }">
+      <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
         <el-form-item label="物品名称:">
           <el-input clearable v-model="where.categoryName" placeholder="请输入" />
         </el-form-item>
       </el-col>
 
-      <!-- <el-col v-bind="styleResponsive ? { lg: 6, md: 10 } : { span: 6 }">
+      <!-- <el-col v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
         <el-form-item label="组织机构:">
           <auth-selection
             v-model="where.deptIds"
@@ -50,12 +50,12 @@
           ></auth-selection>
         </el-form-item>
       </el-col> -->
-      <el-col v-if="activeName == '出入库明细表'" v-bind="styleResponsive ? { lg: 6, md: 10 } : { span: 6 }">
+      <el-col v-if="activeName == '出入库明细表'" v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
         <el-form-item label="单号:">
           <el-input v-model="where.bizNo" placeholder="请输入内容"></el-input>
         </el-form-item>
       </el-col>
-      <el-col v-if="activeName == '出入库明细表'" v-bind="styleResponsive ? { lg: 6, md: 10 } : { span: 6 }">
+      <el-col v-if="activeName == '出入库明细表'" v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
         <el-form-item label="类型:">
           <el-select style="width: 100%" v-model="where.type" placeholder="请选择">
             <el-option v-for="item in typeOption" :label="item.label" :value="item.value" :key="item.value">
@@ -63,16 +63,18 @@
           </el-select>
         </el-form-item>
       </el-col>
-      <el-col v-if="activeName == '出入库明细表'" v-bind="styleResponsive ? { lg: 6, md: 10 } : { span: 6 }">
+      <el-col v-if="activeName == '出入库明细表'" v-bind="styleResponsive ? { lg: 6, md: 12 } : { span: 6 }">
         <el-form-item label="出入库时间:">
           <el-date-picker style="width: 100%" size="small" v-model="where.time" type="daterange" range-separator="至"
             start-placeholder="开始日期" end-placeholder="结束日期" value-format="yyyy-MM-dd"></el-date-picker>
         </el-form-item>
       </el-col>
-      <el-col style="display: flex; justify-content: flex-end" v-bind="styleResponsive
-        ? { lg: activeName == '出入库明细表' ? 24 : 18, md: 24 }
-        : { span: activeName == '出入库明细表' ? 24 : 18 }
+      <el-col style="display: flex; justify-content: flex-end; align-items: flex-start" v-bind="styleResponsive
+        ? { lg: 12, md: 12 }
+        : { span: 12 }
         ">
+        <!-- 按钮列 lg:12:与第二行的 物品编码/物品名称(各 lg:6)正好拼满一行、按钮贴右;
+             原先 lg:18 在第二行放不下(6+6+18>24),整列被挤到第三行导致布局散乱。 -->
         <el-form-item label-width="0px">
           <el-button type="primary" icon="el-icon-search" class="ele-btn-icon" @click="search">
             查询

+ 1 - 1
src/views/warehouseManagement/index/components/DashboardActions.vue

@@ -281,5 +281,5 @@ export default {
 </script>
 <style scoped>
 /* 「默认库区,请核对」:库区是按仓库自动取第一个得来,必须显眼到用户不敢直接用默认值提交。 */
-.dashboard-default-area { margin-left: 8px; padding: 1px 8px; border-radius: 2px; font-size: 12px; color: #F0A03A; background: rgba(240, 160, 58, .16); border: 1px solid rgba(240, 160, 58, .5); }
+.dashboard-default-area { margin-left: 8px; padding: 1px 8px; border-radius: 2px; font-size:13px; color: #ffb020; background: rgba(255, 176, 32, .16); border: 1px solid rgba(255, 176, 32, .5); }
 </style>

+ 23 - 15
src/views/warehouseManagement/index/components/DashboardChart.vue

@@ -2,6 +2,12 @@
 <script>
 import * as echarts from 'echarts';
 import { isIdleTrendDay, trendQuantity, agingColorOf, formatAgingRatio, isAgingUnknown } from '../dashboard-utils';
+// HR 首页同款图表配色(hr/src/views/homeView/index.vue 调色板):
+// 荧光青主色 + 青/蓝/紫/橙语义扩展,深底下的发光感靠柱体渐变与 tooltip 描边实现。
+const CHART_COLORS = ['#00eaff', '#4fd1c5', '#3d8bff', '#a78bfa', '#ffb020', '#ff7a59', '#7af6ff', '#f5d76e'];
+// HR 同款深色 tooltip:近黑蓝底 + 青色描边 + 亮青文字。
+const TOOLTIP_STYLE = { backgroundColor: 'rgba(4,18,36,.94)', borderColor: 'rgba(0,210,255,.35)', borderWidth: 1, textStyle: { color: '#e8f7ff' } };
+const MUTED = '#8fc4de';
 export default {
   name: 'WarehouseDashboardChart',
   props: { kind: String, rows: { type: Array, default: () => [] }, unit: { type: String, default: '' } },
@@ -88,20 +94,22 @@ export default {
           '单日净差:' + signed(netOf(row)) + unitSuffix].join('<br/>');
       };
       return {
-        color: ['#38C8F5', '#F0A03A', '#9B7DF7'],
-        tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' }, formatter: tooltipFormat },
+        color: ['#00eaff', '#ffb020', '#a78bfa'],
+        tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' }, formatter: tooltipFormat, ...TOOLTIP_STYLE },
         // 右侧留出净差轴名位置,别让「单日净差」压在图上。
         grid: { top: 34, right: 62, bottom: 28, left: 62 },
         xAxis: { ...axis, type: 'category', data: rows.map((row) => row.date) },
         yAxis: [
-          { ...axis, type: 'value', name: '数量' + unitSuffix, nameTextStyle: { color: '#9FB8D4' } },
-          { ...axis, type: 'value', name: '单日净差' + unitSuffix, position: 'right', nameTextStyle: { color: '#9B7DF7' }, splitLine: { show: false }, axisLabel: { ...axis.axisLabel, formatter: (value) => (value > 0 ? '+' : '') + value } }
+          { ...axis, type: 'value', name: '数量' + unitSuffix, nameTextStyle: { color: MUTED } },
+          { ...axis, type: 'value', name: '单日净差' + unitSuffix, position: 'right', nameTextStyle: { color: '#a78bfa' }, splitLine: { show: false }, axisLabel: { ...axis.axisLabel, formatter: (value) => (value > 0 ? '+' : '') + value } }
         ],
         series: [
-          { name: '入库量', type: 'bar', yAxisIndex: 0, barMaxWidth: 10, data: rows.map(inboundOf), itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: '#38C8F5' }, { offset: 1, color: 'rgba(46,139,232,.25)' }]) }, markArea: { silent: true, itemStyle: { color: 'rgba(159,184,212,.12)' }, label: { show: false }, data: idleAreas } },
-          { name: '出库量', type: 'bar', yAxisIndex: 0, barMaxWidth: 10, itemStyle: { color: '#F0A03A' }, data: rows.map(outboundOf) },
-          // 净趋势:紫色虚线 #9B7DF7,绑右轴「单日净差」,无作业日断线不留 0 点。
-          { name: '净趋势', type: 'line', yAxisIndex: 1, smooth: true, symbol: 'none', connectNulls: false, itemStyle: { color: '#9B7DF7' }, lineStyle: { type: 'dashed', width: 2, color: '#9B7DF7' }, data: rows.map(netOf) },
+          // 入库柱:HR 入职趋势同款青色渐变 + 发光。
+          { name: '入库量', type: 'bar', yAxisIndex: 0, barMaxWidth: 12, data: rows.map(inboundOf), itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: '#7af6ff' }, { offset: 1, color: 'rgba(10,95,158,.35)' }]), shadowBlur: 8, shadowColor: 'rgba(0,230,255,.45)' }, markArea: { silent: true, itemStyle: { color: 'rgba(159,184,212,.12)' }, label: { show: false }, data: idleAreas } },
+          // 出库柱:HR 离职趋势同款橙色渐变 + 发光。
+          { name: '出库量', type: 'bar', yAxisIndex: 0, barMaxWidth: 12, itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, color: '#ffe29a' }, { offset: 1, color: 'rgba(197,106,0,.55)' }]), shadowBlur: 8, shadowColor: 'rgba(255,176,32,.4)' }, data: rows.map(outboundOf) },
+          // 净趋势:HR 紫 #a78bfa 虚线,绑右轴「单日净差」,无作业日断线不留 0 点。
+          { name: '净趋势', type: 'line', yAxisIndex: 1, smooth: true, symbol: 'none', connectNulls: false, itemStyle: { color: '#a78bfa' }, lineStyle: { type: 'dashed', width: 2, color: '#a78bfa' }, data: rows.map(netOf) },
           // 无作业占位系列:只为让空日在轴触发 tooltip 里有可命中的 dataIndex。
           // silent + opacity 0 = 看不见、也不拦截 zr 点击下钻;提示文案由 formatter 的空日分支接管,
           // 占位值 0 绝不会出现在 tooltip 里。
@@ -132,16 +140,16 @@ export default {
         ].join('<br/>');
       };
       return {
-        tooltip: { trigger: 'item', formatter: agingTooltip },
+        tooltip: { trigger: 'item', formatter: agingTooltip, ...TOOLTIP_STYLE },
         // 图例用后端 label(含「>180天」),不在这里改写分段名。
-        legend: { bottom: 0, textStyle: { color: '#9FB8D4' }, data: rows.map(nameOf) },
-        title: { text: numberText(rows.reduce((sum, row) => sum + Number(row.quantity || 0), 0)), subtext: '在库总量', left: 'center', top: '38%', textStyle: { color: '#EAF4FF', fontSize: 20 }, subtextStyle: { color: '#9FB8D4' } },
+        legend: { bottom: 0, textStyle: { color: MUTED, fontSize: 13 }, data: rows.map(nameOf) },
+        title: { text: numberText(rows.reduce((sum, row) => sum + Number(row.quantity || 0), 0)), subtext: '在库总量', left: 'center', top: '38%', textStyle: { color: '#e4f6ff', fontSize: 20 }, subtextStyle: { color: MUTED } },
         series: [{
           type: 'pie',
           radius: ['48%', '69%'],
           center: ['50%', '45%'],
           label: { show: false },
-          itemStyle: { borderColor: '#0A1830', borderWidth: 2 },
+          itemStyle: { borderColor: '#031428', borderWidth: 2 },
           // 颜色按分段 key 取:known 段顺序色、>180 段固定红、unknown 段中性灰。
           // 点击下钻沿用 mounted 里的 chart.on('click'):dataIndex 与 this.rows 一一对应,行上的 key 原样带出。
           data: rows.map((row) => ({ name: nameOf(row), value: row.quantity, itemStyle: { color: agingColorOf(row) } }))
@@ -150,8 +158,8 @@ export default {
     },
     render() {
       if (!this.chart) return;
-      const color = ['#38C8F5', '#F0A03A', '#9B7DF7', '#3BD48A', '#F25E5E', '#7E93AD', '#2E8BE8', '#D9C96C'];
-      const axis = { axisLine: { lineStyle: { color: '#345573' } }, axisLabel: { color: '#9FB8D4', fontSize: 11 }, splitLine: { lineStyle: { color: 'rgba(80,150,220,.12)' } } };
+      const color = CHART_COLORS;
+      const axis = { axisLine: { lineStyle: { color: 'rgba(0,210,255,.4)' } }, axisLabel: { color: MUTED, fontSize: 13 }, splitLine: { lineStyle: { color: 'rgba(0,190,255,.14)' } } };
       let option;
       if (this.kind === 'trend') {
         option = this.trendOption(axis);
@@ -172,7 +180,7 @@ export default {
           const ratio = row.ratio == null || row.ratio === '' ? '—' : String(row.ratio) + '%';
           return [row.name, '在库数量:' + numberText(row.quantity) + unitSuffix, '占比:' + ratio, '点击进入台账'].join('<br/>');
         };
-        option = { color, tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' }, formatter: categoryTooltip }, grid: { top: 12, right: 55, bottom: 22, left: 85 }, xAxis: { ...axis, type: 'value' }, yAxis: { ...axis, type: 'category', inverse: true, data: this.rows.map((r) => r.name) }, series: [{ name: '在库量', type: 'bar', barMaxWidth: 16, label: { show: true, position: 'right', color: '#9FB8D4', formatter: (p) => (this.rows[p.dataIndex].ratio == null ? '' : this.rows[p.dataIndex].ratio + '%') }, data: this.rows.map((r, i) => ({ value: r.quantity, itemStyle: { color: color[i % color.length], borderRadius: [0, 4, 4, 0] } })) }] };
+        option = { color, tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' }, formatter: categoryTooltip, ...TOOLTIP_STYLE }, grid: { top: 12, right: 55, bottom: 22, left: 85 }, xAxis: { ...axis, type: 'value' }, yAxis: { ...axis, type: 'category', inverse: true, data: this.rows.map((r) => r.name) }, series: [{ name: '在库量', type: 'bar', barMaxWidth: 16, label: { show: true, position: 'right', color: MUTED, formatter: (p) => (this.rows[p.dataIndex].ratio == null ? '' : this.rows[p.dataIndex].ratio + '%') }, data: this.rows.map((r, i) => ({ value: r.quantity, itemStyle: { color: color[i % color.length], shadowBlur: 8, shadowColor: 'rgba(0,200,255,.35)', borderRadius: [0, 0, 0, 0] } })) }] };
       } else {
         option = this.agingOption();
       }

+ 90 - 64
src/views/warehouseManagement/index/components/HomeOverview.vue

@@ -1,28 +1,39 @@
 <template>
 <div class="view active">
           <div class="screen-band">
-            <div class="en">Warehouse Data Center</div>
-            <h1>仓储管理总览</h1>
-            <div class="meta"><span>统计月份:<b>{{ monthLabel }}</b></span><span>服务端查询时间:<b>{{ summary.capturedAt || '尚未更新' }}</b></span></div>
+            <div class="band-wing left"><i></i><i></i><i class="dm"></i></div>
+            <div class="band-frame">
+              <span class="band-shine"></span>
+              <span class="band-deco left"></span>
+              <span class="band-deco right"></span>
+              <h1>仓储管理总览</h1>
+            </div>
+            <div class="band-wing right"><i></i><i></i><i class="dm"></i></div>
+            <div class="meta"><span class="live-dot"></span><span>统计月份:<b>{{ monthLabel }}</b></span><span>服务端查询时间:<b>{{ summary.capturedAt || '尚未更新' }}</b></span><span class="fs-btn" role="button" tabindex="0" :title="isFullscreen ? '退出全屏(ESC)' : '全屏显示'" @click="toggleFullscreen" @keydown.enter="toggleFullscreen">{{ isFullscreen ? '✕' : '⛶' }}</span></div>
           </div>
           <div class="greet"><div><h2>{{ greeting }}{{ userName ? ',' + userName : '' }}</h2><p>今天是 {{ todayLabel }} · 数据更新于 {{ updatedAt || '尚未更新' }}(统计每 15 分钟、预警每 5 分钟、动态每 30 秒刷新)</p></div><div class="home-filters"><label>仓库 <select :value="warehouseId" @change="$emit('warehouse', $event.target.value)"><option value="">全部授权仓库</option><option v-for="item in options.warehouses || []" :key="item.id" :value="item.id">{{ item.name }}</option></select></label><label>基本单位 <select :value="unit" @change="$emit('unit', $event.target.value)"><option value="">全部单位</option><option v-for="item in options.units || []" :key="item.unit" :value="item.unit">{{ item.unit }}</option></select></label></div></div>
           <div v-if="error" class="data-note bad" role="alert">{{ error }} <button type="button" @click="$emit('refresh')">重试</button></div>
           <div class="todo-bar">
-            <div v-for="(item, i) in todoCards" :key="item.key" class="todo-item" role="button" tabindex="0" @click="$emit('open', item.target, item.filters || {})" @keydown.enter="$emit('open', item.target, item.filters || {})">
+            <!-- 待入库/待出库单两张卡可点击:直接跳左侧菜单「出入库申请」的真实路由(warehouseManagement/inboundRequests,独立页签),
+                 不再走首页内嵌 ioapp 视图;direction 决定目标页入库/出库页签,仓库沿用当前筛选。
+                 盘点/审批两张是纯展示:无 target 就不渲染 button 语义、不占焦点、不绑点击,箭头也不显示,CSS 的指针/悬浮效果随 .static 关闭。 -->
+            <div v-for="(item, i) in todoCards" :key="item.key" class="todo-item" :class="{ static: !item.target }" :role="item.target ? 'button' : null" :tabindex="item.target ? 0 : null" @click="item.target && goIoRequests(item)" @keydown.enter="item.target && goIoRequests(item)">
               <div class="todo-icon" :class="'icon-' + item.icon"><span>{{ item.symbol }}</span></div>
               <div><div class="todo-num">{{ display(item.value) }} <span v-if="i < 2" class="badge-today">本月</span><span v-if="item.overdue" class="badge-overdue">{{ item.overdue }} 单超时</span></div><div class="todo-label">{{ item.label }}</div></div>
-              <span class="todo-arrow">›</span>
+              <span v-if="item.target" class="todo-arrow">›</span>
             </div>
           </div>
           <div class="kpi-row">
             <div v-for="item in kpis" :key="item.key" class="card kpi corner" role="button" tabindex="0" @click="$emit('open', item.target, item.filters || {})" @keydown.enter="$emit('open', item.target, item.filters || {})">
               <span class="idx">{{ item.code }}</span><span class="kpi-go">查看明细 ›</span>
-              <div class="lb">{{ item.label }} <span class="q" :title="item.info">?</span></div>
-              <div class="val" :style="{ color: item.color }">{{ displayKpi(item) }}<small v-if="item.value != null">{{ item.unit }}</small></div>
-              <div :class="'delta ' + (item.deltaTone || 'dim')" :title="item.deltaTitle || item.reason">{{ item.delta || item.status || item.reason || '数据待核实' }}</div>
-              <div v-if="item.subtotal" class="delta dim" :title="item.subtotal.title">{{ item.subtotal.text }}</div>
-              <div v-if="item.locked" class="delta dim" :title="item.locked.title">{{ item.locked.text }}</div>
-              <div v-if="item.progress != null" class="bar-mini"><i :style="{ width: item.progress + '%', background: item.color, color: item.color }"></i></div>
+              <div class="kpi-ico" :style="{ color: item.color }"><i></i><span>{{ item.symbol }}</span></div>
+              <div class="kpi-copy">
+                <div class="lb">{{ item.label }}</div>
+                <div class="val" :style="{ color: item.color }">{{ displayKpi(item) }}<small v-if="item.value != null">{{ item.unit }}</small></div>
+                <!-- 环比/状态是真数据才渲染;reason 只是口径解释,随「?」帮助圈一并去掉,缺数时卡片留白 -->
+                <div v-if="item.delta || item.status" :class="'delta ' + (item.deltaTone || 'dim')" :title="item.deltaTitle">{{ item.delta || item.status }}</div>
+                <div v-if="item.progress != null" class="bar-mini"><i :style="{ width: item.progress + '%', background: item.color, color: item.color }"></i></div>
+              </div>
             </div>
           </div>
           <div class="grid-2-1">
@@ -30,8 +41,6 @@
               <div class="card-hd">
                 <div>
                   <h3>出入库动态</h3>
-                  <!-- 窗口口径必须显式写「近 30 天滚动窗口」并给出起止日期,避免被当成自然月或固定周期。 -->
-                  <div class="sub">近 30 天滚动窗口(含今日 {{ trendWindow.start }} ~ {{ trendWindow.end }})· 净趋势=当日入库−当日出库 · 右轴单日净差</div>
                 </div>
                 <div style="display:flex;align-items:center;gap:8px">
                   <!-- 连续同号风险:连续为正=积压风险(橙)、连续为负=缺货风险(红);不足 2 天不渲染。 -->
@@ -45,29 +54,22 @@
               </div>
               <div class="chart-body">
                 <div class="legend">
-                  <span><i style="background:#38C8F5"></i>入库量</span>
-                  <span><i style="background:#F0A03A"></i>出库量</span>
-                  <span><i style="background:#9B7DF7;height:2px"></i>净趋势(单日净差,右轴)</span>
+                  <span><i style="background:#00eaff"></i>入库量</span>
+                  <span><i style="background:#ffb020"></i>出库量</span>
+                  <span><i style="background:#a78bfa;height:2px"></i>净趋势(单日净差,右轴)</span>
                   <span><i style="background:rgba(159,184,212,.35)"></i>该日无作业</span>
                 </div>
                 <dashboard-chart v-if="trend.length" kind="trend" :rows="trend" :unit="unit" @select="trendSelect" />
                 <div v-else class="empty-chart">{{ trendReason || '暂无已完成出入库流水' }}</div>
               </div>
             </section>
-            <section class="card corner"><div class="card-hd"><div><h3>库存分类分布</h3><div class="sub">按物料分类 TOP8(前 7 + 其他兜底)· 按基本单位</div></div><a class="link" href="#" @click.prevent="$emit('open', 'stockLedger')">查看台账 ›</a></div><div class="chart-body"><dashboard-chart v-if="categories.length" kind="category" :rows="categories" :unit="unit" @select="categorySelect" /><div v-else class="empty-chart">{{ categoryReason || '当前筛选无可追溯库存' }}</div></div></section>
+            <section class="card corner"><div class="card-hd"><div><h3>库存分类分布</h3></div><a class="link" href="#" @click.prevent="$emit('open', 'stockLedger')">查看台账 ›</a></div><div class="chart-body"><dashboard-chart v-if="categories.length" kind="category" :rows="categories" :unit="unit" @select="categorySelect" /><div v-else class="empty-chart">{{ categoryReason || '当前筛选无可追溯库存' }}</div></div></section>
           </div>
           <div class="grid-3">
             <section class="card corner">
               <div class="card-hd">
                 <div>
                   <h3>库龄结构</h3>
-                  <div class="sub">
-                    在库批次按库龄分段 · 与呆滞口径联动 · 平均库龄 {{ averageAge.text }}
-                    <!-- 较上月角标:averageAgeChange 是天数差,负数=平均库龄下降=库存更“新”,向好走绿;
-                         正数=库龄变长,恶化走红;null=无基准,只渲染后端原因,绝不显示成 0 天。 -->
-                    <span v-if="averageAgeChange.text" class="tag aging-delta" :class="averageAgeChange.tone === 'good' ? 'g' : averageAgeChange.tone === 'bad' ? 'r' : 'gr'" :title="averageAgeChange.title">{{ averageAgeChange.text }}</span>
-                    <span v-else class="aging-delta-note" :title="averageAgeChange.title">{{ averageAgeChange.title }}</span>
-                  </div>
                 </div>
                 <a class="link" href="#" @click.prevent="$emit('open', 'age')">明细 ›</a>
               </div>
@@ -84,21 +86,16 @@
                     <i :style="{ background: agingUnknown.color }"></i>{{ agingUnknown.label }} {{ agingUnknown.ratioText }}
                   </span>
                 </div>
-                <!-- 含 unknown 段或不可出图时后端给 agingReason,必须展示:否则「日期待核实」会被读成 0 库存。 -->
-                <div v-if="agingReason" class="aging-note">{{ agingReason }}</div>
               </div>
             </section>
             <section class="card corner">
               <div class="card-hd">
                 <div>
                   <h3>安全库存预警</h3>
-                  <!-- 三色口径写在卡面上:缺料=可用量<安全库存 / 偏低=安全库存≤可用量<安全库存×偏低系数(默认 1.2)/
-                       超储=可用量>最大库存(最大库存为空时按 安全库存×超储兜底系数,默认 3)。判定与系数都由后端下发,前端只展示。 -->
-                  <div class="sub">缺料 / 偏低 / 超储 三色分级 · 缺口金额倒序 TOP{{ alertTopLimit }} · 准实时(≤5 分钟)</div>
                 </div>
                 <a class="link" href="#" @click.prevent="openAlertAll()">全部 {{ alertSummary.countText }} 项 ›</a>
               </div>
-              <div v-if="alerts.length">
+              <div v-if="alerts.length" class="card-scroll">
                 <!-- 行内:状态点(配色取后端 color)+ 物料名 + 状态中文标签 + 缺口量/缺口金额(缺价显示「金额待补价」,绝不显示 0)。
                      行尾补「已生成申请 单号」标记,用于识别已经下过采购申请的行(禁用再次发起补货的视觉态由接口动作层负责)。 -->
                 <div v-for="item in alerts" :key="item.id" class="alert-item" role="button" tabindex="0" :title="alertRowTitle(item)" @click="alertSelect(item)" @keydown.enter="alertSelect(item)">
@@ -128,10 +125,10 @@
                  不移动 DOM、不调用任何滚动 API。 -->
             <section class="card corner">
               <div class="card-hd">
-                <div><h3>最近作业动态</h3><div class="sub">出入库 / 调拨 / 盘点流水</div></div>
+                <div><h3>最近作业动态</h3></div>
                 <a class="link" href="#" @click.prevent="$emit('open', 'io')">全部动态 ›</a>
               </div>
-              <div v-if="activities.length" class="feed">
+              <div v-if="activities.length" class="feed card-scroll">
                 <div
                   v-for="item in activities"
                   :key="item.id"
@@ -170,7 +167,7 @@ import { displayNumber, formatSignedNumber, formatComparisonBase, formatPpChange
 // 未识别的状态或 null 一律回落中性色 #9FB8D4 且状态文案留空,避免把「算不出来」误报成健康。
 const TURNOVER_STATUS_STYLE = {
   '达标': { color: '#3BD48A', text: '达标' },
-  '超目标': { color: '#F0A03A', text: '超目标' },
+  '超目标': { color: '#ffb020', text: '超目标' },
   '偏慢': { color: '#F25E5E', text: '周转偏慢' }
 };
 // 后端未给状态(字段缺失或不可计算)时的取值色:中性蓝,不表达健康度。
@@ -195,7 +192,7 @@ const parseBackendDate = (value) => {
 // 前端既取不到该快照,也不该复制一份阈值——仓库改阈值或走多仓汇总后,前端自算必然与口径分叉。
 const STAGNANT_LEVEL_STYLE = {
   normal: { color: '#3BD48A', text: '正常' },
-  near: { color: '#F0A03A', text: '接近阈值' },
+  near: { color: '#ffb020', text: '接近阈值' },
   alert: { color: '#F25E5E', text: '呆滞预警' }
 };
 // 后端未给分级(字段缺失、分母不可核实或阈值未配置)时的取值色:中性蓝,不表达健康度。
@@ -208,7 +205,7 @@ const formatTurnoverDays = (value) => (value == null ? '—' : Number(value).toL
 // 判定边界(98、95 含等于)与三条文案逐位不变,不引入新的判定依据。
 const QUALITY_LEVEL_STYLE = {
   pass: { color: '#3BD48A', text: '达标' },
-  near: { color: '#F0A03A', text: '接近阈值' },
+  near: { color: '#ffb020', text: '接近阈值' },
   fail: { color: '#F25E5E', text: '低于目标' }
 };
 // 后端没给通过率(qualityPassRate 为 null,例如 qualityInspectedCount 为 0)时的取值色:
@@ -255,7 +252,8 @@ export default {
   // TOP 上限只用于卡片标题文案(TOP5),取 dashboard-utils 的同一常量,避免文案与截断条数各写一个数字。
   // alertTopLimit 只用于 F1 卡片标题文案;E3 两个状态放这里:newActivityIds 必须声明在 data 里才是响应式的,
   // highlightTimer 存 3 秒渐隐的定时器句柄,供 beforeDestroy 清理。
-  data() { return { alertTopLimit: ALERT_TOP_LIMIT, newActivityIds: [], highlightTimer: null }; },
+  // isFullscreen:标题带全屏按钮的状态(fullscreenchange 是唯一权威来源——ESC 退出也要能同步图标)。
+  data() { return { alertTopLimit: ALERT_TOP_LIMIT, newActivityIds: [], highlightTimer: null, isFullscreen: false }; },
   computed: {
     // D1 出入库动态:窗口=近 30 天滚动(含今日),后端只返回有作业的日期,
     // 缺的日期补 null 而不是 0;无作业判定与净差口径全部收在 dashboard-utils 的纯函数里,
@@ -363,7 +361,8 @@ export default {
       if (!comparable) {
         const reason = s.comparisonReason || '暂无上周同时点可比快照';
         homeLog('库存总量周环比不可比', { weekOverWeekQuantity: diff, comparisonBaseAt: s.comparisonBaseAt, comparisonReason: s.comparisonReason });
-        return { text: reason, title: s.comparisonReason || reason };
+        // 用户要求:不可比的原因文案不再显示在卡片上,delta 留空(控制台日志保留供排查)。
+        return { text: '', title: reason };
       }
       const unit = String(s.weekOverWeekUnit || this.unit || '').trim();
       const signed = formatSignedNumber(diff);
@@ -407,7 +406,8 @@ export default {
       if (!comparable) {
         const reason = s.inventoryValueComparisonReason || '暂无上周同时点可比成本快照';
         homeLog('库存金额周环比不可比', { inventoryValueChangeRate: rate, comparisonBaseAt: s.comparisonBaseAt, inventoryValueComparisonReason: s.inventoryValueComparisonReason });
-        return { text: reason, title: s.inventoryValueComparisonReason || reason };
+        // 用户要求:不可比的原因文案不再显示在卡片上,delta 留空(控制台日志保留供排查)。
+        return { text: '', title: reason };
       }
       const value = Number(rate);
       const text = '较上周' + (value === 0 ? '持平' : ' ' + (value > 0 ? '+' : '-') + Math.abs(value).toFixed(2) + '%')
@@ -435,8 +435,9 @@ export default {
       let deltaTitle = '';
       let deltaTone = 'dim';
       if (change == null) {
-        delta = s.turnoverChangeReason ? String(s.turnoverChangeReason) : '';
-        deltaTitle = delta || '上月无可比日末快照,不展示「较上月」差值';
+        // 用户要求:无可比快照的原因文案不显示在卡片上,delta 留空。
+        delta = '';
+        deltaTitle = s.turnoverChangeReason ? String(s.turnoverChangeReason) : '上月无可比日末快照,不展示「较上月」差值';
       } else {
         const signed = formatSignedNumber(change);
         delta = '较上月' + (signed === '持平' ? '持平' : ' ' + signed + ' 天');
@@ -508,9 +509,10 @@ export default {
       let deltaTitle = '';
       let deltaTone = 'dim';
       if (change == null) {
-        delta = s.stagnantChangeReason || s.stagnantReason || '数据待核实';
-        deltaTitle = delta;
-        homeWarn('呆滞品环比不可用', { stagnantChange: s.stagnantChange, stagnantBaseDate: s.stagnantBaseDate, stagnantChangeReason: s.stagnantChangeReason, stagnantReason: s.stagnantReason, text: delta });
+        // 用户要求:不可比原因文案不显示在卡片上,delta 留空(homeWarn 日志保留供排查)。
+        delta = '';
+        deltaTitle = s.stagnantChangeReason || s.stagnantReason || '数据待核实';
+        homeWarn('呆滞品环比不可用', { stagnantChange: s.stagnantChange, stagnantBaseDate: s.stagnantBaseDate, stagnantChangeReason: s.stagnantChangeReason, stagnantReason: s.stagnantReason });
       } else {
         const formatted = formatPpChange(change);
         // 持平不拼数值;基准快照日期缺失时省略括号,日期本身不做任何补造。
@@ -569,9 +571,10 @@ export default {
       let deltaTitle = '';
       let deltaTone = 'dim';
       if (change == null) {
-        delta = '—';
+        // 用户要求:不可比原因不显示在卡片上(之前显示「—」),delta 留空。
+        delta = '';
         deltaTitle = s.qualityChangeReason ? String(s.qualityChangeReason) : '较上月不可比(上月无已检批次或口径不可比)';
-        homeWarn('质检通过率环比不可比', { qualityPassRate: s.qualityPassRate, qualityInspectedCount: s.qualityInspectedCount, qualityChange: s.qualityChange, qualityChangeReason: s.qualityChangeReason, text: deltaTitle });
+        homeWarn('质检通过率环比不可比', { qualityPassRate: s.qualityPassRate, qualityInspectedCount: s.qualityInspectedCount, qualityChange: s.qualityChange, qualityChangeReason: s.qualityChangeReason });
       } else {
         const formatted = formatPpChange(change);
         // ▲/▼ 是上升/下降的显式标识(颜色仍由 deltaTone 决定);持平不挂箭头。
@@ -611,24 +614,25 @@ export default {
     monthLabel() { return new Intl.DateTimeFormat('zh-CN', { year: 'numeric', month: '2-digit' }).format(new Date()); },
     todayLabel() { return new Intl.DateTimeFormat('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', weekday: 'long' }).format(new Date()); },
     greeting() { const hour = new Date().getHours(); return hour < 11 ? '上午好' : hour < 18 ? '下午好' : '晚上好'; },
-    // 待办条口径与下钻目标(业务已确认,与后端 WmsHomeSummaryVO 逐条对齐):
-    // 1 待入库单=入库单状态 ∈ {草稿,待审批,待收货,待上架} 且本月创建、按数据权限过滤 → V13 出入库申请(入库页签)
-    // 2 待出库单=出库单状态 ∈ {草稿,待审批,待拣货} 且本月创建、按数据权限过滤 → V13 出入库申请(出库页签)
+    // 待办条口径与跳转目标(业务已确认,与后端 WmsHomeSummaryVO 逐条对齐):
+    // 1 待入库单=入库单状态 ∈ {草稿,待审批,待收货,待上架} 且本月创建、按数据权限过滤 → 左侧菜单「出入库申请」(入库页签)
+    // 2 待出库单=出库单状态 ∈ {草稿,待审批,待拣货} 且本月创建、按数据权限过滤 → 左侧菜单「出入库申请」(出库页签)
     // 3 盘点任务进行中=盘点单状态=执行中 且未过计划完成日期(不限创建月份)→ V5 待办单据(盘点页签)
     // 4 待我审批=当前用户为审批流当前节点处理人的单据数(入库+出库+调拨合计)→ 协同办公流程待办
-    // in/out 都显式写 direction,不依赖 dashboard.vue open() 里 !filters.direction 的兜底:
-    // 待出库单一旦漏写就会落到入库页签,卡片数字与下钻列表立刻对不上。
+    // in/out 都显式写 filters.direction,跳转时由 goIoRequests 带给真实路由:
+    // 待出库单一旦漏写就会落到入库页签,卡片数字与目标页列表立刻对不上。
     // 状态口径说明:t_wms_apply_storage.status 只有 0未处理/1已处理/2已撤销,"未完成(status=0)"
     // 已等价覆盖需求里的 {草稿,待审批,待收货,待上架}(入库)与 {草稿,待审批,待拣货}(出库),
-    // 因此首页数字只按未完成合计展示、不能按细分状态拆开,下钻也只传 direction,
-    // status=0 与本月范围由 open() 统一补,避免两处各写一份范围导致数字与列表分叉。
+    // 因此首页数字只按未完成合计展示、不能按细分状态拆开,跳转也只传 direction 和仓库,
+    // 状态/时间范围交给目标页自己的默认值,避免两处各写一份范围导致数字与列表分叉。
+    // target 在这里只当「可点击」标记用(盘点/审批两张没有 target,纯展示)。
     todoCards() {
       const summary = this.summary || {};
       const cards = [
         { key: 'in', icon: 'in', symbol: '↓', label: '待入库单', value: summary.pendingInbound, target: 'ioapp', filters: { direction: 'in' }, overdue: summary.overdueInbound },
         { key: 'out', icon: 'out', symbol: '↑', label: '待出库单', value: summary.pendingOutbound, target: 'ioapp', filters: { direction: 'out' }, overdue: summary.overdueOutbound },
-        { key: 'check', icon: 'check', symbol: '✓', label: '盘点任务进行中', value: summary.pendingInventory, target: 'todo' },
-        { key: 'approve', icon: 'approve', symbol: '▣', label: '待我审批', value: summary.pendingApproval, overdue: summary.overdueApproval, target: 'flow' }
+        { key: 'check', icon: 'check', symbol: '✓', label: '盘点任务进行中', value: summary.pendingInventory },
+        { key: 'approve', icon: 'approve', symbol: '▣', label: '待我审批', value: summary.pendingApproval, overdue: summary.overdueApproval }
       ];
       // 只在 6 个数值任一变化时打一条,供排查「卡片数字与下钻列表对不上」;不含 query 全量与 token。
       const snapshot = todoCardsSnapshot(cards);
@@ -652,18 +656,18 @@ export default {
       // 质检卡片同理:一次解析同时喂颜色、状态、较上月 pp 与让步接收小字,日志只打一次。
       const quality = this.qualityCard;
       return [
-        { key: 'stock', code: '01', label: '库存总量', value: s.inventoryQuantity, unit: this.unit, reason: s.inventoryReason, delta: this.stockWeekOverWeek.text, deltaTitle: this.stockWeekOverWeek.title, subtotal: this.stockUnitSubtotal, locked: this.stockLocked, info: '当前在库数量含待检、剔除锁定;不同基本单位分别展示,不跨单位加总', color: '#38C8F5', target: 'stockLedger' },
-        // 金额为空时优先展示成本覆盖原因(用户更需要知道为什么没有金额),只有拿到完整金额后才展示周环比。
-        { key: 'value', code: '02', label: '库存金额', value: s.inventoryValue, unit: '元', reason: s.valuationReason, delta: s.inventoryValue == null ? (s.valuationReason || '库存金额待成本数据核实') : this.stockValueWeekOverWeek.text, deltaTitle: s.inventoryValue == null ? (s.valuationReason || '') : this.stockValueWeekOverWeek.title, info: '库存金额=Σ(在库数量×出库成本单价),移动加权平均、不含税;与库存总量同源同一份快照,不扣减呆滞跌价准备', color: '#9B7DF7', target: 'stockLedger' },
+        { key: 'stock', code: '01', symbol: '库', label: '库存总量', value: s.inventoryQuantity, unit: this.unit, reason: s.inventoryReason, delta: this.stockWeekOverWeek.text, deltaTitle: this.stockWeekOverWeek.title, subtotal: this.stockUnitSubtotal, locked: this.stockLocked, info: '当前在库数量含待检、剔除锁定;不同基本单位分别展示,不跨单位加总', color: '#00eaff', target: 'stockLedger' },
+        // 金额为空时 delta 留空(原因文案不再显示在卡片上,控制台日志保留)。
+        { key: 'value', code: '02', symbol: '¥', label: '库存金额', value: s.inventoryValue, unit: '元', reason: s.valuationReason, delta: s.inventoryValue == null ? '' : this.stockValueWeekOverWeek.text, deltaTitle: s.inventoryValue == null ? (s.valuationReason || '') : this.stockValueWeekOverWeek.title, info: '库存金额=Σ(在库数量×出库成本单价),移动加权平均、不含税;与库存总量同源同一份快照,不扣减呆滞跌价准备', color: '#a78bfa', target: 'stockLedger' },
         // 周转天数:颜色与状态全部来自后端 turnoverStatus(null 走中性色、状态留空),
         // 较上月差值按正负换绿/红,第二行小字展示「状态 · 截至 yyyy-MM-dd 日末」(T+1)。
-        { key: 'turnover', code: '03', label: '库存周转天数', value: s.turnoverDays, unit: '天', reason: s.turnoverReason, delta: turnover.delta, deltaTitle: turnover.deltaTitle, deltaTone: turnover.deltaTone, status: turnover.status, subtotal: turnover.subtotal, info: turnover.info, color: turnover.color, target: 'turnover' },
+        { key: 'turnover', code: '03', symbol: '转', label: '库存周转天数', value: s.turnoverDays, unit: '天', reason: s.turnoverReason, delta: turnover.delta, deltaTitle: turnover.deltaTitle, deltaTone: turnover.deltaTone, status: turnover.status, subtotal: turnover.subtotal, info: turnover.info, color: turnover.color, target: 'turnover' },
         // 呆滞品占比:颜色与状态只认后端 stagnantLevel(null 走中性色、状态留空),
         // 阈值/环比/可核实分母同样由后端下发,前端不再自行用 8%/10% 判级(判级依据在仓库配置与上月快照里)。
-        { key: 'stagnant', code: '04', label: '呆滞品占比', value: s.stagnantRate, unit: '%', reason: s.stagnantReason, delta: stagnant.delta, deltaTitle: stagnant.deltaTitle, deltaTone: stagnant.deltaTone, status: stagnant.status, subtotal: stagnant.subtotal, info: stagnant.info, color: stagnant.color, progress: stagnant.progress, target: 'dead' },
+        { key: 'stagnant', code: '04', symbol: '滞', label: '呆滞品占比', value: s.stagnantRate, unit: '%', reason: s.stagnantReason, delta: stagnant.delta, deltaTitle: stagnant.deltaTitle, deltaTone: stagnant.deltaTone, status: stagnant.status, subtotal: stagnant.subtotal, info: stagnant.info, color: stagnant.color, progress: stagnant.progress, target: 'dead' },
         // 质检通过率:颜色与文案按业务确认的 ≥98/95 阈值分级(null 走中性色、状态留空,0 已检批次不显示 0%),
         // 较上月复用 qualityChange(单位 pp,上升为好 → good 绿 ▲),让步接收批次走第二行小字。
-        { key: 'quality', code: '06', label: '质检通过率', value: s.qualityPassRate, unit: '%', reason: s.qualityReason, delta: quality.delta, deltaTitle: quality.deltaTitle, deltaTone: quality.deltaTone, status: quality.status, subtotal: quality.subtotal, info: quality.info, color: quality.color, progress: quality.progress, target: 'qc' }
+        { key: 'quality', code: '06', symbol: '检', label: '质检通过率', value: s.qualityPassRate, unit: '%', reason: s.qualityReason, delta: quality.delta, deltaTitle: quality.deltaTitle, deltaTone: quality.deltaTone, status: quality.status, subtotal: quality.subtotal, info: quality.info, color: quality.color, progress: quality.progress, target: 'qc' }
       ];
     }
   },
@@ -685,13 +689,35 @@ export default {
       }, 3000);
     }
   },
+  mounted() {
+    // 全屏状态跟随浏览器事件同步:用户按 ESC / F11 退出时图标与 title 也要跟着变。
+    this.onFullscreenChange = () => { this.isFullscreen = !!document.fullscreenElement; };
+    document.addEventListener('fullscreenchange', this.onFullscreenChange);
+  },
   beforeDestroy() {
     // 组件销毁时清掉高亮定时器:否则 3 秒后会在已销毁实例上写响应式数据(报错/内存泄漏)。
     if (this.highlightTimer) clearTimeout(this.highlightTimer);
     this.highlightTimer = null;
+    document.removeEventListener('fullscreenchange', this.onFullscreenChange);
   },
   methods: {
     display: displayNumber,
+    // 待入库/待出库单卡点击:直接跳左侧菜单「出入库申请」真实路由(独立页签,不走首页内嵌 ioapp 视图)。
+    // direction 由 todoCards 里的 filters 带出(in→入库页签、out→出库页签),仓库沿用当前筛选;
+    // 状态/时间不传,交给目标页自己的默认值(defaultWhere.status='' 即全部状态)。
+    goIoRequests(item) {
+      this.$emit('navigate', '/warehouseManagement/inboundRequests', { warehouseId: this.warehouseId, ...(item.filters || {}) });
+    },
+    // 标题带全屏切换:把整个看板容器 .wms-home(含首页/下钻视图)投到浏览器全屏,
+    // 系统菜单、页头随容器一起被盖住;再点一次或按 ESC 退出。不支持 Fullscreen API 的环境只记日志不报错。
+    toggleFullscreen() {
+      const el = (this.$el && this.$el.closest && this.$el.closest('.wms-home')) || document.documentElement;
+      if (!document.fullscreenElement) {
+        if (el.requestFullscreen) el.requestFullscreen().catch((err) => homeWarn('进入全屏失败', { message: err && err.message }));
+      } else if (document.exitFullscreen) {
+        document.exitFullscreen().catch((err) => homeWarn('退出全屏失败', { message: err && err.message }));
+      }
+    },
     displayKpi(item) {
       if (item.value == null) return '—';
       const value = Number(item.value);
@@ -785,17 +811,17 @@ export default {
 .aging-delta { margin-left: 6px; }
 /* 较上月不可比时只显示后端原因,用中性小字,不带任何健康度色调。 */
 .aging-delta-note { margin-left: 6px; color: #9FB8D4; }
-.aging-note { margin-top: 8px; font-size: 11px; line-height: 1.6; color: #9FB8D4; }
+.aging-note { margin-top: 8px; font-size:13px; line-height: 1.6; color: #9FB8D4; }
 /* F1 安全库存预警卡:状态标签与缺口行在同一行内展示。
    .alert-gap 用 margin-left:auto 顶到右侧,保持旧版「物料名 + 右侧说明」的视觉节奏(行高不变,仍是 5 行)。 */
-.alert-item .alert-state { flex-shrink: 0; font-size: 11px; font-weight: 600; }
+.alert-item .alert-state { flex-shrink: 0; font-size:13px; }
 .alert-item .alert-gap { margin-left: auto; white-space: nowrap; color: var(--t2, #9FB8D4); font-variant-numeric: tabular-nums; }
 /* 影响工单数只在缺口行出现(0 或缺失不渲染),橙色小字提示「这一行真的挡住了生产」。 */
-.alert-item .alert-wo { margin-left: 8px; white-space: nowrap; color: #F0A03A; }
+.alert-item .alert-wo { margin-left: 8px; white-space: nowrap; color: #ffb020; }
 /* 「已生成申请」=防重复下单的识别标记:中性描边标签,不表达健康度,也不与三色预警抢视觉。 */
 .alert-item .alert-requested { margin-left: 8px; padding: 1px 6px; border: 1px solid rgba(155,125,247,.55); border-radius: 9px; color: #C9B6FF; white-space: nowrap; }
 /* 汇总条:工单数可点击下钻,缺口金额合计与待核实说明为只读信息;上边框与卡片分隔线保持一致。 */
-.alert-summary { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 14px; padding: 9px 18px; border-top: 1px solid var(--grid, rgba(159,184,212,.18)); font-size: 12px; color: var(--t2, #9FB8D4); }
+.alert-summary { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 14px; padding: 9px 18px; border-top: 1px solid var(--grid, rgba(159,184,212,.18)); font-size:13px; color: var(--t2, #9FB8D4); }
 .alert-sum-item { white-space: nowrap; }
 .alert-sum-item[role="button"] { cursor: pointer; }
 .alert-sum-item b { color: #EAF4FF; font-weight: 600; }

+ 54 - 25
src/views/warehouseManagement/index/dashboard-element.scss

@@ -1,30 +1,59 @@
 /* 深色样式只覆盖看板自身容器(.wms-home)和看板专用弹层,保留系统页头、菜单及其下拉框原样。
-   不再挂在 body.wms-dashboard-theme 上:从首页下钻到出库单详情、盘点工单详情等业务页时该 class 若残留,
-   会把新页签里的整页 Element 组件(卡片/表格/输入框)刷成深色 #0A1A30,看着就是「蓝色背景」。 */
+   2026-10-30 新增 body.wms-dashboard-open:业务页(出入库申请等)的 el-select/el-date-picker
+   弹层默认 popper-append-to-body 挂到 body 下,不挂 popper-class,白底弹出层和看板割裂。
+   该 class 由 dashboard.vue 在 created 挂上、deactivated/beforeDestroy 摘除——离开看板即失效,
+   不会像早期方案那样靠路由 query 判断造成残留(见下)。 */
+@mixin wms-dark-element {
+  &.el-select-dropdown,.el-card,.el-dialog,.el-message-box,.el-select-dropdown,.el-picker-panel,.el-cascader-panel,.el-popover,.el-dropdown-menu,.el-autocomplete-suggestion { background: #031428; color: #e4f6ff; border-color: rgba(0,200,255,.28); }
+  .el-card__header,.el-dialog__header,.el-message-box__header { border-color: rgba(0,200,255,.28); }
+  .el-dialog__title,.el-message-box__title,.el-dialog__body,.el-message-box__content,.el-form-item__label,.el-checkbox__label,.el-radio__label { color: #e4f6ff; }
+  .el-input__inner,.el-textarea__inner,.el-range-input { background: #020A16; color: #e4f6ff; border-color: rgba(0,200,255,.35); }
+  .el-input.is-disabled .el-input__inner,.el-textarea.is-disabled .el-textarea__inner { background: #0B2038; color: #8fc4de; }
+  .el-select-dropdown__item,.el-dropdown-menu__item,.el-cascader-node,.el-date-table td { color: #8fc4de; }
+  .el-select-dropdown__item.hover,.el-select-dropdown__item:hover,.el-dropdown-menu__item:hover,.el-cascader-node:hover,.el-date-table td.available:hover { background: #0E3352; color: #00eaff; }
+  .el-select-dropdown__item.selected,.el-dropdown-menu__item.active { color: #00eaff; }
+  /* 日期面板选中态:默认 .in-range 是白底浅蓝、起止日蓝底白字,与深色面板割裂 */
+  .el-date-table td.in-range div,.el-date-table td.in-range div:hover { background: rgba(0,200,255,.14); }
+  .el-date-table td.today span { color: #00eaff; font-weight: 600; }
+  .el-date-table td.start-date span,.el-date-table td.end-date span,.el-date-table td.current:not(.disabled) span,
+  .el-date-table td.selected span,.el-date-table td.selected div {
+    background: #00b8dd; color: #001526; font-weight: 600;
+  }
+  .el-month-table td .cell,.el-year-table td .cell { color: #8fc4de; }
+  .el-month-table td.current:not(.disabled) .cell,.el-year-table td.current:not(.disabled) .cell { color: #00eaff; font-weight: 600; }
+  .el-month-table td .cell:hover,.el-year-table td .cell:hover { background: #0E3352; color: #00eaff; }
+  .el-date-picker__header-label,.el-date-table th,.el-picker-panel__icon-btn { color: #8fc4de; }
+  .el-date-picker__header-label:hover,.el-picker-panel__icon-btn:hover { color: #00eaff; }
+  .el-date-table th { border-bottom-color: rgba(0,200,255,.28); }
+  .el-date-range-picker__content.is-left { border-right-color: rgba(0,200,255,.28); }
+  .el-picker-panel__footer { background: #031428; border-top-color: rgba(0,200,255,.28); }
+  .el-table,.el-table__expanded-cell,.el-table tr { background: #031428; color: #e4f6ff; }
+  .el-table th.el-table__cell,.el-table th { background: #0B2038; color: #7eb2cd; }
+  .el-table td,.el-table th.is-leaf,.el-table--border,.el-table--group { border-color: rgba(0,200,255,.28); }
+  .el-table--enable-row-hover .el-table__body tr:hover > td,.el-table__body tr.current-row > td { background: #0E3352; }
+  .el-table::before,.el-table--border::after { background: rgba(0,200,255,.28); }
+  .el-tabs--border-card,.el-tabs--border-card > .el-tabs__header,.el-tabs__nav-wrap::after { background: #031428; border-color: rgba(0,200,255,.28); }
+  .el-tabs--border-card > .el-tabs__header .el-tabs__item.is-active { background: #0E3352; color: #00eaff; border-color: rgba(0,200,255,.28); }
+  .el-tabs__item,.el-pagination,.el-pagination__total,.el-pagination__jump,.el-upload-list__item-name { color: #8fc4de; }
+  .el-pagination button,.el-pager li { background: #0B2038; color: #8fc4de; }
+  .el-pager li.active { color: #00eaff; }
+  .el-button--default { background: #0B2038; color: #e4f6ff; border-color: rgba(0,200,255,.35); }
+  .el-button--primary { background: #0090c8; border-color: #00eaff; }
+  .el-input-number__increase,.el-input-number__decrease { background: #0B2038; color: #8fc4de; border-color: rgba(0,200,255,.28); }
+  .el-loading-mask { background: rgba(2,10,22,.75); }
+  .el-message { background: #0B2038; border-color: rgba(0,200,255,.35); color: #e4f6ff; }
+  .dashboard-note { color: #8fc4de; line-height: 1.8; margin: 0 0 14px; }
+  .dashboard-success { color: #3BD48A; } .dashboard-error { color: #F25E5E; }
+}
 .wms-home,
 .wms-dashboard-dialog,
 .wms-dashboard-popper {
-  &.el-select-dropdown,.el-card,.el-dialog,.el-message-box,.el-select-dropdown,.el-picker-panel,.el-cascader-panel,.el-popover,.el-dropdown-menu,.el-autocomplete-suggestion { background: #0A1A30; color: #EAF4FF; border-color: rgba(70,160,230,.28); }
-  .el-card__header,.el-dialog__header,.el-message-box__header { border-color: rgba(70,160,230,.28); }
-  .el-dialog__title,.el-message-box__title,.el-dialog__body,.el-message-box__content,.el-form-item__label,.el-checkbox__label,.el-radio__label { color: #EAF4FF; }
-  .el-input__inner,.el-textarea__inner,.el-range-input { background: #071528; color: #EAF4FF; border-color: rgba(70,160,230,.35); }
-  .el-input.is-disabled .el-input__inner,.el-textarea.is-disabled .el-textarea__inner { background: #10243A; color: #9FB8D4; }
-  .el-select-dropdown__item,.el-dropdown-menu__item,.el-cascader-node,.el-date-table td { color: #9FB8D4; }
-  .el-select-dropdown__item.hover,.el-select-dropdown__item:hover,.el-dropdown-menu__item:hover,.el-cascader-node:hover,.el-date-table td.available:hover { background: #173655; color: #38C8F5; }
-  .el-table,.el-table__expanded-cell,.el-table tr { background: #0A1A30; color: #EAF4FF; }
-  .el-table th.el-table__cell,.el-table th { background: #102A48; color: #6FA6D8; }
-  .el-table td,.el-table th.is-leaf,.el-table--border,.el-table--group { border-color: rgba(70,160,230,.28); }
-  .el-table--enable-row-hover .el-table__body tr:hover > td,.el-table__body tr.current-row > td { background: #173655; }
-  .el-table::before,.el-table--border::after { background: rgba(70,160,230,.28); }
-  .el-tabs--border-card,.el-tabs--border-card > .el-tabs__header,.el-tabs__nav-wrap::after { background: #0A1A30; border-color: rgba(70,160,230,.28); }
-  .el-tabs--border-card > .el-tabs__header .el-tabs__item.is-active { background: #173655; color: #38C8F5; border-color: rgba(70,160,230,.28); }
-  .el-tabs__item,.el-pagination,.el-pagination__total,.el-pagination__jump,.el-upload-list__item-name { color: #9FB8D4; }
-  .el-pagination button,.el-pager li { background: #102A48; color: #9FB8D4; }
-  .el-pager li.active { color: #38C8F5; }
-  .el-button--default { background: #102A48; color: #EAF4FF; border-color: rgba(70,160,230,.35); }
-  .el-button--primary { background: #2E8BE8; border-color: #38C8F5; }
-  .el-input-number__increase,.el-input-number__decrease { background: #102A48; color: #9FB8D4; border-color: rgba(70,160,230,.28); }
-  .el-loading-mask { background: rgba(6,15,31,.75); }
-  .dashboard-note { color: #9FB8D4; line-height: 1.8; margin: 0 0 14px; }
-  .dashboard-success { color: #3BD48A; } .dashboard-error { color: #F25E5E; }
+  @include wms-dark-element;
+}
+/* 看板打开期间,所有 popper-append-to-body 的弹层(含系统头部的下拉)统一深色 */
+body.wms-dashboard-open {
+  .el-select-dropdown,.el-dropdown-menu,.el-picker-panel,.el-cascader-panel,.el-autocomplete-suggestion,.el-message-box,.el-dialog,.el-popover,.el-message {
+    background: #031428; color: #e4f6ff; border-color: rgba(0,200,255,.28);
+    @include wms-dark-element;
+  }
 }

+ 9 - 6
src/views/warehouseManagement/index/dashboard-layout.scss

@@ -3,7 +3,7 @@
   /* 首屏遮罩用绝对定位盖住内容区,所以这里必须是定位上下文;不能改成 fixed——那会连系统页头、菜单一起盖掉。 */
   position: relative;
   button:disabled { opacity: .45; cursor: not-allowed; }
-  select option { background: #0A1830; color: #EAF4FF; }
+  select option { background: #031428; color: #e4f6ff; }
   .data-note,.empty { padding: 18px; color: var(--t2); font-size: 13px; }
   .data-note.bad,.overdue-row { color: var(--red); }
   .data-note button { color: var(--cyan); background: none; border: 0; cursor: pointer; }
@@ -13,8 +13,8 @@
   .row-actions { white-space: nowrap; }
   .row-actions a + a { margin-left: 10px; }
   .filters { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px; }
-  .filters label { display: flex; flex-direction: column; gap: 5px; font-size: 12px; color: var(--t2); }
-  .filters select,.filters input { height: 32px; padding: 5px 8px; border: 1px solid var(--panel-line); border-radius: 5px; background: rgba(6,15,31,.7); color: var(--t1); max-width: 190px; }
+  .filters label { display: flex; flex-direction: column; gap: 5px; font-size:13px; color: var(--t2); }
+  .filters select,.filters input { height: 32px; padding: 5px 8px; border: 1px solid var(--panel-line); border-radius: 2px; background: rgba(2,10,22,.72); color: var(--t1); max-width: 190px; }
   .filters input[type=month] { color-scheme: dark; }
   .dashboard-business .drill-head { margin-bottom: 16px; }
   .receive-progress { display: flex; gap: 16px; align-items: center; padding: 14px 18px; margin: 12px 0; border: 1px solid var(--panel-line); background: var(--panel); }
@@ -33,14 +33,17 @@
 .wms-home .home-loading {
   position: absolute; top: 0; right: 0; bottom: 0; left: 0; z-index: 20;
   display: flex; align-items: center; justify-content: center; gap: 10px;
-  background: radial-gradient(ellipse at 50% 0%, #123258 0%, #0A1830 52%, #060F1F 100%);
-  color: #EAF4FF; font-size: 13px; letter-spacing: .5px;
+  background:
+    radial-gradient(ellipse 70% 42% at 50% -8%, rgba(0,150,220,.28), transparent 58%),
+    radial-gradient(ellipse 50% 40% at 50% 100%, rgba(0,40,80,.35), transparent 70%),
+    linear-gradient(180deg, #001526 0%, #000912 42%, #00060d 100%);
+  color: #e4f6ff; font-size: 13px; letter-spacing: .5px;
   /* 首屏内容为空时 .wms-home 的高度只靠 min-height:100% 撑,给遮罩一个下限,避免极端情况下压成一条线。 */
   min-height: 320px;
 }
 .wms-home .home-loading__spinner {
   width: 18px; height: 18px; border-radius: 50%;
-  border: 2px solid rgba(56,200,245,.25); border-top-color: #38C8F5;
+  border: 2px solid rgba(0,230,255,.25); border-top-color: #00eaff;
   animation: home-loading-spin .9s linear infinite;
 }
 @keyframes home-loading-spin { to { transform: rotate(360deg); } }

+ 2 - 4
src/views/warehouseManagement/index/dashboard-screens.js

@@ -6,7 +6,6 @@ export const screens = {
   // 渲染时由 detail.vue 用 home-summary 的 stagnantDays 替换(缺失回落 180),这里不写死天数。
   dead: { title: '呆滞品分析', subtitle: '识别库龄超过 {stagnantDays} 天且期间无出库/领用的在库批次并推动真实处置', definition: '判定口径与首页「呆滞品占比」共用仓库配置的同一库龄阈值({stagnantDays} 天):期间无任何出库/领用动态才算呆滞,分母只含有可核实来源标记与首次入库/最近消耗日期的批次。首次日期缺失单列“日期待核实”。处置申请及审批本身不改变库存;执行关联的调拨或出库才变动库存。', metrics: ['stagnantRate'], columns: columns('categoryCode:物料编码|categoryName:物料名称|warehouseName:仓库|systemBatchNo:系统批次|quantity:呆滞量|unit:单位|valuation:批次估值|stagnantDays:未消耗天数|ageDays:库龄天数'), action: '发起处置' },
   io: { title: '出入库动态', subtitle: '已完成收发货流水及原始单据', definition: '只统计已完成出入库流水;草稿、待审批及待执行计入待办。不同基本单位分别核对。', period: true, columns: columns('orderNo:单据号|direction:方向|businessType:业务类型|categoryName:物料名称|warehouseName:仓库|quantity:数量|unit:单位|amount:金额|operateTime:作业时间|operatorName:操作员'), action: '查看单据' },
-  todo: { title: '待办单据', subtitle: '未完成出入库申请与进行中盘点', definition: '待入库/待出库只计本月创建、尚未完成的入库/出库申请(草稿、待审批、待收货、待上架/待拣货),并按数据权限过滤;盘点只计状态执行中且未过计划完成日期,不限创建月份;待我审批仅计入库、出库、调拨三类流程本月到达当前用户当前节点的任务。', metrics: ['pendingInventory', 'pendingApproval'], columns: columns('orderNo:单据号|type:单据类型|warehouseName:仓库|status:状态|createTime:创建时间|dueTime:计划完成|reason:说明'), action: '查看任务' },
   // 库龄口径(与首页「库龄结构」卡片、后端 WmsHomeSummaryVO.aging 逐条对齐):
   // 库龄=当前日期−首次入库日期,最后入库日期只展示、补库不刷新库龄;期初数据按仓库配置的上线日起算(ageSource=warehouse);
   // 分段 0-30/31-90/91-180/>180 天由后端下发,四段占比合计 100%,“日期待核实”单列且不参与四段占比;
@@ -24,8 +23,7 @@ export const screens = {
   turnover: { title: '库存周转分析', subtitle: '按月初与期末数量快照估算本月库存周转', definition: '月平均库存=(上月末期初+昨日期末)/2;天数按本月已过天数折算,T+1 展示截至昨日日末(首页 turnoverAsOf 标注日末快照日期)。分母只含已完成的消耗性出库(销售/领料/自选领用/配料/按物料需求清单领料),剔除调拨出库。目标天数与预警天数按仓库配置,默认 45 天达标、60 天预警,状态由后端判定(达标/超目标/偏慢)。历史覆盖不足时不补造。', metrics: ['turnoverDays'], columns: columns('categoryName:物料名称|warehouseName:仓库|unit:单位|averageQuantity:平均库存量|outboundQuantity:出库量|turnoverDays:周转天数|turnoverTargetDays:目标天数|turnoverStatus:状态|recordedDays:已采集天数|expectedDays:所需天数|reason:说明'), action: '查看库存' },
   // 口径文案与后端 WmsHomeSummaryVO 逐条对齐:分母只含已检(合格+不合格,让步接收进分母不计合格),
   // 未检、无需质检与来源不可追溯的批次都不进分母;较上月的差值单位是百分点(pp),不是百分比。
-  qc: { title: '入库质检分析', subtitle: '本月质检批次与实际检验结论', definition: '检验完成日期落在本月;通过率=合格批次÷已检批次(合格+不合格,让步接收进分母不计合格);无质检要求与来源不可追溯的批次不计入分母;较上月保留两位小数,单位 pp。', period: true, metrics: ['qualityPassRate', 'qualityChange', 'qualityCoverage'], columns: columns('categoryName:物料名称|warehouseName:仓库|unit:单位|systemBatchNo:系统批次|qualityStatus:质检状态|qualityResult:检验结论|qualityTime:检验时间|reason:说明'), action: '打开检验工作台' },
-  flow: { title: '待我审批', subtitle: '当前用户当前节点的仓储审批任务', definition: '仅查询授权仓库、本月到达当前用户的仓储流程,且仅入库、出库、调拨三类流程;批量审批逐单返回结果,复杂表单进入原审批页处理。', period: true, columns: columns('processInstance.processTypeName:流程分类|processInstance.name:流程名称|name:任务名称|businessCode:单据编码|businessName:单据名称|businessType:单据类型|processInstance.startUserNickname:发起人|createTime:到达时间'), action: '审批' }
+  qc: { title: '入库质检分析', subtitle: '本月质检批次与实际检验结论', definition: '检验完成日期落在本月;通过率=合格批次÷已检批次(合格+不合格,让步接收进分母不计合格);无质检要求与来源不可追溯的批次不计入分母;较上月保留两位小数,单位 pp。', period: true, metrics: ['qualityPassRate', 'qualityChange', 'qualityCoverage'], columns: columns('categoryName:物料名称|warehouseName:仓库|unit:单位|systemBatchNo:系统批次|qualityStatus:质检状态|qualityResult:检验结论|qualityTime:检验时间|reason:说明'), action: '打开检验工作台' }
 };
 export const metricLabels = {
   stagnantRate: ['呆滞占比', '%', 'stagnantReason'], turnoverDays: ['本月周转天数', '天', 'turnoverReason'],
@@ -33,5 +31,5 @@ export const metricLabels = {
   // 较上月差值:单位位留空,pp 与符号已由 formatPpChange 拼进数值文本(detail.vue 的 metricValue),
   // 第三位是明细页 delta 行取的原因字段,不可比时正好展示 qualityChangeReason。
   qualityChange: ['较上月', '', 'qualityChangeReason'],
-  ageCoverage: ['库龄来源覆盖率', '%', 'stagnantReason'], qualityCoverage: ['质检记录覆盖率', '%', 'qualityReason'], pendingInventory: ['进行中盘点', '单', ''], pendingApproval: ['待我审批', '单', 'approvalReason']
+  ageCoverage: ['库龄来源覆盖率', '%', 'stagnantReason'], qualityCoverage: ['质检记录覆盖率', '%', 'qualityReason']
 };

+ 313 - 141
src/views/warehouseManagement/index/dashboard-theme.scss

@@ -1,35 +1,50 @@
-/* 保留设计稿视觉参数,仅应用在 WMS 首页。 */
+/* 视觉参数对齐 HR 项目首页(E:/code/kd-aiot-frontend-hr HomeView.scss):
+   近黑深蓝底 + 多层径向光晕、荧光青 #00eaff 主色、直角卡片 + 四角 L 形发光角标、
+   六边形 clip-path 标题框 + 流光动画。仅应用在 WMS 首页与看板下钻视图。 */
 .wms-home{
 
-  /* 大屏底色 */
-  --bg-top:#123258; --bg-mid:#0A1830; --bg-deep:#060F1F;
-  /* 面板 */
-  --panel:linear-gradient(180deg, rgba(16,40,72,.75), rgba(10,26,48,.85));
-  --panel-line:rgba(70,160,230,.28);
-  --inset:inset 0 0 24px rgba(35,120,210,.10);
-  /* 主色 */
-  --cyan:#38C8F5; --blue:#2E8BE8;
-  /* 语义色 */
-  --orange:#F0A03A; --red:#F25E5E; --green:#3BD48A; --purple:#9B7DF7;
-  /* 文字 */
-  --t1:#EAF4FF; --t2:#9FB8D4; --t3:#5F7A99; --th:#6FA6D8;
+  /* 大屏底色(HR:#001526 → #000912 → #00060d) */
+  --bg-top:#001526; --bg-mid:#000912; --bg-deep:#00060d;
+  /* 面板(HR .panel:165deg 渐变 + blur) */
+  --panel:linear-gradient(165deg, rgba(0,50,85,.38), rgba(2,16,32,.78) 42%, rgba(0,28,50,.55));
+  --panel-line:rgba(0,200,255,.22);
+  --inset:inset 0 0 28px rgba(0,150,220,.08);
+  /* 主色(HR 荧光青) */
+  --cyan:#00eaff; --cyan-2:#4df0ff; --blue:#3d8bff;
+  /* 语义色(橙/紫对齐 HR #ffb020/#a78bfa;红绿维持 WMS 原色阶,与状态文案同源) */
+  --orange:#ffb020; --red:#F25E5E; --green:#3BD48A; --purple:#a78bfa;
+  /* 文字(HR:#e4f6ff / #8fc4de / #6f97b0) */
+  --t1:#e4f6ff; --t2:#8fc4de; --t3:#6f97b0; --th:#7eb2cd;
   /* 线/网格 */
-  --grid:rgba(80,150,220,.12);
-  --split:rgba(70,160,230,.35);
-  --radius:10px;
-  --glow:0 0 12px rgba(56,200,245,.35);
+  --grid:rgba(0,190,255,.14);
+  --split:rgba(0,210,255,.35);
+  /* HR 卡片一律直角 */
+  --radius:0px;
+  --glow:0 0 12px rgba(0,230,255,.45);
+  /* 主按钮渐变(荧光青) */
+  --btn-grad:linear-gradient(135deg,#4df0ff,#00a8dd);
+  --btn-ink:#001526;
 
 &,& *{margin:0;padding:0;box-sizing:border-box;font-family:"PingFang SC","Microsoft YaHei","Segoe UI",sans-serif;}
 &{
-  background:radial-gradient(ellipse at 50% 0%, var(--bg-top) 0%, var(--bg-mid) 52%, var(--bg-deep) 100%) fixed;
+  /* HR 首页同款:网格纹理 + 四层径向光晕 + 纵向渐变底 */
+  background:
+    repeating-linear-gradient(0deg, rgba(0,190,255,.035) 0 1px, transparent 1px 42px),
+    repeating-linear-gradient(90deg, rgba(0,190,255,.035) 0 1px, transparent 1px 42px),
+    radial-gradient(ellipse 70% 42% at 50% -8%, rgba(0,150,220,.28), transparent 58%),
+    radial-gradient(ellipse 38% 32% at 92% 18%, rgba(0,100,180,.20), transparent 62%),
+    radial-gradient(ellipse 34% 28% at 6% 78%, rgba(0,80,140,.18), transparent 60%),
+    radial-gradient(ellipse 50% 40% at 50% 100%, rgba(0,40,80,.35), transparent 70%),
+    linear-gradient(180deg, #001526 0%, var(--bg-mid) 42%, #00060d 100%) fixed;
   color:var(--t1); font-size:14px; min-height:100%;
 }
 
 
 
 
+
 ::-webkit-scrollbar{width:8px;height:8px;}
-::-webkit-scrollbar-thumb{background:rgba(70,160,230,.35);border-radius:4px;}
+::-webkit-scrollbar-thumb{background:rgba(0,210,255,.35);border-radius:4px;}
 ::-webkit-scrollbar-track{background:transparent;}
 
 /* ========== 主区 ========== */
@@ -37,108 +52,180 @@
 .view{display:none;}
 .view.active{display:block;animation:fadeIn .18s ease;}
 @keyframes fadeIn{from{opacity:0;transform:translateY(4px);}to{opacity:1;transform:none;}}
+@keyframes wms-fade-up{from{opacity:0;transform:translateY(10px);}to{opacity:1;transform:none;}}
+@keyframes wms-shine-x{0%{transform:translateX(-130%);}55%,100%{transform:translateX(130%);}}
+@keyframes wms-breathe{0%,100%{opacity:.55;}50%{opacity:1;}}
 
-/* 大屏标题带 */
-.screen-band{margin:0 -20px 16px;padding:18px 20px 16px;position:relative;
-  background:linear-gradient(180deg, rgba(18,50,88,.85), rgba(10,26,48,.15));
-  border-bottom:1px solid var(--panel-line);}
-.screen-band::after{content:"";position:absolute;left:50%;bottom:-1px;transform:translateX(-50%);width:220px;height:2px;background:linear-gradient(90deg,transparent,var(--cyan),transparent);}
-.screen-band .en{font-size:11px;letter-spacing:3px;color:var(--th);text-transform:uppercase;}
-.screen-band h1{font-size:24px;font-weight:700;letter-spacing:2px;margin-top:4px;color:#fff;text-shadow:0 0 18px rgba(56,200,245,.35);}
-.screen-band .meta{position:absolute;right:20px;bottom:16px;display:flex;gap:22px;font-size:12px;color:var(--t2);}
+/* 大屏标题带:居中六边形科技框 + 两侧光翼与菱形(HR overview-title 同款结构)。
+   2026-10-30 用户确认:去掉顶部英文眉题行(Warehouse Data Center),只留主标题。 */
+.screen-band{margin:0 -20px 16px;padding:22px 20px 18px;position:relative;display:flex;align-items:center;justify-content:center;gap:16px;text-align:center;}
+.band-wing{display:flex;align-items:center;gap:6px;opacity:.85;}
+.band-wing i{display:block;height:2px;background:linear-gradient(90deg,transparent,var(--cyan),transparent);box-shadow:0 0 10px rgba(0,230,255,.65);animation:wms-breathe 3s ease-in-out infinite;}
+.band-wing i:nth-child(1){width:54px;}
+.band-wing i:nth-child(2){width:28px;animation-delay:.3s;}
+.band-wing i.dm{width:12px;height:12px;border:1px solid var(--cyan);background:transparent;transform:rotate(45deg);box-shadow:0 0 12px rgba(0,230,255,.7);}
+.band-wing.right{flex-direction:row-reverse;}
+/* 六边形标题框:HR overview-title__frame 1:1 —— 34px 斜切角 + 两端斜纹填充 + 框内菱形装饰 + 流光 */
+.band-frame{position:relative;display:flex;align-items:center;justify-content:center;
+  min-width:min(520px,78vw);min-height:58px;padding:6px 52px 8px;overflow:hidden;
+  background:linear-gradient(180deg, rgba(0,90,140,.55), rgba(0,28,55,.25) 55%, rgba(0,40,70,.4));
+  clip-path:polygon(34px 0,calc(100% - 34px) 0,100% 50%,calc(100% - 34px) 100%,34px 100%,0 50%);
+  box-shadow:0 0 0 1px rgba(0,230,255,.45) inset, 0 0 32px rgba(0,190,255,.28), 0 10px 30px rgba(0,0,0,.35);}
+/* 两端斜纹填充:-45° 重复条纹,向中心渐隐(HR ::before/::after 同款) */
+.band-frame::before,.band-frame::after{content:"";position:absolute;top:0;bottom:0;width:18px;
+  background:repeating-linear-gradient(-45deg, rgba(0,230,255,.55), rgba(0,230,255,.55) 2px, transparent 2px, transparent 6px);
+  opacity:.55;}
+.band-frame::before{left:22px;-webkit-mask-image:linear-gradient(90deg,transparent,#000);mask-image:linear-gradient(90deg,transparent,#000);}
+.band-frame::after{right:22px;-webkit-mask-image:linear-gradient(270deg,transparent,#000);mask-image:linear-gradient(270deg,transparent,#000);}
+/* 框内流光 */
+.band-shine{position:absolute;inset:0;
+  background:linear-gradient(100deg, transparent 30%, rgba(255,255,255,.18) 48%, transparent 62%);
+  animation:wms-shine-x 4.8s ease-in-out infinite;}
+/* 框内两侧发光菱形(HR overview-title__deco) */
+.band-deco{position:absolute;top:50%;z-index:1;width:11px;height:11px;border:1px solid var(--cyan);
+  transform:translateY(-50%) rotate(45deg);box-shadow:0 0 14px rgba(0,230,255,.8);animation:wms-breathe 2.8s ease-in-out infinite;}
+.band-deco.left{left:16px;}
+.band-deco.right{right:16px;animation-delay:1.2s;}
+.screen-band h1{position:relative;z-index:1;margin:0;font-size:26px;font-weight:700;letter-spacing:.32em;text-indent:.32em;color:#f2fcff;
+  text-shadow:0 0 12px rgba(0,230,255,.75), 0 0 28px rgba(0,160,255,.35);}
+.screen-band .meta{position:absolute;right:20px;top:50%;transform:translateY(-50%);display:inline-flex;align-items:center;white-space:nowrap;gap:7px;
+  font-size:13px;color:var(--t2);padding:5px 12px;
+  border:1px solid rgba(0,190,255,.22);background:linear-gradient(180deg,rgba(0,45,78,.6),rgba(0,18,36,.7));box-shadow:0 0 16px rgba(0,170,255,.12);}
+.screen-band .meta > span + span{margin-left:8px;}
 .screen-band .meta b{color:var(--cyan);font-weight:600;}
+/* 全屏切换按钮:青色描边小方块,hover 实心发光;全屏中显示 ✕ 表示可退出。 */
+.screen-band .fs-btn{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;margin-left:4px;
+  font-size:15px;line-height:1;color:var(--cyan);cursor:pointer;border:1px solid rgba(0,230,255,.5);border-radius:2px;
+  background:rgba(0,230,255,.08);transition:all .15s;user-select:none;}
+.screen-band .fs-btn:hover{background:var(--btn-grad);color:var(--btn-ink);box-shadow:0 0 14px rgba(0,230,255,.6);}
+.screen-band .fs-btn:focus{outline:1px solid var(--cyan);outline-offset:2px;}
+/* 绿色实时点(HR live-dot 同款呼吸光圈) */
+.live-dot{width:7px;height:7px;border-radius:50%;background:#2cffb0;animation:wms-live 1.8s ease-out infinite;flex-shrink:0;}
+@keyframes wms-live{0%{box-shadow:0 0 0 0 rgba(44,255,176,.5);}100%{box-shadow:0 0 0 8px rgba(44,255,176,0);}}
 
-/* 通用面板 */
+/* 通用面板:直角 + HR 渐变底 + blur + 入场上浮动画 */
 .card{background:var(--panel);border:1px solid var(--panel-line);border-radius:var(--radius);
-  box-shadow:var(--inset);position:relative;overflow:hidden;}
-.card.corner::before,.card.corner::after{content:"";position:absolute;width:14px;height:14px;border:2px solid var(--cyan);opacity:.75;}
-.card.corner::before{top:-1px;left:-1px;border-right:none;border-bottom:none;border-radius:var(--radius) 0 0 0;}
-.card.corner::after{bottom:-1px;right:-1px;border-left:none;border-top:none;border-radius:0 0 var(--radius) 0;}
+  box-shadow:0 14px 36px rgba(0,0,0,.38), 0 0 28px rgba(0,150,220,.08) inset;
+  backdrop-filter:blur(12px);position:relative;overflow:hidden;animation:wms-fade-up .55s ease both;}
+/* 四角 L 形发光角标(HR metric-card 同款):左上/右上用伪元素,右下/左下用背景渐变补齐 */
+/* 四角 L 形发光角标(HR metric-card 同款):左上/右上用伪元素,右下/左下用背景渐变补齐。
+   注意右上只能由 ::after 画一遍——背景再画会与 KPI 序号(01/02…)叠成一条横线。 */
+.card.corner::before,.card.corner::after{content:"";position:absolute;width:16px;height:16px;border:2px solid var(--cyan);
+  opacity:.9;filter:drop-shadow(0 0 4px rgba(0,230,255,.7));pointer-events:none;}
+.card.corner::before{top:-1px;left:-1px;border-right:none;border-bottom:none;}
+/* 右上角 L 形 = 上边 + 右边(此前误禁 top/left,留下 bottom 边横线插进序号区域) */
+.card.corner::after{top:-1px;right:-1px;border-left:none;border-bottom:none;}
+.card.corner{background:
+  linear-gradient(var(--cyan),var(--cyan)) bottom left/12px 2px no-repeat,
+  linear-gradient(var(--cyan),var(--cyan)) bottom left/2px 12px no-repeat,
+  linear-gradient(var(--cyan),var(--cyan)) bottom right/12px 2px no-repeat,
+  linear-gradient(var(--cyan),var(--cyan)) bottom right/2px 12px no-repeat,
+  var(--panel);}
 .card-hd{display:flex;align-items:center;justify-content:space-between;padding:14px 18px 0;}
-.card-hd h3{font-size:14px;font-weight:600;color:#EAF4FF;display:flex;align-items:center;gap:8px;}
-.card-hd h3::before{content:"";width:4px;height:14px;border-radius:2px;background:var(--cyan);box-shadow:0 0 8px rgba(56,200,245,.7);}
-.card-hd .sub{font-size:12px;color:var(--t3);margin-top:4px;padding-left:12px;}
-.link{color:var(--cyan);font-size:12px;cursor:pointer;text-decoration:none;}
-.link:hover{text-shadow:0 0 10px rgba(56,200,245,.6);}
-.tag{display:inline-block;font-size:11px;padding:2px 8px;border-radius:10px;font-weight:500;border:1px solid transparent;}
+/* 卡片标题:HR panel-header h2 同款——发光渐变竖条 + 15px 白字 + 标题下渐隐细线 */
+.card-hd h3{position:relative;font-size:15px;font-weight:650;color:#eaf9ff;letter-spacing:.1em;display:flex;align-items:center;gap:10px;
+  text-shadow:0 0 12px rgba(0,200,255,.35);}
+.card-hd h3::before{content:"";width:4px;height:16px;flex-shrink:0;
+  background:linear-gradient(180deg,#7af6ff,rgba(0,180,255,.1));box-shadow:0 0 10px rgba(0,230,255,.8);}
+.card-hd h3::after{content:"";position:absolute;left:14px;right:-28px;bottom:-6px;height:1px;
+  background:linear-gradient(90deg,rgba(0,230,255,.55),transparent);}
+.card-hd .sub{font-size:13px;color:var(--t3);margin-top:4px;padding-left:12px;}
+.link{color:var(--cyan);font-size:13px;cursor:pointer;text-decoration:none;}
+.link:hover{text-shadow:0 0 10px rgba(0,230,255,.7);}
+.tag{display:inline-block;font-size:13px;padding:2px 8px;border-radius:2px;border:1px solid transparent;}
 .tag.g{background:rgba(59,212,138,.14);color:var(--green);border-color:rgba(59,212,138,.4);}
 .tag.r{background:rgba(242,94,94,.14);color:var(--red);border-color:rgba(242,94,94,.4);}
-.tag.o{background:rgba(240,160,58,.14);color:var(--orange);border-color:rgba(240,160,58,.4);}
-.tag.b{background:rgba(56,200,245,.14);color:var(--cyan);border-color:rgba(56,200,245,.4);}
-.tag.p{background:rgba(155,125,247,.14);color:var(--purple);border-color:rgba(155,125,247,.4);}
+.tag.o{background:rgba(255,176,32,.14);color:var(--orange);border-color:rgba(255,176,32,.4);}
+.tag.b{background:rgba(0,230,255,.14);color:var(--cyan);border-color:rgba(0,230,255,.4);}
+.tag.p{background:rgba(167,139,250,.14);color:var(--purple);border-color:rgba(167,139,250,.4);}
 .tag.gr{background:rgba(120,160,200,.12);color:var(--t2);border-color:rgba(120,160,200,.3);}
 
 /* ========== 问候 + 快捷操作 ========== */
 .greet{display:flex;align-items:center;justify-content:space-between;margin-bottom:14px;}
-.greet h2{font-size:18px;font-weight:700;color:#fff;}
-.greet p{font-size:12px;color:var(--t3);margin-top:4px;}
+.greet h2{font-size:18px;font-weight:700;color:#fff;text-shadow:0 0 14px rgba(0,200,255,.3);}
+.greet p{font-size:13px;color:var(--t3);margin-top:4px;}
 .greet p b{color:var(--cyan);font-weight:600;}
 .home-filters{display:flex;gap:12px;align-items:center;}
-.home-filters label{display:flex;align-items:center;gap:6px;color:var(--t2);font-size:12px;}
-.home-filters select{max-width:180px;padding:6px 9px;background:#0A1830;border:1px solid var(--panel-line);border-radius:5px;color:var(--t1);}
-.btn{display:inline-flex;align-items:center;gap:6px;background:linear-gradient(135deg,#38C8F5,#2E8BE8);color:#04121F;border:none;border-radius:7px;padding:8px 14px;font-size:13px;cursor:pointer;font-weight:600;box-shadow:0 0 12px rgba(56,200,245,.3);}
-.btn:hover{box-shadow:0 0 18px rgba(56,200,245,.6);}
-.btn.ghost{background:rgba(56,200,245,.06);color:var(--t1);border:1px solid var(--panel-line);box-shadow:none;font-weight:500;}
-.btn.ghost:hover{border-color:var(--cyan);color:var(--cyan);box-shadow:0 0 12px rgba(56,200,245,.25);}
+.home-filters label{display:flex;align-items:center;gap:6px;color:var(--t2);font-size:13px;}
+.home-filters select{max-width:180px;padding:6px 9px;background:rgba(3,20,40,.85);border:1px solid var(--panel-line);border-radius:2px;color:var(--t1);}
+.btn{display:inline-flex;align-items:center;gap:6px;background:var(--btn-grad);color:var(--btn-ink);border:none;border-radius:2px;padding:8px 14px;font-size:13px;cursor:pointer;font-weight:600;box-shadow:0 0 12px rgba(0,230,255,.3);}
+.btn:hover{box-shadow:0 0 18px rgba(0,230,255,.6);}
+.btn.ghost{background:rgba(0,230,255,.06);color:var(--t1);border:1px solid var(--panel-line);box-shadow:none;font-weight:500;}
+.btn.ghost:hover{border-color:var(--cyan);color:var(--cyan);box-shadow:0 0 12px rgba(0,230,255,.25);}
 
 /* ========== 今日待办条(单行合并 V1.2) ========== */
-.todo-bar{display:flex;align-items:stretch;background:var(--panel);border:1px solid var(--panel-line);border-radius:var(--radius);box-shadow:var(--inset);padding:18px 10px;margin-bottom:14px;}
-.todo-item{flex:1;display:flex;align-items:center;gap:14px;padding:8px 26px;cursor:pointer;border-radius:8px;transition:all .2s;position:relative;}
-.todo-item:hover{background:rgba(56,200,245,.08);box-shadow:inset 0 0 14px rgba(56,200,245,.15);}
+.todo-bar{display:flex;align-items:stretch;background:var(--panel);border:1px solid var(--panel-line);border-radius:var(--radius);box-shadow:var(--inset);padding:18px 10px;margin-bottom:14px;backdrop-filter:blur(12px);}
+.todo-item{flex:1;display:flex;align-items:center;gap:14px;padding:8px 26px;cursor:pointer;border-radius:2px;transition:all .2s;position:relative;}
+.todo-item:hover{background:rgba(0,230,255,.08);box-shadow:inset 0 0 14px rgba(0,230,255,.15);}
 .todo-item:hover .todo-arrow{opacity:1;transform:translateX(0);}
-.todo-item+.todo-item::before{content:"";position:absolute;left:0;top:8%;bottom:8%;width:1px;background:rgba(160,190,225,.28);}
-.todo-icon{width:48px;height:48px;border-radius:11px;flex-shrink:0;display:flex;align-items:center;justify-content:center;box-shadow:0 2px 10px rgba(0,0,0,.35);}
+/* 纯展示卡(盘点任务进行中/待我审批):无跳转,指针、悬浮高亮一律关闭 */
+.todo-item.static{cursor:default;}
+.todo-item.static:hover{background:transparent;box-shadow:none;}
+.todo-item+.todo-item::before{content:"";position:absolute;left:0;top:8%;bottom:8%;width:1px;background:rgba(0,190,255,.25);}
+.todo-icon{width:48px;height:48px;border-radius:50%;flex-shrink:0;display:flex;align-items:center;justify-content:center;box-shadow:0 2px 10px rgba(0,0,0,.35);}
 .todo-icon svg{width:23px;height:23px;}
-.icon-in{background:linear-gradient(180deg,#3D9BF0,#2E7FD8);}
-.icon-out{background:linear-gradient(180deg,#F5AC4E,#E08F28);}
+.icon-in{background:linear-gradient(180deg,#4df0ff,#0a9cd8);}
+.icon-out{background:linear-gradient(180deg,#ffcf70,#e09010);}
 .icon-check{background:linear-gradient(180deg,#43DE97,#2CB877);}
-.icon-approve{background:linear-gradient(180deg,#A98FFA,#8A66F0);}
+.icon-approve{background:linear-gradient(180deg,#c3adff,#8f6cf5);}
 .todo-num{font-size:30px;font-weight:700;line-height:1.1;text-shadow:var(--glow);display:flex;align-items:baseline;gap:8px;}
 .todo-label{font-size:13px;color:var(--t2);margin-top:3px;}
 .todo-arrow{margin-left:auto;color:var(--t3);font-size:16px;opacity:0;transform:translateX(-4px);transition:all .2s;}
-.badge-today{font-size:11px;color:var(--t3);font-weight:400;text-shadow:none;}
-.badge-overdue{font-size:11px;color:var(--red);background:rgba(242,94,94,.15);border:1px solid rgba(242,94,94,.5);border-radius:8px;padding:1px 7px;font-weight:400;text-shadow:none;}
+.badge-today{font-size:13px;color:var(--t3);font-weight:400;text-shadow:none;}
+.badge-overdue{font-size:13px;color:var(--red);background:rgba(242,94,94,.15);border:1px solid rgba(242,94,94,.5);border-radius:2px;padding:1px 7px;font-weight:400;text-shadow:none;}
 
-/* ========== KPI 卡 ========== */
-.kpi-row{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:12px;margin-bottom:14px;}
-.kpi{padding:14px 16px;cursor:pointer;transition:transform .12s, box-shadow .12s, border-color .12s;position:relative;overflow:hidden;}
-.kpi:hover{transform:translateY(-2px);border-color:var(--cyan);box-shadow:0 0 22px rgba(56,200,245,.28), var(--inset);}
+/* ========== KPI 卡(HR metric-card 结构:左圆环图标 + 右文案列) ========== */
+.kpi-row{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:14px;margin-bottom:14px;}
+.kpi{padding:14px 16px;cursor:pointer;transition:transform .25s ease, box-shadow .25s ease, border-color .25s ease;position:relative;overflow:hidden;}
+.kpi:hover{transform:translateY(-4px);border-color:rgba(0,230,255,.55);box-shadow:0 0 0 1px rgba(0,200,255,.25) inset, 0 16px 40px rgba(0,0,0,.4), 0 0 36px rgba(0,200,255,.22);}
 .kpi:hover .kpi-go{opacity:1;}
-.kpi .idx{position:absolute;top:8px;right:10px;font-size:20px;font-weight:800;color:rgba(110,170,220,.18);letter-spacing:1px;}
-.kpi .lb{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--t2);position:relative;z-index:1;}
-.kpi .lb .q{width:14px;height:14px;border-radius:50%;background:rgba(56,200,245,.12);color:var(--th);font-size:10px;display:inline-flex;align-items:center;justify-content:center;cursor:help;border:1px solid rgba(56,200,245,.3);}
-.kpi .val{font-size:24px;font-weight:700;margin:8px 0 4px;letter-spacing:-.3px;text-shadow:var(--glow);}
-.kpi .val small{font-size:12px;font-weight:500;color:var(--t3);margin-left:2px;text-shadow:none;}
-.kpi .delta{font-size:11px;display:flex;align-items:center;gap:4px;}
+/* 首页 KPI 行才用「图标 + 文案」横排;下钻页 kpi-strip 的 KPI 保持纵向堆叠 */
+.kpi-row .kpi{display:flex;align-items:center;gap:14px;padding:16px 18px;min-height:108px;}
+.kpi-ico{position:relative;flex:0 0 52px;width:52px;height:52px;border-radius:50%;display:flex;align-items:center;justify-content:center;
+  font-size:22px;font-weight:600;text-shadow:0 0 12px currentColor;
+  background:radial-gradient(circle at 35% 30%, rgba(0,230,255,.3), rgba(0,40,70,.15) 55%, transparent);}
+.kpi-ico i{position:absolute;inset:0;border:1px solid rgba(0,230,255,.55);border-radius:50%;
+  border-top-color:transparent;border-left-color:rgba(0,230,255,.15);box-shadow:0 0 18px rgba(0,200,255,.28);
+  animation:wms-ring-spin 6s linear infinite;}
+@keyframes wms-ring-spin{to{transform:rotate(360deg);}}
+.kpi-copy{position:relative;z-index:1;min-width:0;flex:1;}
+/* 序号放左上角,left 24px 避开左上角 L 形角标(角标占 16px) */
+.kpi .idx{position:absolute;top:9px;left:24px;font-size:20px;font-weight:800;color:rgba(0,220,255,.28);letter-spacing:1px;}
+.kpi .lb{display:flex;align-items:center;gap:6px;font-size:14px;font-weight:600;color:#eaf9ff;letter-spacing:.05em;position:relative;z-index:1;text-shadow:0 0 10px rgba(0,200,255,.3);}
+.kpi .val{font-size:26px;font-weight:700;margin:6px 0 4px;letter-spacing:-.3px;text-shadow:0 0 14px rgba(0,220,255,.45), 0 0 2px rgba(255,255,255,.3);font-variant-numeric:tabular-nums;}
+.kpi .val small{font-size:13px;font-weight:400;color:var(--t3);margin-left:2px;text-shadow:none;}
+.kpi .delta{font-size:13px;display:flex;align-items:center;gap:4px;}
 .kpi .delta.good{color:var(--green);} .kpi .delta.warn{color:var(--orange);} .kpi .delta.bad{color:var(--red);} .kpi .delta.dim{color:var(--t3);}
-.kpi-go{position:absolute;top:12px;right:12px;opacity:0;transition:opacity .15s;color:var(--cyan);font-size:12px;}
-.bar-mini{height:4px;background:rgba(80,150,220,.15);border-radius:2px;margin-top:10px;overflow:hidden;}
+.kpi-go{position:absolute;top:12px;right:12px;opacity:0;transition:opacity .15s;color:var(--cyan);font-size:13px;}
+.bar-mini{height:4px;background:rgba(0,190,255,.15);border-radius:2px;margin-top:10px;overflow:hidden;}
 .bar-mini i{display:block;height:100%;border-radius:2px;box-shadow:0 0 8px currentColor;}
+/* 卡内限高滚动区:安全库存预警列表 / 最近作业动态流,内容超出时自动出滚动条(配色用 .wms-home 全局滚动条) */
+.card-scroll{max-height:264px;overflow-y:auto;overscroll-behavior:contain;padding-right:4px;}
 
 /* ========== 图表区 ========== */
-.grid-2-1{display:grid;grid-template-columns:1fr 380px;gap:12px;margin-bottom:12px;}
-.grid-3{display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px;margin-bottom:12px;}
-.grid-2{display:grid;grid-template-columns:1fr 1fr;gap:12px;}
+.grid-2-1{display:grid;grid-template-columns:1fr 380px;gap:14px;margin-bottom:14px;}
+.grid-3{display:grid;grid-template-columns:1fr 1fr 1fr;gap:14px;margin-bottom:14px;}
+.grid-2{display:grid;grid-template-columns:1fr 1fr;gap:14px;}
 .chart-body{padding:8px 18px 16px;}
-.legend{display:flex;gap:14px;font-size:11px;color:var(--t2);}
+.legend{display:flex;gap:14px;font-size:13px;color:var(--t2);}
 .legend i{display:inline-block;width:9px;height:9px;border-radius:2px;margin-right:4px;vertical-align:-1px;}
-table.tbl{width:100%;border-collapse:collapse;font-size:12.5px;}
-table.tbl th{text-align:left;color:var(--th);font-weight:500;padding:9px 10px;border-bottom:1px solid var(--grid);white-space:nowrap;}
+table.tbl{width:100%;border-collapse:collapse;font-size:13px;}
+table.tbl th{text-align:left;color:var(--th);font-weight:500;padding:9px 10px;border-bottom:1px solid rgba(0,190,255,.2);white-space:nowrap;}
 table.tbl td{padding:9px 10px;border-bottom:1px solid var(--grid);white-space:nowrap;color:var(--t1);}
-table.tbl tr:hover td{background:rgba(56,200,245,.06);}
+table.tbl tr:hover td{background:rgba(0,200,255,.06);}
 .num-r{text-align:right;font-variant-numeric:tabular-nums;}
 /* 预警列表 */
-.alert-item{display:flex;align-items:center;gap:10px;padding:9px 18px;border-bottom:1px solid var(--grid);font-size:12.5px;}
+.alert-item{display:flex;align-items:center;gap:10px;padding:9px 18px;border-bottom:1px solid var(--grid);font-size:13px;}
 .alert-item:last-child{border-bottom:none;}
 .alert-item .dot{width:7px;height:7px;border-radius:50%;flex-shrink:0;box-shadow:0 0 8px currentColor;}
 .alert-item .meta{color:var(--t2);margin-left:auto;white-space:nowrap;}
-.alert-item b{color:#EAF4FF;font-weight:600;}
+.alert-item b{color:var(--t1);font-weight:600;}
 /* 动态 feed */
 .feed{padding:6px 18px 14px;}
-.feed-item{display:flex;gap:10px;padding:8px 0;font-size:12.5px;border-bottom:1px dashed var(--grid);color:var(--t2);}
+.feed-item{display:flex;gap:10px;padding:8px 0;font-size:13px;border-bottom:1px dashed var(--grid);color:var(--t2);}
 .feed-item:last-child{border:none;}
-.feed-item .tm{color:var(--t3);white-space:nowrap;font-size:11.5px;padding-top:1px;}
-.feed-item b{color:#EAF4FF;font-weight:600;}
+.feed-item .tm{color:var(--t3);white-space:nowrap;font-size:13px;padding-top:1px;}
+.feed-item b{color:var(--t1);font-weight:600;}
 .feed-status{margin-left:auto;color:var(--green);white-space:nowrap;}
 .feed-status.active{color:var(--cyan);}
 .feed-status.waiting{color:var(--orange);}
@@ -146,8 +233,8 @@ table.tbl tr:hover td{background:rgba(56,200,245,.06);}
    状态四色与待办条同源;.neutral 是「状态未识别」的中性灰——既不当成功绿、也不当待办橙,
    否则「前端不认识的状态」会被读成「已完成」或「在等谁处理」。 */
 .feed-item .feed-doc{flex:0 1 auto;min-width:0;display:inline-flex;align-items:baseline;gap:6px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--t2);}
-.feed-item .feed-no{color:var(--cyan);text-decoration:none;border-bottom:1px dashed rgba(56,200,245,.45);cursor:pointer;font-variant-numeric:tabular-nums;}
-.feed-item .feed-no:hover{color:#EAF4FF;border-bottom-color:var(--cyan);}
+.feed-item .feed-no{color:var(--cyan);text-decoration:none;border-bottom:1px dashed rgba(0,230,255,.45);cursor:pointer;font-variant-numeric:tabular-nums;}
+.feed-item .feed-no:hover{color:var(--t1);border-bottom-color:var(--cyan);}
 .feed-item .feed-no:focus{outline:1px solid var(--cyan);outline-offset:2px;border-radius:2px;}
 /* 动作描述单行省略:卡片宽度固定,长文案必须自己截断,不能把状态段挤出卡片,也不许换行撑高行高。 */
 .feed-item .feed-desc{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--t3);}
@@ -160,90 +247,172 @@ table.tbl tr:hover td{background:rgba(56,200,245,.06);}
 /* 新增事件高亮:3 秒从青色底渐隐到透明。只改背景色,不动 pointer-events、不动 DOM 顺序,
    也不触发任何滚动——30 秒轮询里主动滚动会把用户正在看的位置顶走。 */
 .feed-item.is-new{animation:feed-new-fade 3s ease-out 1;}
-@keyframes feed-new-fade{0%{background:rgba(56,200,245,.22);}55%{background:rgba(56,200,245,.12);}100%{background:transparent;}}
+@keyframes feed-new-fade{0%{background:rgba(0,230,255,.22);}55%{background:rgba(0,230,255,.12);}100%{background:transparent;}}
 
 /* ========== 下钻页 ========== */
-.crumb{display:flex;align-items:center;gap:8px;margin:16px 0 14px;color:var(--t3);font-size:13px;}
-.crumb b{color:#EAF4FF;}
-.crumb .back{color:var(--cyan);cursor:pointer;display:none;align-items:center;gap:4px;}
-.view.drill .crumb .back{display:inline-flex;}
+/* 面包屑:当前页名加粗亮色;「返回首页」做成描边按钮(2026-10-30 用户反馈小链接看不清、难点中)。 */
+.crumb{display:flex;align-items:center;gap:10px;margin:16px 0 14px;color:var(--t2);font-size:13px;}
+.crumb b{color:var(--t1);font-size:15px;font-weight:600;}
+/* 返回首页:实心荧光青主按钮。此前样式挂在 .crumb .back 上但模板没挂 back 类,导致一直是普通链接。 */
+.crumb .back{color:var(--btn-ink);cursor:pointer;display:inline-flex;align-items:center;gap:6px;
+  font-size:14px;font-weight:600;padding:8px 20px;border:1px solid rgba(0,230,255,.8);border-radius:2px;
+  background:var(--btn-grad);letter-spacing:.05em;
+  box-shadow:0 0 14px rgba(0,230,255,.45);transition:all .15s;text-decoration:none;}
+.crumb .back:hover{box-shadow:0 0 22px rgba(0,230,255,.75);transform:translateY(-1px);}
 .drill-head{margin-bottom:12px;}
-.drill-head h2{font-size:17px;font-weight:700;text-shadow:0 0 14px rgba(56,200,245,.25);}
-.drill-head p{font-size:12px;color:var(--t3);margin-top:4px;}
-.def-strip{display:flex;gap:10px;align-items:flex-start;background:rgba(56,200,245,.07);border:1px solid rgba(56,200,245,.28);border-radius:8px;padding:10px 14px;font-size:12px;color:#B9D8EF;margin-bottom:12px;line-height:1.6;}
-.def-strip b{color:var(--cyan);}
+.drill-head h2{font-size:20px;font-weight:700;letter-spacing:.08em;color:#f2fcff;text-shadow:0 0 16px rgba(0,230,255,.55), 0 0 32px rgba(0,160,255,.3);}
+.drill-head h2::before{content:"";display:inline-block;width:4px;height:16px;margin-right:10px;vertical-align:-1px;
+  background:var(--cyan);box-shadow:0 0 8px rgba(0,230,255,.8);}
 .filters{display:flex;gap:10px;align-items:center;padding:12px 14px;flex-wrap:wrap;}
-.filters select,.filters input{height:32px;border:1px solid var(--panel-line);border-radius:6px;padding:0 10px;font-size:12.5px;color:var(--t1);background:rgba(6,15,31,.6);outline:none;}
-.filters select:focus,.filters input:focus{border-color:var(--cyan);box-shadow:0 0 10px rgba(56,200,245,.25);}
-.filters select option{background:#0A1830;color:var(--t1);}
-.filters .lb{font-size:12px;color:var(--t2);}
+.filters select,.filters input{height:32px;border:1px solid var(--panel-line);border-radius:2px;padding:0 10px;font-size:13px;color:var(--t1);background:rgba(2,10,22,.72);outline:none;}
+.filters select:focus,.filters input:focus{border-color:var(--cyan);box-shadow:0 0 10px rgba(0,230,255,.25);}
+.filters select option{background:#031428;color:var(--t1);}
+.filters .lb{font-size:13px;color:var(--t2);}
 .filters input::placeholder{color:var(--t3);}
-.pager{display:flex;align-items:center;justify-content:flex-end;gap:6px;padding:12px 14px;font-size:12px;color:var(--t3);}
-.pager .pg{min-width:26px;height:26px;border:1px solid var(--panel-line);border-radius:5px;display:inline-flex;align-items:center;justify-content:center;background:rgba(6,15,31,.5);cursor:pointer;color:var(--t2);}
+.pager{display:flex;align-items:center;justify-content:flex-end;gap:6px;padding:12px 14px;font-size:13px;color:var(--t3);}
+.pager .pg{min-width:26px;height:26px;border:1px solid var(--panel-line);border-radius:2px;display:inline-flex;align-items:center;justify-content:center;background:rgba(2,10,22,.5);cursor:pointer;color:var(--t2);}
 .pager .pg:hover{border-color:var(--cyan);color:var(--cyan);}
-.pager .pg.on{background:linear-gradient(135deg,#38C8F5,#2E8BE8);border-color:transparent;color:#04121F;font-weight:700;}
-.kpi-strip{display:grid;grid-template-columns:repeat(5,1fr);gap:10px;margin-bottom:12px;}
+.pager .pg.on{background:var(--btn-grad);border-color:transparent;color:var(--btn-ink);font-weight:700;}
+/* auto-fit:指标多时自动均分成多列(io 等视图 5 个指标视觉不变),只有 1 个指标时占满整行——
+   批次库龄明细页只有「库龄来源覆盖率」一张卡,固定 5 列会让长口径说明挤在 1/5 宽里折成十来行。 */
+.kpi-strip{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:10px;margin-bottom:12px;}
 .kpi-strip .kpi{padding:12px 14px;cursor:default;}
 .kpi-strip .kpi .val{font-size:19px;margin:6px 0 2px;}
-/* 页签 */
-.tabs{display:flex;gap:8px;padding:12px 14px 0;flex-wrap:wrap;}
-.tab{padding:7px 14px;border-radius:7px;font-size:13px;color:var(--t2);cursor:pointer;border:1px solid transparent;background:rgba(56,200,245,.05);transition:all .15s;}
-.tab:hover{color:#EAF4FF;border-color:var(--panel-line);}
-.tab.on{background:linear-gradient(135deg,#38C8F5,#2E8BE8);color:#04121F;font-weight:600;box-shadow:0 0 12px rgba(56,200,245,.35);}
-.tab-pane{display:none;}
-.tab-pane.on{display:block;animation:fadeIn .18s ease;}
 /* 盘点任务弹窗 */
-.modal-mask{display:none;position:fixed;inset:0;background:rgba(4,10,22,.62);z-index:60;align-items:center;justify-content:center;}
+.modal-mask{display:none;position:fixed;inset:0;background:rgba(0,4,10,.68);z-index:60;align-items:center;justify-content:center;}
 .modal-mask.on{display:flex;}
-.modal{width:680px;max-width:92%;background:linear-gradient(180deg,#0F2A4C,#0A1B36);border:1px solid rgba(70,160,230,.4);border-radius:12px;box-shadow:0 18px 60px rgba(0,0,0,.5), inset 0 0 24px rgba(35,120,210,.12);overflow:hidden;animation:fadeIn .18s ease;}
-.modal-hd{display:flex;justify-content:space-between;align-items:center;padding:14px 18px;border-bottom:1px solid rgba(70,160,230,.25);}
-.modal-hd h3{font-size:15px;color:#EAF4FF;font-weight:600;display:flex;align-items:center;gap:8px;}
-.modal-hd h3::before{content:"";width:4px;height:14px;background:#38C8F5;border-radius:2px;display:inline-block;}
-.modal-x{cursor:pointer;color:#9FB8D4;font-size:18px;line-height:1;padding:2px 6px;}
-.modal-x:hover{color:#EAF4FF;}
+.modal{width:680px;max-width:92%;background:linear-gradient(165deg, rgba(0,50,85,.92), rgba(2,16,32,.96));border:1px solid rgba(0,210,255,.4);border-radius:2px;box-shadow:0 18px 60px rgba(0,0,0,.55), 0 0 24px rgba(0,190,255,.15);overflow:hidden;animation:fadeIn .18s ease;}
+.modal-hd{display:flex;justify-content:space-between;align-items:center;padding:14px 18px;border-bottom:1px solid rgba(0,210,255,.25);}
+.modal-hd h3{font-size:15px;color:var(--t1);font-weight:600;display:flex;align-items:center;gap:8px;}
+.modal-hd h3::before{content:"";width:4px;height:14px;background:var(--cyan);display:inline-block;box-shadow:0 0 8px rgba(0,230,255,.8);}
+.modal-x{cursor:pointer;color:var(--t2);font-size:18px;line-height:1;padding:2px 6px;}
+.modal-x:hover{color:var(--t1);}
 .modal-ft{display:flex;justify-content:flex-end;gap:10px;padding:12px 18px 16px;}
 /* 库位热力图 */
-.loc{height:52px;border-radius:6px;display:flex;flex-direction:column;align-items:center;justify-content:center;font-size:11px;cursor:pointer;border:1px solid rgba(255,255,255,.10);transition:all .15s;color:#04121F;}
-.loc b{font-size:12.5px;font-weight:700;}
-.loc:hover{transform:translateY(-2px);border-color:var(--cyan);box-shadow:0 0 14px rgba(56,200,245,.45);}
-.legend-box{display:flex;gap:16px;padding:6px 18px 14px;font-size:11px;color:var(--t2);flex-wrap:wrap;}
-.legend-box i{display:inline-block;width:10px;height:10px;border-radius:3px;margin-right:5px;vertical-align:-1px;}
+.loc{height:52px;border-radius:2px;display:flex;flex-direction:column;align-items:center;justify-content:center;font-size:13px;cursor:pointer;border:1px solid rgba(255,255,255,.10);transition:all .15s;color:var(--btn-ink);}
+.loc b{font-size:13px;font-weight:700;}
+.loc:hover{transform:translateY(-2px);border-color:var(--cyan);box-shadow:0 0 14px rgba(0,230,255,.45);}
+.legend-box{display:flex;gap:16px;padding:6px 18px 14px;font-size:13px;color:var(--t2);flex-wrap:wrap;}
+.legend-box i{display:inline-block;width:10px;height:10px;border-radius:2px;margin-right:5px;vertical-align:-1px;}
 /* ========== 单据新增表单页 ========== */
 .frm{display:grid;grid-template-columns:repeat(3,1fr);gap:12px 26px;padding:14px 18px 6px;}
 .frm .it{display:flex;align-items:center;gap:8px;min-width:0;}
-.frm .lb{width:96px;text-align:right;font-size:12.5px;color:var(--t2);flex-shrink:0;}
+.frm .lb{width:96px;text-align:right;font-size:13px;color:var(--t2);flex-shrink:0;}
 .frm .lb.req::before{content:"*";color:var(--red);margin-right:3px;font-weight:700;}
-.frm input,.frm select{flex:1;height:32px;border:1px solid var(--panel-line);border-radius:6px;background:rgba(6,15,31,.6);color:var(--t1);padding:0 10px;font-size:12.5px;outline:none;min-width:0;}
-.frm input:focus,.frm select:focus{border-color:var(--cyan);box-shadow:0 0 10px rgba(56,200,245,.25);}
+.frm input,.frm select{flex:1;height:32px;border:1px solid var(--panel-line);border-radius:2px;background:rgba(2,10,22,.72);color:var(--t1);padding:0 10px;font-size:13px;outline:none;min-width:0;}
+.frm input:focus,.frm select:focus{border-color:var(--cyan);box-shadow:0 0 10px rgba(0,230,255,.25);}
 .frm input::placeholder{color:var(--t3);}
 .frm input[readonly]{color:var(--t3);}
-.frm select option{background:#0A1830;}
+.frm select option{background:#031428;}
 .frm .span2{grid-column:span 2;}
-.btn-pick{height:32px;padding:0 12px;border-radius:6px;border:none;background:linear-gradient(135deg,#38C8F5,#2E8BE8);color:#04121F;font-size:12.5px;font-weight:600;cursor:pointer;flex-shrink:0;}
+.btn-pick{height:32px;padding:0 12px;border-radius:2px;border:none;background:var(--btn-grad);color:var(--btn-ink);font-size:13px;font-weight:600;cursor:pointer;flex-shrink:0;}
 .sec-hd{display:flex;align-items:center;justify-content:space-between;padding:16px 18px 0;}
 .sec-hd h3{font-size:14px;font-weight:600;display:flex;align-items:center;gap:8px;}
-.sec-hd h3::before{content:"";width:4px;height:14px;border-radius:2px;background:var(--cyan);box-shadow:0 0 8px rgba(56,200,245,.7);}
-.btn-add{height:30px;padding:0 14px;border-radius:6px;border:1px solid rgba(56,200,245,.5);background:rgba(56,200,245,.10);color:var(--cyan);font-size:12.5px;cursor:pointer;}
-.btn-add:hover{background:rgba(56,200,245,.2);box-shadow:0 0 12px rgba(56,200,245,.3);}
+.sec-hd h3::before{content:"";width:4px;height:14px;background:var(--cyan);box-shadow:0 0 8px rgba(0,230,255,.8);}
+.btn-add{height:30px;padding:0 14px;border-radius:2px;border:1px solid rgba(0,230,255,.5);background:rgba(0,230,255,.10);color:var(--cyan);font-size:13px;cursor:pointer;}
+.btn-add:hover{background:rgba(0,230,255,.2);box-shadow:0 0 12px rgba(0,230,255,.3);}
 .form-foot{display:flex;justify-content:center;gap:14px;padding:18px 18px 22px;}
 /* ========== 仓库切换器 ========== */
 .ws-wrap{position:relative;}
 .ws-menu{display:none;position:absolute;top:calc(100% + 10px);right:0;min-width:210px;z-index:99;
-  background:linear-gradient(180deg, rgba(16,40,72,.98), rgba(10,26,48,.98));
-  border:1px solid rgba(56,200,245,.5);border-radius:8px;padding:6px;
-  box-shadow:0 8px 30px rgba(0,0,0,.5), 0 0 18px rgba(56,200,245,.2);}
+  background:linear-gradient(180deg, rgba(0,45,80,.96), rgba(2,14,28,.96));
+  border:1px solid rgba(0,230,255,.5);border-radius:2px;padding:6px;
+  box-shadow:0 8px 30px rgba(0,0,0,.55), 0 0 18px rgba(0,230,255,.2);}
 .ws-menu.show{display:block;animation:fadeIn .15s ease;}
 .ws-menu::before{content:"";position:absolute;top:-6px;right:20px;width:10px;height:10px;
-  background:rgba(16,40,72,.98);border-left:1px solid rgba(56,200,245,.5);border-top:1px solid rgba(56,200,245,.5);transform:rotate(45deg);}
-.ws-item{padding:9px 12px;border-radius:6px;font-size:13px;color:var(--t2);cursor:pointer;}
-.ws-item:hover{background:rgba(56,200,245,.12);color:#EAF4FF;}
-.ws-item.on{color:var(--cyan);background:rgba(56,200,245,.10);font-weight:600;}
+  background:rgba(0,45,80,.96);border-left:1px solid rgba(0,230,255,.5);border-top:1px solid rgba(0,230,255,.5);transform:rotate(45deg);}
+.ws-item{padding:9px 12px;border-radius:2px;font-size:13px;color:var(--t2);cursor:pointer;}
+.ws-item:hover{background:rgba(0,230,255,.12);color:var(--t1);}
+.ws-item.on{color:var(--cyan);background:rgba(0,230,255,.10);font-weight:600;}
 /* toast */
 .toast{position:fixed;bottom:26px;left:50%;transform:translateX(-50%) translateY(20px);
-  background:linear-gradient(180deg, rgba(16,40,72,.98), rgba(10,26,48,.98));
-  border:1px solid rgba(56,200,245,.6);color:#EAF4FF;padding:10px 18px;border-radius:8px;font-size:13px;
-  opacity:0;transition:all .25s;pointer-events:none;z-index:99;box-shadow:0 0 26px rgba(56,200,245,.35);}
+  background:linear-gradient(180deg, rgba(0,45,80,.96), rgba(2,14,28,.96));
+  border:1px solid rgba(0,230,255,.6);color:var(--t1);padding:10px 18px;border-radius:2px;font-size:13px;
+  opacity:0;transition:all .25s;pointer-events:none;z-index:99;box-shadow:0 0 26px rgba(0,230,255,.35);}
 .toast.show{opacity:1;transform:translateX(-50%) translateY(0);}
+/* ========== 嵌入看板的业务页(出入库申请/入总库申请/新增出入库单)==========
+   ele-admin 的 el-card / el-tabs / ele-pro-table 默认白底白表头,与看板深色风割裂;
+   这里只对 .dashboard-business 生效,配色对齐看板表格(同 dashboard-element.scss 的 popper 规则)。 */
+.dashboard-business{
+  .el-card{background:linear-gradient(165deg, rgba(0,50,85,.38), rgba(2,16,32,.78) 42%, rgba(0,28,50,.55));
+    border:1px solid var(--panel-line);border-radius:0;box-shadow:var(--inset);color:var(--t1);}
+  /* 页签(border-card 型):入库/出库加大统一尺寸 + 底部发光光条 + 渐变选中态 */
+  .el-tabs--border-card{background:transparent;border-color:var(--panel-line);box-shadow:none;}
+  .el-tabs--border-card > .el-tabs__header{background:rgba(3,20,40,.6);border-bottom-color:var(--panel-line);}
+  .el-tabs--border-card > .el-tabs__header .el-tabs__nav-wrap::after{background-color:transparent;}
+  .el-tabs--border-card > .el-tabs__header .el-tabs__item{
+    height:42px;line-height:42px;padding:0 36px;font-size:14px;font-weight:600;letter-spacing:.12em;
+    color:var(--t2);border-color:transparent;background:transparent;transition:all .2s;position:relative;overflow:hidden;
+  }
+  /* 底部光条:hover 亮起一半,选中铺满并加辉光 */
+  .el-tabs--border-card > .el-tabs__header .el-tabs__item::after{
+    content:"";position:absolute;left:50%;bottom:0;width:0;height:2px;transform:translateX(-50%);
+    background:linear-gradient(90deg,transparent,var(--cyan),transparent);
+    box-shadow:0 0 10px rgba(0,230,255,.9);transition:width .28s ease;
+  }
+  .el-tabs--border-card > .el-tabs__header .el-tabs__item:hover{color:var(--cyan);background:rgba(0,230,255,.06);}
+  .el-tabs--border-card > .el-tabs__header .el-tabs__item:hover::after{width:46%;}
+  .el-tabs--border-card > .el-tabs__header .el-tabs__item.is-active{
+    color:var(--cyan);background:linear-gradient(180deg,rgba(0,230,255,.16),rgba(0,110,180,.05));
+    border-right-color:transparent;border-left-color:transparent;
+    text-shadow:0 0 10px rgba(0,230,255,.65);
+  }
+  .el-tabs--border-card > .el-tabs__header .el-tabs__item.is-active::after{width:78%;}
+  .el-tabs--border-card > .el-tabs__header .el-tabs__item.is-active:first-child{border-left-color:transparent;}
+  .el-tabs--border-card > .el-tabs__header .el-tabs__item.is-active:last-child{border-right-color:transparent;}
+  .el-tabs__content{padding:0;}
+  /* 查询/重置/批量收货等 element 按钮:对齐看板按钮风格 */
+  .el-button--primary:not(.is-disabled){
+    background:var(--btn-grad);border:1px solid rgba(0,230,255,.8);color:var(--btn-ink);
+    border-radius:2px;font-weight:600;box-shadow:0 0 10px rgba(0,230,255,.35);transition:all .15s;
+  }
+  .el-button--primary:not(.is-disabled):hover,.el-button--primary:not(.is-disabled):focus{
+    background:linear-gradient(135deg,#8ff4ff,#19c4ee);color:var(--btn-ink);
+    box-shadow:0 0 20px rgba(0,230,255,.7);transform:translateY(-1px);
+  }
+  /* 禁用态(如未勾选时的批量收货):深色半透明弱化,不抢视觉 */
+  .el-button--primary.is-disabled,.el-button--primary.is-disabled:hover,.el-button--primary.is-disabled:focus{
+    background:rgba(0,180,255,.08);border-color:rgba(0,210,255,.22);color:rgba(143,214,255,.35);box-shadow:none;opacity:1;transform:none;
+  }
+  /* 搜索表单里的「重置」:次操作用 ghost 描边样式,与看板筛选栏「查询(实体)/重置(描边)」一致 */
+  .ele-form-search .el-form-item .el-button + .el-button{
+    background:rgba(0,230,255,.06);border:1px solid var(--panel-line);color:var(--t1);box-shadow:none;font-weight:500;
+  }
+  .ele-form-search .el-form-item .el-button + .el-button:hover,.ele-form-search .el-form-item .el-button + .el-button:focus{
+    background:rgba(0,230,255,.12);border-color:var(--cyan);color:var(--cyan);box-shadow:0 0 12px rgba(0,230,255,.25);transform:translateY(-1px);
+  }
+  /* ele-pro-table / el-table:深色表头 + 深色表体 */
+  .ele-pro-table{background:#031428;}
+  /* ele-pro-table 工具条:真实类名是 .ele-table-tool([class*="toolbar"] 匹配不到它,
+     这是之前白底漏掉的原因);默认底色来自 .ele-table-tool-default 的 background。 */
+  .ele-pro-table [class*="toolbar"],.ele-pro-table .ele-table-tool{background:transparent;color:var(--t2);border-color:var(--panel-line);}
+  .ele-pro-table [class*="toolbar"] .el-button--text,.ele-pro-table [class*="toolbar"] i,
+  .ele-pro-table .ele-table-tool .el-button--text,.ele-pro-table .ele-table-tool i,.ele-pro-table .ele-table-tool .ele-tool-item{color:var(--t2);}
+  .ele-pro-table .ele-table-tool .ele-tool-item:hover,.ele-pro-table .ele-table-tool .ele-tool-item.active{color:var(--cyan);}
+  .el-table,.el-table__expanded-cell,.el-table tr{background:#031428;color:#e4f6ff;}
+  .el-table th.el-table__cell,.el-table th,.el-table__header-wrapper{background:#0B2038;color:#7eb2cd;}
+  .el-table th.el-table__cell > .cell{color:#7eb2cd;font-weight:600;}
+  .el-table td,.el-table th.is-leaf,.el-table--border,.el-table--group{border-color:rgba(0,200,255,.28);}
+  .el-table::before,.el-table--border::after{background-color:rgba(0,200,255,.28);}
+  .el-table td.el-table__cell{border-bottom:1px solid rgba(0,200,255,.16);}
+  .el-table--enable-row-hover .el-table__body tr:hover > td,.el-table__body tr.current-row > td{background:#0E3352;}
+  .el-table .el-link--primary{color:var(--cyan);}
+  .el-table .sort-caret.ascending{border-bottom-color:#7eb2cd;}
+  .el-table .sort-caret.descending{border-top-color:#7eb2cd;}
+  .el-table .ascending .sort-caret.ascending{border-bottom-color:var(--cyan);}
+  .el-table .descending .sort-caret.descending{border-top-color:var(--cyan);}
+  /* 工具条(刷新/密度/列设置)与空数据、分页 */
+  .el-table__toolbar,.el-table__header{background:transparent;}
+  .el-table__empty-block{background:transparent;color:var(--t3);}
+  .el-pagination{color:var(--t2);}
+  .el-pagination button,.el-pagination .el-pager li,.el-pagination__total,.el-pagination__jump{background:transparent;color:var(--t2);}
+  .el-pagination button:disabled{color:var(--t3);}
+  .el-pagination .el-pager li.active{color:var(--cyan);}
+  .el-pagination .el-select .el-input .el-input__inner,.el-pagination .el-input__inner{background:rgba(3,20,40,.7);border-color:var(--panel-line);color:var(--t1);}
+  /* 表格内普通按钮改青色描边风 */
+  .el-button--default{background:rgba(0,230,255,.08);border-color:rgba(0,230,255,.5);color:var(--cyan);}
+  .el-button--default:hover{background:rgba(0,230,255,.18);color:var(--cyan-2);}
+}
 @media (max-width:1400px){
   .greet{flex-wrap:wrap;gap:12px;}
   .kpi-row{grid-template-columns:repeat(3,1fr);}
@@ -252,9 +421,12 @@ table.tbl tr:hover td{background:rgba(56,200,245,.06);}
   /* 待办条任何宽度都强制一行,只收紧间距不允许换行 */
   .todo-item{padding:8px 14px;gap:10px;}
   .todo-num{font-size:24px;}
-  .todo-icon{width:40px;height:40px;border-radius:9px;}
+  .todo-icon{width:40px;height:40px;}
   .todo-icon svg{width:19px;height:19px;}
-  .screen-band .meta{position:static;margin-top:10px;}
+  .screen-band{flex-wrap:wrap;}
+  .band-wing{display:none;}
+  .band-frame{min-width:min(420px,86vw);min-height:50px;padding:5px 40px 7px;}
+  .screen-band .meta{position:static;transform:none;margin-top:10px;}
 }
 
 }

+ 4 - 4
src/views/warehouseManagement/index/dashboard-utils.js

@@ -223,10 +223,10 @@ export function trendRisk(streak, minDays = TREND_RISK_MIN_DAYS) {
 const AGING_SEGMENT_KEYS = ['0-30', '31-90', '91-180', '180+'];
 const AGING_UNKNOWN_KEY = 'unknown';
 const AGING_NEUTRAL_COLOR = '#7E93AD';
-// 分段色阶:前三段顺序色(库龄越久越警示),>180 段固定红 #F25E5E —— 与呆滞分级「呆滞预警(alert)」同一色,
+// 分段色阶(对齐 HR 首页图表调色板):前三段顺序色(库龄越久越警示),>180 段固定红 #F25E5E —— 与呆滞分级「呆滞预警(alert)」同一色,
 // 因为 >180 天正是呆滞口径的候选区间;两处同色,用户才不会把「库龄结构」和「呆滞品占比」读成两套标准。
 // unknown 用中性灰:它不属于四段色阶,也不参与四段占比。
-const AGING_SEGMENT_COLORS = { '0-30': '#38C8F5', '31-90': '#2E8BE8', '91-180': '#F0A03A', '180+': '#F25E5E', unknown: AGING_NEUTRAL_COLOR };
+const AGING_SEGMENT_COLORS = { '0-30': '#00eaff', '31-90': '#3d8bff', '91-180': '#ffb020', '180+': '#F25E5E', unknown: AGING_NEUTRAL_COLOR };
 // 后端 label 缺失时的兜底名(正常路径一律用后端 label,含「>180天」的写法)。
 const AGING_SEGMENT_LABELS = { '0-30': '0-30天', '31-90': '31-90天', '91-180': '91-180天', '180+': '>180天', unknown: '日期待核实' };
 // >180 天段的库龄边界:与呆滞默认阈值(180 天)同界,只用于明细行的红色强调与「建呆滞处置单」入口判定,
@@ -331,9 +331,9 @@ export const ALERT_STATUS_ORDER = ['critical', 'warning', 'excess', 'normal', 'u
 // 状态中文标签:契约要求后端下发 statusLabel,这张表只在后端未下发时兜底,文案与后端逐字一致
 // (critical=缺料 warning=偏低 excess=超储 normal=正常 unknown=待核实)。
 export const ALERT_STATUS_LABELS = { critical: '缺料', warning: '偏低', excess: '超储', normal: '正常', unknown: '待核实' };
-// 配色兜底:后端 row.color 缺失时才启用(critical 红 #F25E5E / warning 橙 #F0A03A / excess 紫 #9B7DF7 / unknown 灰 #7E93AD)。
+// 配色兜底:后端 row.color 缺失时才启用(critical 红 #F25E5E / warning 橙 #ffb020 / excess 紫 #a78bfa / unknown 灰 #7E93AD)。
 // normal 的 color 按契约就是 null(正常行不表达健康度),因此它不在这张表里,也不算「缺失」,不打日志。
-export const ALERT_FALLBACK_COLORS = { critical: '#F25E5E', warning: '#F0A03A', excess: '#9B7DF7', unknown: '#7E93AD' };
+export const ALERT_FALLBACK_COLORS = { critical: '#F25E5E', warning: '#ffb020', excess: '#a78bfa', unknown: '#7E93AD' };
 
 // —— 日志去重状态(模块级)——
 // 预警数据是计算属性,父组件重渲染、5 分钟准实时轮询都会让它反复求值;只有「数据指纹真的变了」才打日志,

+ 12 - 24
src/views/warehouseManagement/index/dashboard.vue

@@ -2,8 +2,8 @@
   <div class="wms-home">
     <main class="main">
         <home-overview v-if="view === 'home'" :summary="summary" :options="options" :warehouse-id="warehouseId" :unit="unit" :updated-at="updatedAt" :error="error" @open="open" @refresh="refresh" @business="business" @warehouse="setWarehouse" @unit="setUnit" @navigate="navigate" />
-        <section v-else-if="businessComponent" class="view active dashboard-business"><nav class="crumb"><a href="#" @click.prevent="open('home')">‹ 返回首页</a><span>仓储管理系统</span> / <b>{{ businessTitle }}</b></nav><div class="drill-head"><h2>{{ businessTitle }}</h2></div><div v-if="receiveQueue.length" class="receive-progress">批量收货:正在核对第 {{ preparedReceipts.length + 1 }} / {{ receiveQueue.length }} 单。请核对仓库、产品明细及包装后确认。<el-button @click="cancelReceive">取消批量收货</el-button></div><div v-if="!receiveQueue.length && preparedReceipts.length" class="receive-progress">有 {{ preparedReceipts.length }} 单待重试。<el-button :loading="receiving" @click="submitReceipts">重试收货</el-button></div><component :is="businessComponent" :key="view + ($route.query.inId || '')" :batch-prepare="receiveQueue.length > 0" @batch-receive="startReceive" @prepared="prepareReceipt" @cancelled="cancelReceive" /></section>
-        <dashboard-detail v-else :key="view" :view="view" :filters="filters" :options="options" :result="result" :summary="summary" :loading="loading" :error="error" :exporting="exporting" :stagnant-days="stagnantDays" @back="open('home')" @navigate="open" @search="search" @retry="loadDetail" @export="exportRows" @business="business" @batch="batchApprove" />
+        <section v-else-if="businessComponent" class="view active dashboard-business"><nav class="crumb"><a href="#" class="back" @click.prevent="open('home')">‹ 返回首页</a><span>仓储管理系统</span> / <b>{{ businessTitle }}</b></nav><div class="drill-head"><h2>{{ businessTitle }}</h2></div><div v-if="receiveQueue.length" class="receive-progress">批量收货:正在核对第 {{ preparedReceipts.length + 1 }} / {{ receiveQueue.length }} 单。请核对仓库、产品明细及包装后确认。<el-button @click="cancelReceive">取消批量收货</el-button></div><div v-if="!receiveQueue.length && preparedReceipts.length" class="receive-progress">有 {{ preparedReceipts.length }} 单待重试。<el-button :loading="receiving" @click="submitReceipts">重试收货</el-button></div><component :is="businessComponent" :key="view + ($route.query.inId || '')" :batch-prepare="receiveQueue.length > 0" @batch-receive="startReceive" @prepared="prepareReceipt" @cancelled="cancelReceive" /></section>
+        <dashboard-detail v-else :key="view" :view="view" :filters="filters" :options="options" :result="result" :summary="summary" :loading="loading" :error="error" :exporting="exporting" :stagnant-days="stagnantDays" @back="open('home')" @navigate="open" @search="search" @retry="loadDetail" @export="exportRows" @business="business" />
     </main>
     <dashboard-actions ref="actions" :options="options" :filters="filters" :alert-rows="alertRows" @success="refresh" @business="business" />
     <!-- 首屏遮罩:看板要等 home-options + home-summary 都回来才有数字,之前是空卡片逐步填充,
@@ -19,7 +19,7 @@
 import HomeOverview from './components/HomeOverview.vue';
 import DashboardDetail from './detail.vue';
 import DashboardActions from './components/DashboardActions.vue';
-import { homeOptions, homeSummary, homeDetail, warehouseTasks, dashboardAction } from '@/api/warehouseManagement/dashboard';
+import { homeOptions, homeSummary, homeDetail, dashboardAction } from '@/api/warehouseManagement/dashboard';
 import { getResourcesTree } from '@/api/layout';
 import { getCurrentUser } from '@/utils/token-util';
 import { monthRange, cleanQuery, findPortalEntry, homeLog, homeWarn } from './dashboard-utils';
@@ -60,6 +60,9 @@ export default {
   watch: { '$route.query': { deep: true, handler() { this.readRoute(); this.refresh(); } } },
   async created() {
     this.active = true; this.generation = 0; this.requests = {}; this.timers = [];
+    // 看板打开期间给 body 打标:让业务页 filter 弹层(el-select/el-date-picker 默认挂 body、无 popper-class)走深色主题。
+    // 用组件生命周期而非路由 query 判断:离开看板(跳转/页签/keep-alive 停用)必然走 deactivated/beforeDestroy,不会残留。
+    document.body.classList.add('wms-dashboard-open');
     this.readRoute();
     try {
       this.options = await homeOptions({ warehouseId: this.warehouseId }); this.optionsLoaded = true;
@@ -72,19 +75,18 @@ export default {
     this.homeLoading = false;
     this.startTimers();
   },
-  // 深色主题只由页面自身的 .wms-home 承担(见 dashboard-element.scss),不再往 body 挂全局 class。
-  // 原来的 body class 依赖路由销毁时机与 $route.query.dashboard 判断,而下钻目标页同样带着 dashboard=1,
-  // 一旦判断落空就把深色主题留给了下一个页签,业务页因此变成深蓝背景。
-  activated() { const resumed = !this.active; this.active = true; this.startTimers(); if (resumed) this.refresh(); },
-  deactivated() { this.stop(); },
-  beforeDestroy() { this.stop(); },
+  // 2026-10-30 恢复 body class(wms-dashboard-open):业务页弹层默认挂 body 又没有 popper-class,
+  // 只能由 body 标记接管。与旧方案的区别:旧方案靠 $route.query.dashboard 判断会残留到下钻业务页;
+  // 现在挂在组件生命周期上,离开看板必然走 deactivated/beforeDestroy,可靠摘除。
+  activated() { document.body.classList.add('wms-dashboard-open'); const resumed = !this.active; this.active = true; this.startTimers(); if (resumed) this.refresh(); },
+  deactivated() { document.body.classList.remove('wms-dashboard-open'); this.stop(); },
+  beforeDestroy() { document.body.classList.remove('wms-dashboard-open'); this.stop(); },
   methods: {
     readRoute() {
       const query = this.$route.query;
       const knownView = screens[query.dashboardView] || businessViews[query.dashboardView] || query.dashboardView === 'home';
       this.view = knownView ? query.dashboardView : 'home';
       this.filters = filterKeys.reduce((r,key) => { if (query[key] != null) r[key] = query[key]; return r; }, { ...monthRange(), page: 1 });
-      if (this.view === 'todo' && !this.filters.type) this.filters.type = 'inventory';
       this.warehouseId = this.filters.warehouseId || ''; this.unit = this.filters.unit || '';
       if (this.view === 'home') this.applyDefaultUnit(Object.prototype.hasOwnProperty.call(query, 'unit'));
     },
@@ -124,7 +126,6 @@ export default {
       if (view === 'stockLedger') return this.$router.push({ path: '/warehouseManagement/stockLedger', query: cleanQuery(extra) }).catch((e) => { if (e.name !== 'NavigationDuplicated') this.$message.error(e.message); });
       const filters = { ...this.filters, page: 1, ...extra };
       if (view !== this.view) ['stockBatchId','age','level','inspection','type','direction'].forEach((key) => { if (!(key in extra)) delete filters[key]; });
-      if (view === 'todo' && !filters.type) filters.type = 'inventory';
       if (view === 'ioapp' && !filters.direction) filters.direction = 'in';
       if (['ioapp','inapp'].includes(view)) {
         filters.status = Object.prototype.hasOwnProperty.call(extra,'status') ? extra.status : '0';
@@ -168,10 +169,6 @@ export default {
       }
     },
     async fetchDetail(filters = this.filters, page = filters.page || 1, limit = 20) {
-      if (this.view === 'flow') {
-        const data = await warehouseTasks({ pageNo: page, pageSize: limit, warehouseScope: true, warehouseId: filters.warehouseId || undefined, month: (filters.startTime || monthRange().startTime).slice(0,7), name: filters.keyword || undefined });
-        return { ...data, records: data.list || data.records || [], total: data.total == null ? data.count : data.total };
-      }
       return homeDetail(cleanQuery({ ...filters, view: this.view, page, limit }));
     },
     async loadDetail() {
@@ -244,7 +241,6 @@ export default {
         homeLog('alerts 行动作分流', { status:row && row.status, action:target, warehouseId:row && row.warehouseId, categoryId:row && row.categoryId, gapQuantity:row && row.gapQuantity, purchaseRequestCode:(row && row.purchaseRequestCode) || null });
         return this.$refs.actions.open(target,row);
       }
-      if (kind === 'flow') return this.portal((n) => /bpm\/todo/.test(n.component || '') || /^\/?todo$/.test(n.path || ''), { taskId:row.id,warehouseScope:'true',warehouseId:this.warehouseId,month:(this.filters.startTime || '').slice(0,7),dashboard:'1' });
       // 打开采购需求:savedPurchase 是刚存下的需求(id 就是需求主键),而预警行只有后端台账回传的
       // purchaseRequestCode(business_code)。只带单号时不能拿 row.id(那是预警行主键)冒充 requirementId,
       // 否则会打开另一张需求单;此时只透传单号,让采购需求页按单号自行定位。
@@ -259,7 +255,6 @@ export default {
       if (kind === 'io' && row.activityType === 'inventory') return this.navigate('/warehouseManagement/stocktaking/work/details',{ id:row.orderId, planId:row.planId });
       if (kind === 'io' && row.activityType === 'transfer') return this.open('io',{ warehouseId:row.warehouseId,keyword:row.orderNo });
       if (kind === 'io') return row.orderId ? this.navigate(String(row.direction) === '1' ? '/warehouseManagement/stockManagement/details' : '/warehouseManagement/outgoingManagement/details',{ id:row.orderId }) : this.$message.warning('该流水缺少来源单据身份,请先核实');
-      if (kind === 'todo') return row.type === 'inventory' ? this.navigate('/warehouseManagement/stocktaking/work/details',{ id:row.id, planId:row.planId }) : this.open(row.type === 'central' ? 'inapp' : 'ioapp',{ direction:row.type === 'out' ? 'out' : 'in',keyword:row.orderNo });
       if (kind === 'turnover') return this.open('stockLedger');
       if (['batch','age'].includes(kind)) return this.navigate('/warehouseManagement/stockLedger/batchDetails',{ id:row.categoryId,batchNo:row.batchNo,systemBatchNo:row.systemBatchNo,stockBatchId:row.stockBatchId || row.id,warehouseId:row.warehouseId });
       if (kind === 'disposal-document') return this.navigate(row.documentType === 'ALLOT' ? '/warehouseManagement/inventoryAllocation/details' : '/warehouseManagement/outgoingManagement/details',{ id:row.documentId });
@@ -295,13 +290,6 @@ export default {
         this.route('ioapp',{ ...this.filters,direction:'in',status:'0' },true); this.$refs.actions.showResults(results,'成功表示已保存并提交原审批或质检;库存是否增加取决于原流程的执行结果。'); this.refresh();
       } catch (e) { if (e !== 'cancel' && e !== 'close') this.$message.error(e.message || '收货失败,保留原信息供重试'); this.receiveQueue = []; this.route('ioapp',{ ...this.filters,direction:'in',status:'0' },true); }
       finally { this.receiving = false; }
-    },
-    async batchApprove(rows) {
-      try {
-        const { value } = await this.$prompt('审批意见(将逐单校验当前节点权限)','批量审批',{ inputValue:'同意',inputValidator:(v) => !!(v || '').trim(),inputErrorMessage:'请输入审批意见' });
-        const results = await dashboardAction('batch-approve',{ taskIds:rows.map((r) => r.id),reason:value,month:(this.filters.startTime || '').slice(0,7),warehouseId:this.warehouseId || undefined });
-        this.$refs.actions.showResults(results); this.refresh();
-      } catch (e) { if (e !== 'cancel' && e !== 'close') this.$message.error(e.message || '批量审批失败,请重试'); }
     }
   }
 };

+ 14 - 29
src/views/warehouseManagement/index/detail.vue

@@ -1,25 +1,19 @@
 <template>
   <section class="wms-drill view active">
-    <nav class="crumb"><a href="#" @click.prevent="$emit('back')">‹ 返回首页</a><span>仓储管理系统</span> / <b>{{ current.title }}</b></nav>
-    <div class="drill-head"><h2>{{ current.title }}</h2><p>{{ current.subtitle }}</p></div>
-    <div class="def-strip"><b>口径说明:</b><span>{{ current.definition }}<br v-if="result.reason"><span v-if="result.reason">{{ result.reason }}</span></span></div>
+    <nav class="crumb"><a href="#" class="back" @click.prevent="$emit('back')">‹ 返回首页</a><span>仓储管理系统</span> / <b>{{ current.title }}</b></nav>
+    <div class="drill-head"><h2>{{ current.title }}</h2></div>
     <div v-if="current.metrics && current.metrics.length" class="kpi-strip">
       <!-- 数值走 metricValue:百分点差值(qualityChange)自带符号与 pp,模板不再追加单位小字(避免 +1.20pppp)。 -->
-      <div v-for="key in current.metrics" :key="key" class="card kpi"><div class="lb">{{ metricLabels[key][0] }}</div><div class="val">{{ metricValue(key) }}<small v-if="summary[key] != null && metricLabels[key][1]">{{ metricLabels[key][1] }}</small></div><div class="delta dim">{{ summary[metricLabels[key][2]] || '' }}</div></div>
+      <div v-for="key in current.metrics" :key="key" class="card kpi"><div class="lb">{{ metricLabels[key][0] }}</div><div class="val">{{ metricValue(key) }}<small v-if="summary[key] != null && metricLabels[key][1]">{{ metricLabels[key][1] }}</small></div></div>
     </div>
     <div class="card corner" v-loading="loading">
-      <div v-if="view === 'todo'" class="tabs"><button class="tab" :class="{ on: query.type === 'inventory' }" @click="changeType('inventory')">盘点任务</button><button class="tab" @click="$emit('navigate', 'flow')">待我审批</button></div>
       <div class="filters">
         <label><span>仓库</span><select v-model="query.warehouseId"><option value="">全部授权仓库</option><option v-for="item in options.warehouses || []" :key="item.id" :value="item.id">{{ item.name }}</option></select></label>
-        <label v-if="!['todo','flow'].includes(view)"><span>单位</span><select v-model="query.unit"><option value="">按单位分别展示</option><option v-for="item in options.units || []" :key="item.unit" :value="item.unit">{{ item.unit }}</option></select></label>
-        <label v-if="!['flow', 'todo'].includes(view)"><span>物料</span><select v-model="query.categoryId"><option value="">全部</option><option v-for="item in options.categories || []" :key="item.id" :value="item.id">{{ item.name }}</option></select></label>
-        <!-- qc 明细页不渲染可编辑的开始/结束日期:后端 queryHomeDetail 对 qc 强制固定本月(YearMonth.now()),
-             传 startTime/endTime 也不会生效——静默失效的筛选比没有筛选更糟,因此改为下面的只读口径提示。
-             其它 period 视图(io/age/turnover/dead)条件不变,仍是可编辑日期区间。 -->
-        <label v-if="current.period && !['flow', 'qc'].includes(view)"><span>开始日期</span><input v-model="startDate" type="date"></label><label v-if="current.period && !['flow', 'qc'].includes(view)"><span>结束日期</span><input v-model="endDate" type="date"></label>
-        <!-- 只读提示占用原日期筛选位置:复用 <label>/<span> 现有结构,不新增样式;时间窗由后端固定,不给输入控件。 -->
-        <label v-if="view === 'qc'" title="较上月为同口径的百分点差值;本页时间窗由后端固定,不接受自定义区间"><span>统计区间</span>本月(检验完成日期,1 号至今)</label>
-        <label v-if="view === 'flow'"><span>任务到达月份</span><input v-model="flowMonth" type="month"></label>
+        <label><span>单位</span><select v-model="query.unit"><option value="">按单位分别展示</option><option v-for="item in options.units || []" :key="item.unit" :value="item.unit">{{ item.unit }}</option></select></label>
+        <label><span>物料</span><select v-model="query.categoryId"><option value="">全部</option><option v-for="item in options.categories || []" :key="item.id" :value="item.id">{{ item.name }}</option></select></label>
+        <!-- qc 明细页不渲染开始/结束日期:后端 queryHomeDetail 对 qc 强制固定本月,传了也不生效,
+             因此不给输入控件(静默失效的筛选比没有筛选更糟)。其它 period 视图(io/age/turnover/dead)不变。 -->
+        <label v-if="current.period && !['qc'].includes(view)"><span>开始日期</span><input v-model="startDate" type="date"></label><label v-if="current.period && !['qc'].includes(view)"><span>结束日期</span><input v-model="endDate" type="date"></label>
         <label v-if="view === 'io'"><span>方向</span><select v-model="query.direction"><option value="">全部</option><option value="1">入库</option><option value="2">出库</option></select></label>
         <label v-if="view === 'age'"><span>库龄</span><select v-model="query.age"><option value="">全部</option><option value="0-30">0-30 天(≤30 天)</option><option value="31-90">31-90 天</option><option value="91-180">91-180 天(≤180 天)</option><option value="180+">&gt;180 天(与呆滞口径同界)</option><option value="unknown">日期待核实(不参与四段占比)</option></select></label>
                 <!-- 预警类型与新口径 statusLabel 逐字对齐:unknown=待核实(无唯一有效阈值配置或可用量为负),
@@ -31,11 +25,10 @@
         <button v-if="view === 'io'" class="btn ghost" @click="$emit('navigate', 'addin')">新增入库单</button><button v-if="view === 'io'" class="btn ghost" @click="$emit('navigate', 'addout')">新增出库单</button>
         <button v-if="view === 'dead'" class="btn ghost" @click="$emit('business', 'disposal-list', {})">处置申请与财务登记</button>
         <button v-if="view === 'alerts'" class="btn ghost" @click="$emit('business', 'threshold', {})">配置安全库存</button>
-        <button v-if="view === 'flow'" class="btn ghost" :disabled="!selected.length" @click="$emit('batch', selected)">批量审批({{ selected.length }})</button>
       </div>
       <div v-if="error" class="data-note bad" role="alert">{{ error }} <button @click="$emit('retry')">重试</button></div>
-      <div class="table-wrap"><table class="tbl"><thead><tr><th v-if="view === 'flow'"><input type="checkbox" :checked="rows.length > 0 && selected.length === rows.length" aria-label="选择当前页全部任务" @change="selectAll($event.target.checked)"></th><th v-for="column in current.columns" :key="column.key" :class="{ 'num-r': column.numeric }">{{ column.name }}</th><th>操作</th></tr></thead><tbody>
-        <tr v-for="(row, index) in rows" :key="row.id || index" :class="{ 'overdue-row': row.overdue }"><td v-if="view === 'flow'"><input v-model="selected" type="checkbox" :value="row" :aria-label="'选择任务' + (row.businessCode || row.name)"></td><td v-for="column in current.columns" :key="column.key" :class="{ 'num-r': column.numeric, 'aging-overdue': column.key === 'ageDays' && agingStagnant(row), 'alert-state': view === 'alerts' && column.key === 'status' }" :style="alertCellStyle(row, column.key)" :title="cellTitle(row, column.key)">{{ cell(row, column.key) }}<span v-if="column.key === 'dueTime' && row.overdue" class="badge-overdue">逾期</span></td><td class="row-actions">
+      <div class="table-wrap"><table class="tbl"><thead><tr><th v-for="column in current.columns" :key="column.key" :class="{ 'num-r': column.numeric }">{{ column.name }}</th><th>操作</th></tr></thead><tbody>
+        <tr v-for="(row, index) in rows" :key="row.id || index" :class="{ 'overdue-row': row.overdue }"><td v-for="column in current.columns" :key="column.key" :class="{ 'num-r': column.numeric, 'aging-overdue': column.key === 'ageDays' && agingStagnant(row), 'alert-state': view === 'alerts' && column.key === 'status' }" :style="alertCellStyle(row, column.key)" :title="cellTitle(row, column.key)">{{ cell(row, column.key) }}<span v-if="column.key === 'dueTime' && row.overdue" class="badge-overdue">逾期</span></td><td class="row-actions">
           <!-- 非预警视图沿用原来的单一动作入口(io/dead/age/... 行为一字不变)。 -->
           <a v-if="view !== 'alerts'" class="link" href="#" @click.prevent="$emit('business', view, row)">{{ current.action }}</a>
           <!-- 预警行的闭环动作按状态分流(优先级与理由见脚本里的 alertActionOf):超储=发起调拨、待核实=核对阈值、
@@ -49,11 +42,8 @@
             <a v-if="row.alertAction.primary !== 'threshold'" class="link" href="#" @click.prevent="alertBusiness(row, 'threshold')">配置阈值</a>
           </template>
           <a v-if="view === 'dead'" class="link" href="#" @click.prevent="$emit('business', 'batch', row)">批次流水</a><a v-if="view === 'age' && agingStagnant(row)" class="link aging-dispose" href="#" title="与呆滞口径联动:库龄越过 >180 天段即可建呆滞处置单" @click.prevent="disposeAge(row)">建呆滞处置单</a></td></tr>
-        <tr v-if="!loading && !rows.length"><td class="empty" :colspan="current.columns.length + (view === 'flow' ? 2 : 1)">{{ error ? '数据加载失败,请重试' : result.reason || '当前筛选无明细数据' }}</td></tr>
+        <tr v-if="!loading && !rows.length"><td class="empty" :colspan="current.columns.length + 1">{{ error ? '数据加载失败,请重试' : result.reason || '当前筛选无明细数据' }}</td></tr>
       </tbody></table></div>
-      <!-- 预警口径条:新口径必须写在列表页上,否则「可用量为什么小于在库量」「缺口金额为什么是待补价」只能靠猜。
-           首页卡片与这里同一口径,文案与后端 alertRows 逐条对齐。 -->
-      <div v-if="view === 'alerts'" class="alert-scope">口径:可用量=在库量−锁定量(含待检、未放行批次)· 缺口量=安全库存−可用量(不小于 0,仅缺料/偏低有意义)· 缺口金额=缺口量×移动加权不含税单价,单价缺失显示「待补价」且不计入合计(绝不按 0 计)· 按缺口金额倒序,缺价行排最后,其次 缺料→偏低→超储→待核实</div>
       <div class="pager">共 {{ show(result.total) }} 条 · 每页 20 条 <button class="pg" :disabled="Number(filters.page || 1) <= 1" @click="page(-1)">‹</button><strong>{{ filters.page || 1 }}</strong><button class="pg" :disabled="!result.total || Number(filters.page || 1) * 20 >= result.total" @click="page(1)">›</button></div>
     </div>
   </section>
@@ -88,7 +78,7 @@ export default {
   // 字段名与后端契约一致(stagnantDays):由 dashboard.vue 从同一份 home-summary 透传,
   // 用于替换口径条里的 {stagnantDays} 占位符,不额外请求接口。
   props: { view: String, filters: { type: Object, default: () => ({}) }, options: { type: Object, default: () => ({}) }, result: { type: Object, default: () => ({}) }, summary: { type: Object, default: () => ({}) }, error: String, loading: Boolean, exporting: Boolean, stagnantDays: { type: Number, default: null } },
-  data() { return { query: {}, selected: [], metricLabels }; },
+  data() { return { query: {}, metricLabels }; },
   computed: {
     // 口径条(副标题 + 口径说明):把 {stagnantDays} 换成与首页卡片同一配置项的实际阈值。
     // 阈值取自 props.stagnantDays(首页已请求到的 home-summary.stagnantDays),缺失时回落默认 180 天;
@@ -113,11 +103,10 @@ export default {
       if (this.view !== 'alerts' || !Array.isArray(list)) return list;
       return list.map((row) => ({ ...row, alertAction: alertActionOf(row) }));
     },
-    flowMonth: { get() { return (this.query.startTime || '').slice(0,7); },set(v) { this.$set(this.query,'startTime',v ? v + '-01 00:00:00' : ''); this.$set(this.query,'endTime',v ? v + '-' + new Date(Number(v.slice(0,4)),Number(v.slice(5,7)),0).getDate() + ' 23:59:59' : ''); } },
     startDate: { get() { return (this.query.startTime || '').slice(0, 10); }, set(v) { this.$set(this.query, 'startTime', v ? v + ' 00:00:00' : ''); } },
     endDate: { get() { return (this.query.endTime || '').slice(0, 10); }, set(v) { this.$set(this.query, 'endTime', v ? v + ' 23:59:59' : ''); } }
   },
-  watch: { filters: { immediate: true, deep: true, handler(v) { this.query = { ...v }; } }, result() { this.selected = []; } },
+  watch: { filters: { immediate: true, deep: true, handler(v) { this.query = { ...v }; } } },
   methods: {
     show: displayNumber,
     // 明细 KPI 条数值:百分点差值(qualityChange)必须带显式符号与 pp 单位,直接复用公共的 formatPpChange
@@ -129,8 +118,6 @@ export default {
     },
     search() { if (this.query.startTime && this.query.endTime && this.query.startTime > this.query.endTime) return this.$message.warning('开始日期不能晚于结束日期'); this.$emit('search', { ...this.query, page: 1 }); },
     page(offset) { this.$emit('search', { ...this.filters, page: Number(this.filters.page || 1) + offset }); },
-    changeType(type) { this.$emit('search', { ...this.filters, type, page: 1 }); },
-    selectAll(checked) { this.selected = checked ? this.rows.slice() : []; },
     // >180 天段的行(ageDays 越过后端固定分段 180+ 的红线):库龄单元格红色 + 给「建呆滞处置单」入口。
     // 阈值与呆滞默认阈值同界,只做视觉强调,不在前端重算库龄;ageDays 缺失一律 false(日期待核实不染红)。
     agingStagnant(row) { return isStagnantAge(row && row.ageDays); },
@@ -235,13 +222,11 @@ export default {
 table.tbl td.aging-overdue { color: #F25E5E; font-weight: 600; }
 /* 处置入口沿用行内 .link 的既有样式,只补左间距(row-actions 的 a + a 规则同样生效)。 */
 .aging-dispose { color: #F25E5E; }
-/* 预警口径条:中性小字,与表头同宽,不抢数据视觉;表下说明而不是弹层,扫表时随手可核对。 */
-.alert-scope { padding: 8px 18px 0; font-size: 11px; line-height: 1.7; color: #9FB8D4; }
 /* 预警类型列加粗:三色(缺料红/偏低橙/超储紫/待核实灰)由 alertCellStyle 内联下发,这里只管字重。 */
 table.tbl td.alert-state { font-weight: 600; }
 /* 行尾「已生成申请」标记:与首页预警卡同一视觉(紫色描边小标签),提示该行已经下过单、不要再重复发起;
    标记只做识别,不承担禁用逻辑(禁用视觉态在动作弹层里)。 */
-.row-actions .alert-requested { display: inline-block; margin-right: 8px; padding: 0 6px; border: 1px solid rgba(155,125,247,.55); border-radius: 9px; color: #C9B6FF; font-size: 11px; white-space: nowrap; }
+.row-actions .alert-requested { display: inline-block; margin-right: 8px; padding: 0 6px; border: 1px solid rgba(155,125,247,.55); border-radius: 9px; color: #C9B6FF; font-size:13px; white-space: nowrap; }
 /* 兜底占位:既没有缺料也没有已生成申请的行(normal/未识别状态)显示「—」,
    避免操作列空白被读成「这一行还没渲染完」。 */
 .row-actions .alert-no-action { color: #9FB8D4; }