lunanting 55b755c1d9 feat: 新增「缺货补货监控」看板(拣货位缺货 → 从存货位补货)
数据源:聚水潭「箱及仓位库存」rpt.jst_packInventoryInfo(副本里是视图,
按 SKU+货位聚合单次 35~45s,因此服务端缓存 10 分钟 + 启动预热)。

口径:
- 拣货位:按业务导入的货位清单判定(本地表 bin_replenish_pick_bin,按仓库隔离);
  副本没有货位类型主数据,未导入时回退为 pack_type='Bin' 的记录,页面明确标注。
- 拣货位库存 / 存货位库存 / 现有库存合计;
- 目标库存 = max(10, 近15天日销 × 目标天数(默认7));
- 建议补货量 = min(存货位库存, 目标 − 拣货位库存);
- 状态:拣货位缺货 / 无货可补 / 拣货位偏低 / 未设拣货位 / 正常。
  「未设拣货位」单列,避免把整箱直发商品误报成缺货。

后端:binReplenishService + binReplenishSchema + /api/bin-replenish
(list / export / pick-bins GET-POST-DELETE / ping),建表与预热挂在 localSchema。
前端:/planning/operation/bin-replenish-monitor 页面、侧栏入口、页面权限注册,
含 KPI 卡、状态分段筛选、关键字搜索、目标天数、货位清单导入/清除、服务端导出。
2026-10-22 11:08:20 +08:00

304 lines
5.9 KiB
CSS

/* 缺货补货监控 —— 页头 / 指标卡 / 工具栏 / 明细表 */
.brp-page {
--brp-gap: 12px;
--brp-border: var(--sycm-border-card, #e6e6e6);
--brp-radius: var(--sycm-radius-xl, 12px);
--brp-shadow: var(--sycm-shadow-card, 0 1px 3px rgba(0, 0, 0, 0.035));
--brp-blue: #2062e6;
--brp-red: #e5484d;
--brp-orange: #e8890c;
--brp-grey: #6b7280;
--brp-green: #1bb574;
}
/* ── 页头 ───────────────────────────────────────────────── */
.brp-hero {
display: flex;
flex-wrap: wrap;
align-items: flex-end;
justify-content: space-between;
gap: 12px 24px;
margin-bottom: var(--brp-gap);
}
.brp-hero__main {
min-width: 320px;
}
.brp-hero__title {
margin: 0;
color: var(--sycm-text-strong, #262626);
font-size: 20px;
font-weight: 600;
line-height: 1.3;
}
.brp-hero__desc {
margin: 4px 0 0;
max-width: 820px;
color: var(--sycm-text-secondary, #595959);
font-size: 12px;
line-height: 1.6;
}
.brp-hero__meta {
display: grid;
gap: 3px;
color: var(--sycm-text-secondary, #595959);
font-size: 12px;
line-height: 1.6;
text-align: right;
}
.brp-warn {
color: var(--brp-orange);
}
.brp-ok {
color: var(--brp-green);
}
/* ── 指标卡 ─────────────────────────────────────────────── */
.brp-metrics {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: var(--brp-gap);
margin-bottom: var(--brp-gap);
}
.brp-metric {
position: relative;
overflow: hidden;
padding: 12px 14px 11px;
border: 1px solid var(--brp-border);
border-radius: var(--brp-radius);
background: var(--sycm-bg-container, #fff);
box-shadow: var(--brp-shadow);
}
.brp-metric::before {
content: '';
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 3px;
background: var(--brp-tone, var(--brp-blue));
}
.brp-metric--red { --brp-tone: var(--brp-red); }
.brp-metric--orange { --brp-tone: var(--brp-orange); }
.brp-metric--blue { --brp-tone: var(--brp-blue); }
.brp-metric--grey { --brp-tone: var(--brp-grey); }
.brp-metric__label {
display: flex;
align-items: center;
gap: 4px;
color: var(--sycm-text-tertiary, #8c8c8c);
font-size: 12px;
cursor: help;
}
.brp-metric__info {
font-size: 11px;
}
.brp-metric__value {
margin-top: 6px;
color: var(--sycm-text-strong, #262626);
font-size: 24px;
font-variant-numeric: tabular-nums;
font-weight: 600;
line-height: 1.15;
}
.brp-metric__unit {
margin-left: 4px;
color: var(--sycm-text-tertiary, #8c8c8c);
font-size: 12px;
font-weight: 400;
}
.brp-metric__foot {
margin-top: 6px;
color: var(--sycm-text-secondary, #595959);
font-size: 12px;
line-height: 1.4;
}
/* ── 工具栏 ─────────────────────────────────────────────── */
.brp-toolbar {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px 12px;
margin-bottom: 10px;
padding: 10px 12px;
border: 1px solid var(--brp-border);
border-radius: var(--brp-radius);
background: var(--sycm-bg-container, #fff);
box-shadow: var(--brp-shadow);
}
.brp-toolbar__field {
display: inline-flex;
align-items: center;
gap: 6px;
}
.brp-toolbar__label {
color: var(--sycm-text-tertiary, #8c8c8c);
font-size: 12px;
white-space: nowrap;
}
.brp-toolbar__spacer {
flex: 1 1 auto;
}
.brp-alert {
margin-bottom: 10px;
}
/* ── 表格 ───────────────────────────────────────────────── */
.brp-table-spin {
display: block;
}
.brp-table .ant-table-thead > tr > th {
background: #fafafa;
font-size: 12px;
font-weight: 600;
white-space: nowrap;
}
.brp-table .ant-table-tbody > tr > td {
font-size: 12px;
}
.brp-status-tag {
margin-inline-end: 0;
}
.brp-sku {
display: flex;
flex-direction: column;
gap: 2px;
}
.brp-sku__code {
color: var(--sycm-text-strong, #262626);
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 12px;
}
.brp-sku__supplier {
overflow: hidden;
color: var(--sycm-text-tertiary, #8c8c8c);
font-size: 11px;
text-overflow: ellipsis;
white-space: nowrap;
}
.brp-bins {
display: flex;
flex-wrap: wrap;
gap: 4px;
}
.brp-bin {
margin: 0;
padding: 0 6px;
border-color: #d9e2f2;
background: #f5f8ff;
color: #2b4a80;
font-size: 11px;
line-height: 18px;
}
.brp-bin b {
margin-left: 4px;
font-variant-numeric: tabular-nums;
}
.brp-bin.is-zero {
border-color: #f3d2d2;
background: #fff6f6;
color: #a33a3a;
}
.brp-bin--more {
border-color: #eee;
background: #fafafa;
color: #8c8c8c;
}
.brp-qty {
font-variant-numeric: tabular-nums;
font-weight: 500;
}
.brp-qty--zero {
color: var(--brp-red);
font-weight: 600;
}
.brp-qty--suggest {
color: var(--brp-blue);
font-weight: 600;
}
.brp-qty--muted {
color: var(--sycm-text-tertiary, #8c8c8c);
font-weight: 400;
}
.brp-muted {
color: var(--sycm-text-tertiary, #8c8c8c);
}
/* ── 口径说明 ───────────────────────────────────────────── */
.brp-notes {
margin-top: 14px;
padding: 12px 14px;
border: 1px dashed var(--brp-border);
border-radius: var(--brp-radius);
background: var(--sycm-bg-container, #fff);
color: var(--sycm-text-secondary, #595959);
font-size: 12px;
line-height: 1.7;
}
.brp-notes__title {
margin-bottom: 6px;
color: var(--sycm-text-strong, #262626);
font-weight: 600;
}
.brp-notes ul {
margin: 0;
padding-left: 18px;
}
.brp-notes li + li {
margin-top: 4px;
}
@media (max-width: 1200px) {
.brp-metrics {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.brp-hero__meta {
text-align: left;
}
}
@media (max-width: 640px) {
.brp-metrics {
grid-template-columns: minmax(0, 1fr);
}
}