20260706002

This commit is contained in:
xiaojibeier
2026-07-06 17:05:09 +08:00
parent fcf6aa2df6
commit a602334039
4 changed files with 532 additions and 803 deletions
File diff suppressed because it is too large Load Diff
@@ -2,7 +2,7 @@
<div class="gt-wh-panel" v-loading="panelLoading" element-loading-text="加载中…"> <div class="gt-wh-panel" v-loading="panelLoading" element-loading-text="加载中…">
<div v-if="showSchemaHint" class="gt-wh-hint"> <div v-if="showSchemaHint" class="gt-wh-hint">
<el-icon><InfoFilled /></el-icon> <el-icon><InfoFilled /></el-icon>
<span>仓库管理为系统固定结构左侧选 RDC上方横向切换费用票据填写提成 / 利润内部零售渠道的率与额</span> <span>仓库管理左侧选 RDC右侧按费用票据分组填写提成 / 利润各渠道的率与额</span>
</div> </div>
<template v-if="!rdcOptions.length"> <template v-if="!rdcOptions.length">
@@ -22,71 +22,70 @@
<span v-if="rdcDisplaySub(rdc)" class="gt-wh-rdc-nav-sub">{{ rdcDisplaySub(rdc) }}</span> <span v-if="rdcDisplaySub(rdc)" class="gt-wh-rdc-nav-sub">{{ rdcDisplaySub(rdc) }}</span>
</button> </button>
</nav> </nav>
<div class="gt-wh-main">
<div v-if="showLegacyRdcHint" class="gt-wh-legacy-hint">
当前 RDC 存在历史格式数据未按票据分桶请选择票据后重新填写并保存以迁移到新结构
</div>
<div class="gt-wh-workspace">
<template v-if="!expenseBills.length"> <template v-if="!expenseBills.length">
<el-empty :image-size="64" description="暂无费用票据" class="gt-wh-empty" /> <el-empty :image-size="64" description="暂无费用票据" class="gt-wh-empty" />
</template> </template>
<template v-else-if="!activeBillCode">
<el-empty :image-size="64" description="请选择费用票据" class="gt-wh-empty" />
</template>
<template v-else> <template v-else>
<el-tabs v-model="activeBillCode" class="gt-wh-bill-tabs"> <div v-if="showLegacyRdcHint" class="gt-wh-legacy-hint gt-wh-legacy-hint--workspace">
<el-tab-pane 当前 RDC 存在历史格式数据未按票据分桶请重新填写并保存以迁移到新结构
v-for="bill in expenseBills" </div>
:key="bill.bill_code" <div class="gt-wh-bills-scroll">
:name="normBillCode(bill.bill_code)"
>
<template #label>
<span class="gt-wh-bill-tab" :title="billTabTitle(bill)">
<span class="gt-wh-bill-tab-name">{{ billTabParts(bill).name }}</span>
<span v-if="billTabParts(bill).tax" class="gt-wh-bill-tab-tax">{{ billTabParts(bill).tax }}%</span>
</span>
</template>
</el-tab-pane>
</el-tabs>
<div class="gt-wh-form-body">
<div <div
v-for="sec in WAREHOUSE_SECTIONS" v-for="bill in expenseBills"
:key="sec.key" :key="normBillCode(bill.bill_code)"
class="gt-wh-section-block" class="gt-wh-bill-group"
> >
<div class="gt-wh-section-title">{{ sec.label }}</div> <div class="gt-wh-bill-group-title">
<div class="gt-wh-channel-grid"> <span class="gt-wh-bill-group-name">{{ billTabParts(bill).name }}</span>
<div <span v-if="billTabParts(bill).tax" class="gt-wh-bill-group-tax">{{ billTabParts(bill).tax }}%</span>
v-for="ch in WAREHOUSE_CHANNELS" </div>
:key="ch.key" <div class="gt-wh-bill-group-body">
class="gt-wh-channel-card" <div class="gt-wh-sections-row">
> <div
<div class="gt-wh-channel-title">{{ ch.label }}</div> v-for="sec in WAREHOUSE_SECTIONS"
<div class="gt-wh-metrics"> :key="`${normBillCode(bill.bill_code)}-${sec.key}`"
<div class="gt-wh-metric"> class="gt-wh-section-block"
<span class="gt-wh-metric-label"></span> :class="sectionBlockClass(sec.key)"
<el-input-number >
:model-value="metricValue(sec.key, ch.key, 'rate')" <div class="gt-wh-section-title">{{ sec.label }}</div>
:disabled="readonly" <div class="gt-wh-channel-grid">
:controls="false" <div
placeholder="—" v-for="ch in WAREHOUSE_CHANNELS"
size="small" :key="`${normBillCode(bill.bill_code)}-${sec.key}-${ch.key}`"
class="gt-wh-metric-input" class="gt-wh-channel-card"
@update:model-value="(v) => setMetric(sec.key, ch.key, 'rate', v)" >
/> <div class="gt-wh-channel-title">{{ ch.label }}</div>
<span class="gt-wh-metric-suffix">%</span> <div class="gt-wh-metrics">
</div> <div class="gt-wh-metric">
<div class="gt-wh-metric"> <span class="gt-wh-metric-label"></span>
<span class="gt-wh-metric-label">额</span> <el-input-number
<el-input-number :model-value="metricValue(sec.key, ch.key, 'rate', bill.bill_code)"
:model-value="metricValue(sec.key, ch.key, 'amount')" :disabled="readonly"
:disabled="readonly" :controls="false"
:controls="false" placeholder="—"
placeholder="" size="small"
size="small" class="gt-wh-metric-input"
class="gt-wh-metric-input" @update:model-value="(v) => setMetric(sec.key, ch.key, 'rate', v, bill.bill_code)"
@update:model-value="(v) => setMetric(sec.key, ch.key, 'amount', v)" />
/> <span class="gt-wh-metric-suffix">%</span>
</div>
<div class="gt-wh-metric">
<span class="gt-wh-metric-label">额</span>
<el-input-number
:model-value="metricValue(sec.key, ch.key, 'amount', bill.bill_code)"
:disabled="readonly"
:controls="false"
placeholder=""
size="small"
class="gt-wh-metric-input"
@update:model-value="(v) => setMetric(sec.key, ch.key, 'amount', v, bill.bill_code)"
/>
<span class="gt-wh-metric-suffix"></span>
</div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -120,9 +119,7 @@ import {
const props = defineProps({ const props = defineProps({
modelValue: { type: Object, default: () => ({}) }, modelValue: { type: Object, default: () => ({}) },
readonly: { type: Boolean, default: false }, readonly: { type: Boolean, default: false },
/** 全局页签管理:展示结构说明 */
showSchemaHint: { type: Boolean, default: false }, showSchemaHint: { type: Boolean, default: false },
/** 物料 /goods/view 返回的 rdcs;公共页签、物料分类留空即可 */
rdcList: { type: Array, default: () => [] }, rdcList: { type: Array, default: () => [] },
}) })
@@ -136,7 +133,6 @@ const billsLoaded = ref(false)
/** @type {import('vue').Ref<object[]>} */ /** @type {import('vue').Ref<object[]>} */
const allBills = ref([]) const allBills = ref([])
const activeRdcCode = ref('') const activeRdcCode = ref('')
const activeBillCode = ref('')
const panelLoading = computed(() => billLoading.value) const panelLoading = computed(() => billLoading.value)
@@ -147,7 +143,6 @@ const bucket = computed({
set: (v) => emit('update:modelValue', v), set: (v) => emit('update:modelValue', v),
}) })
/** 仓库管理仅使用费用票据 type=4 */
const expenseBills = computed(() => { const expenseBills = computed(() => {
return allBills.value.filter((b) => { return allBills.value.filter((b) => {
if (b?.is_valid === false || b?.is_valid === 0 || b?.is_valid === '0') return false if (b?.is_valid === false || b?.is_valid === 0 || b?.is_valid === '0') return false
@@ -161,6 +156,12 @@ const showLegacyRdcHint = computed(() => {
return isLegacyRdcMetricsBucket(rdc) return isLegacyRdcMetricsBucket(rdc)
}) })
function sectionBlockClass(sectionKey) {
if (sectionKey === 'commission') return 'is-commission'
if (sectionKey === 'profit') return 'is-profit'
return ''
}
function rdcDisplayName(rdc) { function rdcDisplayName(rdc) {
const name = String(rdc?.name || '').trim() const name = String(rdc?.name || '').trim()
const code = String(rdc?.code || '').trim() const code = String(rdc?.code || '').trim()
@@ -179,11 +180,6 @@ function billTabParts(bill) {
return expenseBillTabParts(bill) return expenseBillTabParts(bill)
} }
function billTabTitle(bill) {
const { name, tax } = billTabParts(bill)
return tax ? `${name} · ${tax}%` : name
}
function normBillCode(code) { function normBillCode(code) {
return String(code ?? '').trim() return String(code ?? '').trim()
} }
@@ -192,26 +188,18 @@ function normRdcCode(code) {
return String(code ?? '').trim() return String(code ?? '').trim()
} }
function pickInitialBillCode() { function ensureAllBillBucketsForRdc(rdcCode) {
const list = expenseBills.value const rdc = normRdcCode(rdcCode)
if (!list.length) { if (!rdc || props.readonly) return
activeBillCode.value = '' for (const bill of expenseBills.value) {
return ensureBillBucket(rdc, bill.bill_code)
} }
const cur = normBillCode(activeBillCode.value)
if (cur && list.some((b) => normBillCode(b.bill_code) === cur)) {
activeBillCode.value = cur
return
}
activeBillCode.value = normBillCode(list[0].bill_code)
} }
function syncActiveWarehouseSelection() { function syncActiveRdcBuckets() {
pickInitialBillCode()
const rdc = normRdcCode(activeRdcCode.value) const rdc = normRdcCode(activeRdcCode.value)
const bill = normBillCode(activeBillCode.value) if (!rdc || !expenseBills.value.length) return
if (!rdc || !bill) return if (!props.readonly) ensureAllBillBucketsForRdc(rdc)
if (!props.readonly) ensureBillBucket(rdc, bill)
} }
function selectRdc(code) { function selectRdc(code) {
@@ -219,12 +207,11 @@ function selectRdc(code) {
activeRdcCode.value = c activeRdcCode.value = c
if (!c || props.readonly) return if (!c || props.readonly) return
const next = { ...bucket.value } const next = { ...bucket.value }
const rdc = next[c] if (!next[c]) {
if (!rdc) {
next[c] = {} next[c] = {}
bucket.value = next bucket.value = next
} }
syncActiveWarehouseSelection() syncActiveRdcBuckets()
} }
function ensureBillBucket(rdcCode, billCode) { function ensureBillBucket(rdcCode, billCode) {
@@ -246,25 +233,25 @@ function ensureBillBucket(rdcCode, billCode) {
return next[rdc][bill] return next[rdc][bill]
} }
function currentBillBucket() { function billBucketFor(rdcCode, billCode) {
const rdc = bucket.value[normRdcCode(activeRdcCode.value)] const rdc = bucket.value[normRdcCode(rdcCode)]
const bill = normBillCode(activeBillCode.value) const bill = normBillCode(billCode)
if (!rdc || !bill) return null if (!rdc || !bill) return null
if (isLegacyRdcMetricsBucket(rdc)) return rdc if (isLegacyRdcMetricsBucket(rdc)) return rdc
return rdc[bill] || null return rdc[bill] || null
} }
function metricValue(sectionKey, channelKey, metricKey) { function metricValue(sectionKey, channelKey, metricKey, billCode) {
const b = currentBillBucket() const b = billBucketFor(activeRdcCode.value, billCode)
const v = b?.[sectionKey]?.[channelKey]?.[metricKey] const v = b?.[sectionKey]?.[channelKey]?.[metricKey]
return v === null || v === undefined ? null : Number(v) return v === null || v === undefined ? null : Number(v)
} }
function setMetric(sectionKey, channelKey, metricKey, raw) { function setMetric(sectionKey, channelKey, metricKey, raw, billCode) {
const rdcCode = normRdcCode(activeRdcCode.value) const rdcCode = normRdcCode(activeRdcCode.value)
const billCode = normBillCode(activeBillCode.value) const bill = normBillCode(billCode)
if (props.readonly || !rdcCode || !billCode) return if (props.readonly || !rdcCode || !bill) return
const billBucket = ensureBillBucket(rdcCode, billCode) const billBucket = ensureBillBucket(rdcCode, bill)
if (!billBucket) return if (!billBucket) return
if (!billBucket[sectionKey]) billBucket[sectionKey] = createEmptyRdcBucket()[sectionKey] if (!billBucket[sectionKey]) billBucket[sectionKey] = createEmptyRdcBucket()[sectionKey]
if (!billBucket[sectionKey][channelKey]) { if (!billBucket[sectionKey][channelKey]) {
@@ -281,17 +268,16 @@ async function loadBillList() {
const res = await request.post('/api/bill/listed', { page: 1, limit: 500, type: FINANCE_BILL_TYPE_EXPENSE }) const res = await request.post('/api/bill/listed', { page: 1, limit: 500, type: FINANCE_BILL_TYPE_EXPENSE })
const list = Array.isArray(res?.data) ? res.data : (Array.isArray(res?.data?.list) ? res.data.list : []) const list = Array.isArray(res?.data) ? res.data : (Array.isArray(res?.data?.list) ? res.data.list : [])
allBills.value = list.filter((b) => normalizeFinanceBillType(b?.type) === FINANCE_BILL_TYPE_EXPENSE) allBills.value = list.filter((b) => normalizeFinanceBillType(b?.type) === FINANCE_BILL_TYPE_EXPENSE)
syncActiveWarehouseSelection() syncActiveRdcBuckets()
} catch { } catch {
allBills.value = [] allBills.value = []
activeBillCode.value = ''
} finally { } finally {
billLoading.value = false billLoading.value = false
} }
} }
watch(expenseBills, () => { watch(expenseBills, () => {
syncActiveWarehouseSelection() syncActiveRdcBuckets()
}) })
function pickInitialRdcCode(opts) { function pickInitialRdcCode(opts) {
@@ -314,7 +300,7 @@ watch(
if (nextRdc !== normRdcCode(activeRdcCode.value)) { if (nextRdc !== normRdcCode(activeRdcCode.value)) {
selectRdc(nextRdc) selectRdc(nextRdc)
} else { } else {
syncActiveWarehouseSelection() syncActiveRdcBuckets()
} }
if (!billsLoaded.value) { if (!billsLoaded.value) {
billsLoaded.value = true billsLoaded.value = true
@@ -324,10 +310,6 @@ watch(
{ immediate: true }, { immediate: true },
) )
watch(activeBillCode, () => {
syncActiveWarehouseSelection()
})
watch( watch(
() => props.modelValue, () => props.modelValue,
() => { () => {
@@ -364,12 +346,6 @@ defineExpose({
border: 1px solid #d9ecff; border: 1px solid #d9ecff;
border-radius: 6px; border-radius: 6px;
} }
.gt-wh-hint code {
font-size: 11px;
padding: 0 4px;
background: #ecf5ff;
border-radius: 3px;
}
.gt-wh-legacy-hint { .gt-wh-legacy-hint {
padding: 8px 12px; padding: 8px 12px;
font-size: 12px; font-size: 12px;
@@ -379,6 +355,10 @@ defineExpose({
border: 1px solid #faecd8; border: 1px solid #faecd8;
border-radius: 6px; border-radius: 6px;
} }
.gt-wh-legacy-hint--workspace {
flex-shrink: 0;
margin: 10px 12px 0;
}
.gt-wh-split { .gt-wh-split {
display: flex; display: flex;
align-items: stretch; align-items: stretch;
@@ -389,7 +369,7 @@ defineExpose({
background: #fff; background: #fff;
} }
.gt-wh-rdc-nav { .gt-wh-rdc-nav {
flex: 0 0 132px; flex: 0 0 168px;
align-self: stretch; align-self: stretch;
min-height: 0; min-height: 0;
border-right: 1px solid #ebeef5; border-right: 1px solid #ebeef5;
@@ -436,44 +416,53 @@ defineExpose({
color: #909399; color: #909399;
word-break: break-all; word-break: break-all;
} }
.gt-wh-main { .gt-wh-workspace {
flex: 1; flex: 1;
min-width: 0; min-width: 0;
min-height: 0; min-height: 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 12px;
padding: 12px 14px 14px;
overflow-y: auto;
}
.gt-wh-bill-tabs :deep(.el-tabs__header) {
margin-bottom: 0;
}
.gt-wh-bill-tabs :deep(.el-tabs__nav-wrap::after) {
height: 1px;
}
.gt-wh-bill-tabs :deep(.el-tabs__item) {
height: 38px;
padding: 0 14px;
}
.gt-wh-bill-tab {
display: inline-flex;
align-items: center;
gap: 6px;
max-width: 168px;
vertical-align: middle;
}
.gt-wh-bill-tab-name {
flex: 0 1 auto;
min-width: 0;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 13px;
line-height: 1.2;
} }
.gt-wh-bill-tab-tax { .gt-wh-bills-scroll {
flex: 1;
min-width: 0;
min-height: 0;
overflow-y: auto;
overflow-x: hidden;
padding: 10px 10px 12px;
display: flex;
flex-direction: column;
gap: 12px;
}
.gt-wh-bill-group {
flex-shrink: 0; flex-shrink: 0;
display: flex;
flex-direction: column;
background: #fafbfc;
border: 1px solid #ebeef5;
border-radius: 8px;
overflow: hidden;
}
.gt-wh-bill-group-title {
flex-shrink: 0;
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 8px;
padding: 8px 14px 6px;
font-size: 12px;
font-weight: 600;
color: #8b9ab0;
letter-spacing: 0.5px;
border-bottom: 1px solid #ebeef5;
background: #fafbfc;
}
.gt-wh-bill-group-name {
color: #606266;
font-size: 13px;
}
.gt-wh-bill-group-tax {
padding: 0 6px; padding: 0 6px;
height: 18px; height: 18px;
line-height: 18px; line-height: 18px;
@@ -482,36 +471,32 @@ defineExpose({
font-weight: 600; font-weight: 600;
background: #fef0f0; background: #fef0f0;
color: #f56c6c; color: #f56c6c;
transition: background 0.15s, color 0.15s;
} }
.gt-wh-bill-tabs :deep(.el-tabs__item.is-active) .gt-wh-bill-tab-name { .gt-wh-bill-group-body {
font-weight: 600; padding: 12px;
} }
.gt-wh-bill-tabs :deep(.el-tabs__item.is-active) .gt-wh-bill-tab-tax { .gt-wh-sections-row {
background: #f56c6c; display: grid;
color: #fff; grid-template-columns: repeat(2, minmax(0, 1fr));
} gap: 12px;
.gt-wh-bill-tabs :deep(.el-tabs__item:hover) .gt-wh-bill-tab-tax { align-items: start;
background: #fde2e2;
}
.gt-wh-bill-tabs :deep(.el-tabs__item.is-active:hover) .gt-wh-bill-tab-tax {
background: #f56c6c;
color: #fff;
}
.gt-wh-form-body {
display: flex;
flex-direction: column;
gap: 14px;
} }
.gt-wh-section-block { .gt-wh-section-block {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 10px; gap: 10px;
min-width: 0;
padding: 10px 10px 12px;
border-radius: 8px;
border: 1px solid transparent;
} }
.gt-wh-section-block + .gt-wh-section-block { .gt-wh-section-block.is-commission {
margin-top: 4px; background: #f7fafd;
padding-top: 14px; border-color: #e6eef8;
border-top: 1px solid #ebeef5; }
.gt-wh-section-block.is-profit {
background: #f8fbf6;
border-color: #e5efe5;
} }
.gt-wh-section-title { .gt-wh-section-title {
font-size: 13px; font-size: 13px;
@@ -519,56 +504,50 @@ defineExpose({
color: #303133; color: #303133;
padding-left: 2px; padding-left: 2px;
} }
.gt-wh-section-block.is-commission .gt-wh-section-title {
color: #4a6fa5;
}
.gt-wh-section-block.is-profit .gt-wh-section-title {
color: #5a8f5a;
}
.gt-wh-channel-grid { .gt-wh-channel-grid {
display: grid; display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr)); grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 12px; gap: 10px;
}
@media (max-width: 900px) {
.gt-wh-split {
flex-direction: column;
}
.gt-wh-rdc-nav {
flex: 0 0 auto;
max-height: 140px;
border-right: none;
border-bottom: 1px solid #ebeef5;
}
.gt-wh-channel-grid {
grid-template-columns: 1fr;
}
} }
.gt-wh-channel-card { .gt-wh-channel-card {
padding: 12px; padding: 10px 8px;
border: 1px solid #ebeef5; border: 1px solid #ebeef5;
border-radius: 8px; border-radius: 6px;
background: #fff; background: #fff;
min-width: 0;
} }
.gt-wh-channel-title { .gt-wh-channel-title {
font-size: 13px; font-size: 12px;
font-weight: 600; font-weight: 600;
color: #303133; color: #303133;
margin-bottom: 10px; margin-bottom: 8px;
padding-bottom: 8px; padding-bottom: 6px;
border-bottom: 1px dashed #ebeef5; border-bottom: 1px dashed #ebeef5;
} }
.gt-wh-metrics { .gt-wh-metrics {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 10px; gap: 8px;
} }
.gt-wh-metric { .gt-wh-metric {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 6px;
} }
.gt-wh-metric-label { .gt-wh-metric-label {
flex: 0 0 20px; flex: 0 0 18px;
font-size: 12px; font-size: 12px;
color: #909399; color: #909399;
} }
.gt-wh-metric-input { .gt-wh-metric-input {
flex: 1; flex: 1;
min-width: 0;
width: 100%; width: 100%;
} }
.gt-wh-metric-input :deep(.el-input__inner) { .gt-wh-metric-input :deep(.el-input__inner) {
@@ -582,4 +561,21 @@ defineExpose({
.gt-wh-empty { .gt-wh-empty {
padding: 24px 0; padding: 24px 0;
} }
@media (max-width: 900px) {
.gt-wh-split {
flex-direction: column;
}
.gt-wh-rdc-nav {
flex: 0 0 auto;
max-height: 120px;
border-right: none;
border-bottom: 1px solid #ebeef5;
}
.gt-wh-sections-row {
grid-template-columns: 1fr;
}
.gt-wh-channel-grid {
grid-template-columns: 1fr;
}
}
</style> </style>
+7
View File
@@ -89,6 +89,10 @@
.wops-flex-table.is-cols-pr-cost { .wops-flex-table.is-cols-pr-cost {
--wops-cols: 7fr 10.5fr 10.5fr 7fr 8fr 8fr 6fr 9fr 6fr 8fr; --wops-cols: 7fr 10.5fr 10.5fr 7fr 8fr 8fr 6fr 9fr 6fr 8fr;
} }
/* 物料成本围栏(11 列):内部交易相关表头加宽,配合 88vw 弹窗单行显示 */
.wops-flex-table.is-cols-cost-fence {
--wops-cols: 8fr 9fr 10fr 7fr 6fr 9fr 10fr 8fr 11fr 7fr 5fr;
}
.wops-flex-scroll { .wops-flex-scroll {
flex: 1; flex: 1;
@@ -197,6 +201,7 @@
.wops-flex-table.is-cols-share-deploy .wops-flex-row, .wops-flex-table.is-cols-share-deploy .wops-flex-row,
.wops-flex-table.is-cols-share-waste .wops-flex-row, .wops-flex-table.is-cols-share-waste .wops-flex-row,
.wops-flex-table.is-cols-pr-cost .wops-flex-row, .wops-flex-table.is-cols-pr-cost .wops-flex-row,
.wops-flex-table.is-cols-cost-fence .wops-flex-row,
.wops-flex-table.is-cols-collect-whp .wops-flex-row, .wops-flex-table.is-cols-collect-whp .wops-flex-row,
.wops-flex-table.is-cols-logs-whp .wops-flex-row, .wops-flex-table.is-cols-logs-whp .wops-flex-row,
.wops-flex-table.is-cols-orders-whp .wops-flex-row, .wops-flex-table.is-cols-orders-whp .wops-flex-row,
@@ -220,6 +225,8 @@
.wops-flex-table.is-cols-share-waste .wops-flex-head .wops-col--num, .wops-flex-table.is-cols-share-waste .wops-flex-head .wops-col--num,
.wops-flex-table.is-cols-pr-cost .wops-col--num, .wops-flex-table.is-cols-pr-cost .wops-col--num,
.wops-flex-table.is-cols-pr-cost .wops-flex-head .wops-col--num, .wops-flex-table.is-cols-pr-cost .wops-flex-head .wops-col--num,
.wops-flex-table.is-cols-cost-fence .wops-col--num,
.wops-flex-table.is-cols-cost-fence .wops-flex-head .wops-col--num,
.wops-flex-table.is-cols-collect-whp .wops-col--num, .wops-flex-table.is-cols-collect-whp .wops-col--num,
.wops-flex-table.is-cols-collect-whp .wops-flex-head .wops-col--num, .wops-flex-table.is-cols-collect-whp .wops-flex-head .wops-col--num,
.wops-flex-table.is-cols-logs-whp .wops-col--num, .wops-flex-table.is-cols-logs-whp .wops-col--num,
+38 -9
View File
@@ -414,7 +414,7 @@
<el-dialog <el-dialog
v-model="materialDialogVisible" v-model="materialDialogVisible"
:title="batchSelMode ? `批量设置选配(${selectedRows.length} 条物料)` : (materialDialogMode === 'add' ? '新增物料' : String(materialEditingId || '修改物料'))" :title="batchSelMode ? `批量设置选配(${selectedRows.length} 条物料)` : (materialDialogMode === 'add' ? '新增物料' : String(materialEditingId || '修改物料'))"
width="72vw" width="88vw"
:close-on-click-modal="false" :close-on-click-modal="false"
destroy-on-close destroy-on-close
append-to-body append-to-body
@@ -504,7 +504,7 @@
</div> </div>
<template v-for="(cat, catIdx) in materialForm.categories" :key="`${cat.category_code}-${catIdx}`"> <template v-for="(cat, catIdx) in materialForm.categories" :key="`${cat.category_code}-${catIdx}`">
<div <div
v-show="activeCatCode === cat.category_code && mlActiveNavKey !== ML_NAV_WAREHOUSE" v-show="activeCatCode === cat.category_code && mlActiveNavKey !== ML_NAV_WAREHOUSE && mlActiveNavKey !== ML_NAV_COST"
class="mf-cat-pane" class="mf-cat-pane"
:class="{ 'mf-cat-pane--fill': mlActiveNavKey === ML_NAV_BASIC }" :class="{ 'mf-cat-pane--fill': mlActiveNavKey === ML_NAV_BASIC }"
> >
@@ -2579,10 +2579,25 @@ const windowW = ref(window.innerWidth)
const isMobile = computed(() => windowW.value <= 600) const isMobile = computed(() => windowW.value <= 600)
window.addEventListener('resize', () => { windowW.value = window.innerWidth }) window.addEventListener('resize', () => { windowW.value = window.innerWidth })
// 72vw padding/ 80px 240px1920525607 /** 物料弹窗宽度(须与 template width 一致) */
const MF_DIALOG_VW = 0.88
/** 弹窗 inset + 左侧导航 + 页签区内边距 + 字段网格内边距 */
const MF_GRID_INSET = 244
/** 每列最小宽度(1920×88vw 右侧字段区 → 6 列) */
const MF_COL_MIN_PX = 215
const MF_GRID_GAP = 16
function mfGridAvailW() {
return Math.max(320, windowW.value * MF_DIALOG_VW - MF_GRID_INSET)
}
function mfGridColPx(cols = mfColCount.value) {
return Math.max(160, Math.floor((mfGridAvailW() - (cols - 1) * MF_GRID_GAP) / cols))
}
// 88vw 1920625608
const mfColCount = computed(() => { const mfColCount = computed(() => {
const bodyW = windowW.value * 0.72 - 80 return Math.max(2, Math.min(8, Math.floor(mfGridAvailW() / MF_COL_MIN_PX)))
return Math.max(2, Math.min(8, Math.floor(bodyW / 240)))
}) })
// MaterialCategory.fieldGridSpan // MaterialCategory.fieldGridSpan
@@ -2591,7 +2606,7 @@ function mfFieldSpan(field) {
if (field.type === 'table' || field.type === 'textarea') return 'mf-field-full' if (field.type === 'table' || field.type === 'textarea') return 'mf-field-full'
if (field.type === 'attachment') return 'mf-field-span2' if (field.type === 'attachment') return 'mf-field-span2'
const cols = mfColCount.value const cols = mfColCount.value
const colPx = Math.max(160, Math.floor((windowW.value * 0.72 - 80 - (cols - 1) * 16) / cols)) const colPx = mfGridColPx(cols)
if (field.type === 'radio') { if (field.type === 'radio') {
const opts = field.options || [] const opts = field.options || []
if (opts.length <= 1) return '' if (opts.length <= 1) return ''
@@ -2616,7 +2631,7 @@ function mfFieldSpan(field) {
/** 适配项占列数:名称较长时占两列 */ /** 适配项占列数:名称较长时占两列 */
function adpItemSpan(name) { function adpItemSpan(name) {
const cols = mfColCount.value const cols = mfColCount.value
const colPx = Math.max(160, Math.floor((windowW.value * 0.72 - 80 - (cols - 1) * 16) / cols)) const colPx = mfGridColPx(cols)
const len = String(name || '').length const len = String(name || '').length
// 14px/ 8px/ // 14px/ 8px/
const estPx = len * 12 + 36 // 36 = + gap const estPx = len * 12 + 36 // 36 = + gap
@@ -2650,7 +2665,7 @@ function _collectCategoryWarehouseFlatFields(categoryCode, warehouseCode) {
/** 选配查看态:每个控件在 mf-fields-grid 中的占列规则,与 mfFieldSpan 一致 */ /** 选配查看态:每个控件在 mf-fields-grid 中的占列规则,与 mfFieldSpan 一致 */
function mfSelCompGridClass(comp) { function mfSelCompGridClass(comp) {
const cols = mfColCount.value const cols = mfColCount.value
const colPx = Math.max(160, Math.floor((windowW.value * 0.72 - 80 - (cols - 1) * 16) / cols)) const colPx = mfGridColPx(cols)
const type = comp?.type const type = comp?.type
const opts = comp?.options || [] const opts = comp?.options || []
if (type === 'radio') { if (type === 'radio') {
@@ -10891,6 +10906,20 @@ provide('mlTabPaneCtx', {
padding: 8px 12px 12px; padding: 8px 12px 12px;
box-sizing: border-box; box-sizing: border-box;
} }
.mf-nav-pane--cost-api {
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
width: 100%;
padding: 8px 12px 12px;
box-sizing: border-box;
}
.mf-right-scroll:has(.mf-nav-pane--cost-api) {
display: flex;
flex-direction: column;
overflow: hidden;
}
.mf-nav-pane--basic { .mf-nav-pane--basic {
padding-top: 4px; padding-top: 4px;
} }
@@ -11059,7 +11088,7 @@ provide('mlTabPaneCtx', {
.mf-fields-grid { .mf-fields-grid {
display: grid; display: grid;
grid-template-columns: repeat(4, 1fr); /* 默认 4 列,JS 会覆盖 */ grid-template-columns: repeat(6, 1fr); /* 默认 6 列,JS 会覆盖 */
gap: 10px 16px; gap: 10px 16px;
align-items: start; align-items: start;
padding: 12px 14px; padding: 12px 14px;