From 5eab17d62462d4ef162df1ccb3b39a79d87bef3f Mon Sep 17 00:00:00 2001 From: xiaojibeier Date: Sun, 19 Jul 2026 00:39:51 +0800 Subject: [PATCH] 20260719 --- src/api/pricingTable.js | 133 +++++- src/api/warehouseRdc.js | 23 + .../global-tab/GlobalTabCostPanel.vue | 398 +++++++++++++++--- src/config/buildId.json | 4 +- src/data/appChangelog.js | 77 +++- src/styles/warehouseOpsFlexTable.css | 7 +- src/views/material/MaterialList.vue | 38 +- 7 files changed, 603 insertions(+), 77 deletions(-) diff --git a/src/api/pricingTable.js b/src/api/pricingTable.js index a4fc0af..bc7fc9a 100644 --- a/src/api/pricingTable.js +++ b/src/api/pricingTable.js @@ -1,5 +1,10 @@ import request from '@/utils/request' -import { orgTaxTypeLabel } from '@/constants/orgTaxType' +import { + ORG_TAX_TYPE_GENERAL, + ORG_TAX_TYPE_RECEIPT, + ORG_TAX_TYPE_SMALL, + orgTaxTypeLabel, +} from '@/constants/orgTaxType' import { pickGoodsNameFromListedRow } from '@/utils/goodsCategorys' function str(v) { @@ -26,6 +31,18 @@ export const PRICING_TAXPAYER_BY_ORG = { export const PRICING_TAXPAYER_DEFAULT = 'GENERAL' +/** 成本围栏 Tab key → 保存接口 org_type(1 一般 / 2 小规模 / 3 收据) */ +export const PRICING_TAXPAYER_ORG_TYPE = { + GENERAL: ORG_TAX_TYPE_GENERAL, + SMALL: ORG_TAX_TYPE_SMALL, + INDIVIDUAL: ORG_TAX_TYPE_RECEIPT, +} + +export function pricingTaxpayerTypeToOrgType(taxpayerType) { + const key = str(taxpayerType).toUpperCase() + return PRICING_TAXPAYER_ORG_TYPE[key] ?? null +} + const PRICING_TAXPAYER_KEYS = new Set(PRICING_TAXPAYER_TABS.map((t) => t.key)) /** 接口 data.list */ @@ -41,6 +58,81 @@ function unwrapPricingTableList(res) { return [] } +/** 成本价上限:0 或未设视为无勾选(后端默认 0) */ +export function normalizePricingTableCostPriceBound(value) { + if (value == null || value === '') return null + const n = Number(value) + if (!Number.isFinite(n) || n <= 0) return null + return n +} + +/** 纳税身份 Tab → 定价表 / RDC 成本价上限字段 */ +export const PRICING_COST_PRICE_BOUND_FIELDS = { + GENERAL: 'general_cost_price_bound', + SMALL: 'small_cost_price_bound', + INDIVIDUAL: 'individual_cost_price_bound', +} + +export function emptyPricingTableCostPriceBounds() { + return { + GENERAL: null, + SMALL: null, + INDIVIDUAL: null, + } +} + +function pricingTableCostPriceBoundSources(res, list = []) { + const envelope = res?.data ?? res + const payload = envelope?.data ?? envelope + const sources = [payload, envelope, res].filter((s) => s && typeof s === 'object' && !Array.isArray(s)) + for (const row of list) { + if (row && typeof row === 'object') sources.push(row) + } + return sources +} + +/** 定价表响应中的 RDC 物料成本价上限(三种纳税身份各一) */ +export function pickPricingTableCostPriceBounds(res, list = [], { taxpayer_type = '' } = {}) { + const bounds = emptyPricingTableCostPriceBounds() + const sources = pricingTableCostPriceBoundSources(res, list) + + for (const [key, field] of Object.entries(PRICING_COST_PRICE_BOUND_FIELDS)) { + for (const src of sources) { + const n = pickMoney(src, field) + if (n != null) { + bounds[key] = normalizePricingTableCostPriceBound(n) + break + } + } + } + + const legacyKey = str(taxpayer_type).toUpperCase() + if (legacyKey && PRICING_TAXPAYER_KEYS.has(legacyKey) && bounds[legacyKey] == null) { + for (const src of sources) { + const n = pickMoney(src, 'cost_price_bound') + if (n != null) { + bounds[legacyKey] = normalizePricingTableCostPriceBound(n) + break + } + } + } + + return bounds +} + +/** 指定纳税身份的成本价上限 */ +export function pickPricingTableCostPriceBoundForType(bounds, taxpayerType) { + const key = str(taxpayerType).toUpperCase() + if (!key || !PRICING_TAXPAYER_KEYS.has(key)) return null + return normalizePricingTableCostPriceBound(bounds?.[key]) +} + +/** @deprecated 请用 pickPricingTableCostPriceBounds + pickPricingTableCostPriceBoundForType */ +export function pickPricingTableCostPriceBound(res, list = [], taxpayerType = '') { + const bounds = pickPricingTableCostPriceBounds(res, list, { taxpayer_type: taxpayerType }) + return pickPricingTableCostPriceBoundForType(bounds, taxpayerType) +} + function pickText(raw, ...keys) { for (const k of keys) { const v = raw[k] @@ -92,6 +184,7 @@ export function normalizePricingTableRow(raw, idx = 0) { const supplier_org_type = pickRate(raw, 'supplier_org_type') const supplier_org_type_label = orgTaxTypeLabel(supplier_org_type) || null const goods_name = pickGoodsNameFromListedRow(raw) || pickText(raw, 'goods_name') + const pre_price = pickMoney(raw, 'pre_price') const bill_type = pickText(raw, 'bill_name', 'bill_type') const input_tax_rate = pickRate(raw, 'bill_tax', 'input_tax_rate') const source_company_name = pickText(raw, 'receive_company_name', 'source_company_name') @@ -121,11 +214,20 @@ export function normalizePricingTableRow(raw, idx = 0) { ) const fence_value = pickMoney(raw, 'fence_price', 'fence_value') const bloc = pickText(raw, 'supplier_bloc_code') - const supplier_company_code = pickText(raw, 'supplier_company_code', 'company_code') + const supplier_company_code = pickPricingTableRowSupplierCompanyCode(raw) const supply_collection_code = pickText(raw, 'supply_collection_code', 'collection_code') + const explicitKey = pickText(raw, 'key', 'id') + const rowKey = explicitKey ?? [ + bloc || 'bloc', + supplier_company_code || `sup-${idx}`, + receive_company_code || `rcv-${idx}`, + source_warehouse_code || '', + supply_collection_code || '', + ].filter(Boolean).join('-') + return { - key: pickText(raw, 'key', 'id') ?? `${bloc || 'bloc'}-${supplier_company_code || idx}`, + key: rowKey, supplier_bloc_name, supplier_company_name, supplier_company_display: formatCompanyNameWithOrgType(supplier_company_name, supplier_org_type_label), @@ -134,6 +236,7 @@ export function normalizePricingTableRow(raw, idx = 0) { supplier_company_code, supply_collection_code, goods_name, + pre_price, bill_type, input_tax_rate, source_company_name, @@ -150,9 +253,22 @@ export function normalizePricingTableRow(raw, idx = 0) { source_rdc_name: source_warehouse_name, source_rdc_code: source_warehouse_code, fence_value, + raw, } } +/** 定价表行 → 供货组织编码(围栏值 ? debug 请求用) */ +export function pickPricingTableRowSupplierCompanyCode(row) { + const raw = row?.raw && typeof row.raw === 'object' ? row.raw : row + if (!raw || typeof raw !== 'object') return '' + return pickText( + raw, + 'supplier_company_code', + 'supply_company_code', + 'supplier_org_code', + ) +} + /** * 组装 POST /api/pricing/table 请求体 * @param {object} body @@ -197,9 +313,18 @@ export function buildPricingTablePayload(body = {}, opts = {}) { export async function pricingTableQuery(body = {}) { const payload = buildPricingTablePayload(body, { debug: false }) const res = await request.post('/api/pricing/table', payload) - return unwrapPricingTableList(res) + const list = unwrapPricingTableList(res) + const rows = list .map((row, idx) => normalizePricingTableRow(row, idx)) .filter(Boolean) + const cost_price_bounds = pickPricingTableCostPriceBounds(res, list, { + taxpayer_type: payload.taxpayer_type, + }) + return { + rows, + cost_price_bounds, + cost_price_bound: pickPricingTableCostPriceBoundForType(cost_price_bounds, payload.taxpayer_type), + } } /** diff --git a/src/api/warehouseRdc.js b/src/api/warehouseRdc.js index 4c852b8..35eacab 100755 --- a/src/api/warehouseRdc.js +++ b/src/api/warehouseRdc.js @@ -388,6 +388,29 @@ export function warehouseRdcSetZone(body) { return request.post('/api/warehouse/rdc/set_zone', payload) } +/** POST /api/warehouse/rdc/set_cost_price_bound — body: { warehouse_code, goods_code, org_type, cost_price_bound } */ +export function warehouseRdcSetCostPriceBound(body) { + const warehouse_code = String(body?.warehouse_code ?? '').trim() + const goods_code = String(body?.goods_code ?? '').trim() + const org_type = Number(body?.org_type) + const cost_price_bound = Number(body?.cost_price_bound) + if (!warehouse_code || !goods_code) { + return Promise.reject(new Error('set_cost_price_bound 缺少 warehouse_code / goods_code')) + } + if (!Number.isFinite(org_type) || org_type < 1 || org_type > 3) { + return Promise.reject(new Error('set_cost_price_bound 缺少有效 org_type(1/2/3)')) + } + if (!Number.isFinite(cost_price_bound)) { + return Promise.reject(new Error('set_cost_price_bound 缺少有效 cost_price_bound')) + } + return request.post('/api/warehouse/rdc/set_cost_price_bound', { + warehouse_code, + goods_code, + org_type, + cost_price_bound, + }) +} + /** POST /api/warehouse/rdc/match_zone — body: { warehouse_code, goods_code } */ export async function warehouseRdcMatchZone(body) { const warehouse_code = String(body?.warehouse_code ?? '').trim() diff --git a/src/components/global-tab/GlobalTabCostPanel.vue b/src/components/global-tab/GlobalTabCostPanel.vue index d12b303..d81bf82 100644 --- a/src/components/global-tab/GlobalTabCostPanel.vue +++ b/src/components/global-tab/GlobalTabCostPanel.vue @@ -57,64 +57,96 @@
-
-
-
- {{ col.label }}({{ col.unit }}) +
+
+
+
+ {{ col.label }}({{ col.unit }}) +
+ -
-

暂无围栏数据

-
+
@@ -214,15 +260,20 @@ import { pickCustomerCompanyCode, pickCustomerDisplayName, } from '@/api/customer' -import { formatOrgSaleTypeLabel } from '@/api/company' +import { warehouseRdcSetCostPriceBound } from '@/api/warehouseRdc' import { PRICING_TAXPAYER_DEFAULT, PRICING_TAXPAYER_TABS, PRICING_TRADE_DOMESTIC, + emptyPricingTableCostPriceBounds, formatPricingTableDebug, + pickPricingTableCostPriceBoundForType, pickPricingTableError, + pickPricingTableRowSupplierCompanyCode, + normalizePricingTableCostPriceBound, pricingTableDebugQuery, pricingTableQuery, + pricingTaxpayerTypeToOrgType, } from '@/api/pricingTable' const props = defineProps({ @@ -231,10 +282,12 @@ const props = defineProps({ rdcList: { type: Array, default: () => [] }, }) +const emit = defineEmits(['fence-goods-names-change']) + const FENCE_COLUMNS = [ { key: 'supplier_bloc_name', label: '供应商集团名称' }, { key: 'supplier_company_name', label: '供应商组织名称' }, - { key: 'goods_name', label: '商品名称' }, + { key: 'pre_price', label: '商品预估成本', unit: '元' }, { key: 'bill_type', label: '票据类型' }, { key: 'input_tax_rate', label: '进项税率', unit: '%' }, { key: 'source_company_name', label: '源采购组织名称' }, @@ -245,11 +298,10 @@ const FENCE_COLUMNS = [ { key: 'fence_value', label: '围栏值', unit: '元' }, ] -const NUM_KEYS = new Set(['input_tax_rate', 'internal_min_price', 'internal_input_tax_rate', 'fence_value']) +const NUM_KEYS = new Set(['pre_price', 'input_tax_rate', 'internal_min_price', 'internal_input_tax_rate', 'fence_value']) function colHeadClass(key) { if (NUM_KEYS.has(key)) return 'wops-col--num' - if (key === 'goods_name') return 'wops-col--goods' if (key === 'supplier_bloc_name' || key === 'supplier_company_name' || key === 'source_company_name' || key === 'source_rdc') { return 'wops-col--wh' } @@ -260,7 +312,7 @@ function colHeadColorClass(key) { const map = { supplier_bloc_name: 'gt-cost-head-col--bloc', supplier_company_name: 'gt-cost-head-col--company', - goods_name: 'gt-cost-head-col--goods', + pre_price: 'gt-cost-head-col--pre-price', bill_type: 'gt-cost-head-col--bill', input_tax_rate: 'gt-cost-head-col--tax', source_company_name: 'gt-cost-head-col--source-co', @@ -285,6 +337,77 @@ function orgTypeTabClass(key) { return '' } +const FENCE_MONEY_EPS = 0.005 + +function fenceRowFenceNumber(row) { + const v = row?.fence_value + if (v == null || v === '') return null + const n = Number(v) + return Number.isFinite(n) ? n : null +} + +function isFenceMoneyEqual(a, b) { + if (a == null || b == null) return false + return Math.abs(a - b) < FENCE_MONEY_EPS +} + +function activeCostPriceBound() { + return pickPricingTableCostPriceBoundForType(costPriceBounds.value, activeTaxpayerType.value) +} + +function isFenceRowSelected(row) { + const bound = activeCostPriceBound() + const fence = fenceRowFenceNumber(row) + if (bound == null) return false + return isFenceMoneyEqual(fence, bound) +} + +function isFenceRowAboveBound(row) { + const bound = activeCostPriceBound() + const fence = fenceRowFenceNumber(row) + if (bound == null || fence == null) return false + return fence > bound + FENCE_MONEY_EPS +} + +function canSelectFenceRow(row) { + const fence = fenceRowFenceNumber(row) + return fence != null && fence > 0 +} + +async function selectFenceRow(row) { + if (isFenceRowSelected(row)) return + const cost_price_bound = fenceRowFenceNumber(row) + if (!canSelectFenceRow(row)) { + ElMessage.warning('该行围栏值无效,无法设为成本价上限') + return + } + const goods_code = String(props.goodsCode || '').trim() + const warehouse_code = String(activeWarehouseCode.value || '').trim() + if (!goods_code || !warehouse_code) return + const taxpayer_type = String(activeTaxpayerType.value || '').trim() + const org_type = pricingTaxpayerTypeToOrgType(taxpayer_type) + if (!taxpayer_type || org_type == null) return + if (savingCostBound.value || loading.value) return + + savingCostBound.value = true + try { + await warehouseRdcSetCostPriceBound({ + warehouse_code, + goods_code, + org_type, + cost_price_bound, + }) + costPriceBounds.value = { + ...costPriceBounds.value, + [taxpayer_type]: normalizePricingTableCostPriceBound(cost_price_bound), + } + } catch (e) { + ElMessage.error(String(e?.data?.message || e?.message || '设置成本价上限失败').trim() || '设置成本价上限失败') + } finally { + savingCostBound.value = false + } +} + const loading = ref(false) const loadError = ref('') /** 当前 Tab 对应的接口 taxpayer_type 枚举 */ @@ -292,9 +415,24 @@ const activeTaxpayerType = ref(PRICING_TAXPAYER_DEFAULT) const activeWarehouseCode = ref('') /** @type {import('vue').Ref} */ const fenceRows = ref([]) +/** 当前售出 RDC 三种纳税身份的成本价上限(与围栏值相等的行打勾) */ +const costPriceBounds = ref(emptyPricingTableCostPriceBounds()) +const savingCostBound = ref(false) /** 递增序号,避免切换售出仓 / 纳税身份时旧请求覆盖新结果 */ let fenceFetchSeq = 0 +const fenceGoodsNames = computed(() => { + const names = [] + const seen = new Set() + for (const row of fenceRows.value) { + const name = String(row?.goods_name ?? '').trim() + if (!name || seen.has(name)) continue + seen.add(name) + names.push(name) + } + return names +}) + const calculateDebugVisible = ref(false) const calculateDebugTitle = ref('算价明细') const calculateDebugText = ref('') @@ -342,6 +480,14 @@ function fmtMoney(v) { return n.toFixed(2) } +function fenceCellText(...candidates) { + for (const v of candidates) { + const s = String(v ?? '').trim() + if (s) return s + } + return '—' +} + function fmtTaxRate(v) { if (v == null || v === '') return '—' const n = Number(v) @@ -372,7 +518,7 @@ function buildPricingTableDebugBody(row, customerCode = '') { } const receive_company_code = String(row?.receive_company_code || '').trim() if (receive_company_code) body.receive_company_code = receive_company_code - const supplier_company_code = String(row?.supplier_company_code || '').trim() + const supplier_company_code = pickPricingTableRowSupplierCompanyCode(row) if (supplier_company_code) body.supplier_company_code = supplier_company_code const source_warehouse_code = String(row?.source_warehouse_code || row?.source_rdc_code || '').trim() if (source_warehouse_code) body.source_warehouse_code = source_warehouse_code @@ -483,14 +629,17 @@ async function fetchFenceTable() { if (!goods_code) { fenceRows.value = [] + costPriceBounds.value = emptyPricingTableCostPriceBounds() return } if (!warehouse_code) { fenceRows.value = [] + costPriceBounds.value = emptyPricingTableCostPriceBounds() return } if (!taxpayer_type) { fenceRows.value = [] + costPriceBounds.value = emptyPricingTableCostPriceBounds() return } @@ -498,7 +647,7 @@ async function fetchFenceTable() { loading.value = true loadError.value = '' try { - const rows = await pricingTableQuery({ + const { rows, cost_price_bounds } = await pricingTableQuery({ goods_code, warehouse_code, taxpayer_type, @@ -507,6 +656,7 @@ async function fetchFenceTable() { if (seq !== fenceFetchSeq) return if (activeTaxpayerType.value !== taxpayer_type) return fenceRows.value = rows + costPriceBounds.value = cost_price_bounds } catch (e) { if (seq !== fenceFetchSeq) return if (activeTaxpayerType.value !== taxpayer_type) return @@ -521,6 +671,7 @@ async function loadCost() { const code = String(props.goodsCode || '').trim() loadError.value = '' fenceRows.value = [] + costPriceBounds.value = emptyPricingTableCostPriceBounds() if (!code) { activeTaxpayerType.value = PRICING_TAXPAYER_DEFAULT activeWarehouseCode.value = '' @@ -539,6 +690,14 @@ watch( { immediate: true }, ) +watch( + fenceGoodsNames, + (names) => { + emit('fence-goods-names-change', names) + }, + { immediate: true }, +) + watch( () => props.rdcList, () => { @@ -674,6 +833,7 @@ onBeforeUnmount(() => { min-height: 0; display: flex; flex-direction: column; + overflow: hidden; } .gt-cost-org-tabs { flex-shrink: 0; @@ -734,20 +894,30 @@ onBeforeUnmount(() => { padding-right: 6px; } .gt-cost-table-wrap :deep(.wops-flex-table) { - flex: 1; + flex: 1 1 0; min-height: 0; - height: 100%; + max-height: 100%; + overflow: hidden; +} +.gt-cost-table-wrap :deep(.wops-flex-body), +.gt-cost-table-body { + flex: 1 1 0; + min-height: 0; + overflow-y: auto; + overflow-x: hidden; } .gt-cost-table-wrap :deep(.wops-flex-scroll) { - flex: 1; + flex: none; min-height: 0; + overflow: visible; +} +.gt-cost-table-wrap :deep(.wops-flex-scroll > .wops-flex-head) { + position: sticky; + top: 0; + z-index: 3; } .gt-cost-table-body { - flex: 1; min-height: 120px; - position: relative; - display: flex; - flex-direction: column; } .gt-cost-table-wrap :deep(.wops-empty-wrap) { flex: 1; @@ -774,8 +944,84 @@ onBeforeUnmount(() => { color: #b0b3b8; } .gt-cost-table-wrap :deep(.gt-cost-head-col--bloc) { border-bottom-color: #409eff; } +.gt-cost-table-wrap :deep(.wops-col--sel) { + padding: 0; + overflow: visible; +} +.gt-cost-fence-sel-btn { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0; + border: none; + background: transparent; + cursor: pointer; + line-height: 0; +} +.gt-cost-fence-sel-box { + display: inline-block; + width: 14px; + height: 14px; + flex-shrink: 0; + border: 1.5px solid #c0c4cc; + border-radius: 2px; + background: #fff; + box-sizing: border-box; + transition: border-color 0.15s, background 0.15s; + position: relative; +} +.gt-cost-fence-sel-btn:hover .gt-cost-fence-sel-box { + border-color: #409eff; +} +.gt-cost-fence-sel-btn:disabled { + cursor: not-allowed; + opacity: 0.55; +} +.gt-cost-fence-sel-btn:disabled:hover .gt-cost-fence-sel-box { + border-color: #c0c4cc; +} +.gt-cost-fence-sel-box.is-selected { + border-color: #409eff; + background: #409eff; +} +.gt-cost-fence-sel-box.is-selected::after { + content: ''; + position: absolute; + left: 3px; + top: 1px; + width: 5px; + height: 8px; + border: 2px solid #fff; + border-top: none; + border-left: none; + transform: rotate(45deg); +} +.gt-cost-table-wrap :deep(.gt-cost-head-col--sel) { + border-bottom: 2px solid #dcdfe6; + padding-left: 0; + padding-right: 0; +} +.gt-cost-table-wrap :deep(.wops-flex-data.is-below-selected) { + color: #c0c4cc; + background: #f5f7fa; +} +.gt-cost-table-wrap :deep(.wops-flex-data.is-below-selected .wops-name), +.gt-cost-table-wrap :deep(.wops-flex-data.is-below-selected .wops-code), +.gt-cost-table-wrap :deep(.wops-flex-data.is-below-selected .wops-ellipsis), +.gt-cost-table-wrap :deep(.wops-flex-data.is-below-selected .gt-cost-fence-num), +.gt-cost-table-wrap :deep(.wops-flex-data.is-below-selected .gt-cost-fence-help-icon) { + color: #c0c4cc; +} +.gt-cost-table-wrap :deep(.wops-flex-data.is-below-selected.is-stripe) { + background: #eef0f3; +} +.gt-cost-table-wrap :deep(.wops-flex-data.is-below-selected:hover), +.gt-cost-table-wrap :deep(.wops-flex-data.is-below-selected.is-stripe:hover) { + background: #eef0f3; + color: #c0c4cc; +} .gt-cost-table-wrap :deep(.gt-cost-head-col--company) { border-bottom-color: #5470c6; } -.gt-cost-table-wrap :deep(.gt-cost-head-col--goods) { border-bottom-color: #67c23a; } +.gt-cost-table-wrap :deep(.gt-cost-head-col--pre-price) { border-bottom-color: #95d475; } .gt-cost-table-wrap :deep(.gt-cost-head-col--bill) { border-bottom-color: #909399; } .gt-cost-table-wrap :deep(.gt-cost-head-col--tax) { border-bottom-color: #e6a23c; } .gt-cost-table-wrap :deep(.gt-cost-head-col--source-co) { border-bottom-color: #13c2c2; } @@ -786,10 +1032,28 @@ onBeforeUnmount(() => { .gt-cost-table-wrap :deep(.gt-cost-head-col--fence) { border-bottom-color: #f56c6c; } .gt-cost-table-wrap :deep(.wops-col--wh) { justify-content: flex-start; + align-items: center; padding-left: 6px; + padding-right: 4px; + overflow: hidden; } -.gt-cost-table-wrap :deep(.wops-col--goods) { - padding-left: 6px; +.gt-cost-table-wrap :deep(.wops-flex-head .wops-col--wh) { + justify-content: flex-start; + text-align: left; +} +.gt-cost-table-wrap :deep(.wops-col--wh > .el-tooltip) { + display: block; + flex: 1 1 0; + min-width: 0; + max-width: 100%; + overflow: hidden; +} +.gt-cost-table-wrap :deep(.wops-col--wh .el-tooltip__trigger) { + display: block; + min-width: 0; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; } .gt-cost-table-wrap :deep(.gt-cost-head-col--bill), .gt-cost-table-wrap :deep(.wops-flex-head .gt-cost-head-col--bill) { @@ -848,24 +1112,27 @@ onBeforeUnmount(() => { justify-content: center; text-align: center; } -.gt-cost-table-wrap :deep(.gt-cost-head-col--int-tax), -.gt-cost-table-wrap :deep(.wops-flex-head .gt-cost-head-col--int-tax) { - display: flex; - align-items: center; - justify-content: center; - text-align: center; - overflow: visible; - text-overflow: clip; -} .gt-cost-table-wrap :deep(.gt-cost-col--internal-tax) { display: flex; align-items: center; justify-content: center; text-align: center; } -.gt-cost-table-wrap :deep(.gt-cost-head-col--min-price) { +.gt-cost-table-wrap :deep(.wops-flex-head .gt-cost-head-col--pre-price), +.gt-cost-table-wrap :deep(.wops-flex-head .gt-cost-head-col--min-price) { padding-left: 4px; - padding-right: 4px; + padding-right: 2px; + text-align: right; + overflow: visible; + text-overflow: clip; +} +.gt-cost-table-wrap :deep(.gt-cost-head-col--int-tax), +.gt-cost-table-wrap :deep(.wops-flex-head .gt-cost-head-col--int-tax) { + display: flex; + align-items: center; + justify-content: center; + padding-left: 0; + padding-right: 0; text-align: center; overflow: visible; text-overflow: clip; @@ -880,6 +1147,7 @@ onBeforeUnmount(() => { .gt-cost-table-wrap :deep(.is-cols-cost-fence .wops-col--num) { padding-right: 6px; } +.gt-cost-table-wrap :deep(.is-cols-cost-fence .gt-cost-col--pre-price), .gt-cost-table-wrap :deep(.is-cols-cost-fence .gt-cost-col--input-tax), .gt-cost-table-wrap :deep(.is-cols-cost-fence .gt-cost-col--internal-tax), .gt-cost-table-wrap :deep(.is-cols-cost-fence .gt-cost-col--tax-rate), diff --git a/src/config/buildId.json b/src/config/buildId.json index 08d85e4..6dd4455 100755 --- a/src/config/buildId.json +++ b/src/config/buildId.json @@ -1,4 +1,4 @@ { - "buildId": "20260717010", - "generatedAt": "2026-07-17T13:14:15.818Z" + "buildId": "20260718006", + "generatedAt": "2026-07-18T02:58:25.837Z" } \ No newline at end of file diff --git a/src/data/appChangelog.js b/src/data/appChangelog.js index 825f978..6333c3c 100755 --- a/src/data/appChangelog.js +++ b/src/data/appChangelog.js @@ -41,18 +41,93 @@ */ /** 应用版本(如 0.7.040 → 侧栏 v0.7,弹窗 v0.7.040) */ -export const APP_VERSION = '0.7.138' +export const APP_VERSION = '0.7.146' /** @type {ChangelogDay[]} */ export const APP_CHANGELOG = [ { date: '2026-07-17', items: [ + { + icon: 'mdi:fence', + kind: 'change', + text: '「物料弹窗 → 成本围栏」保存成本价上限 `POST /api/warehouse/rdc/set_cost_price_bound` 改传 `org_type`(1 一般纳税人 / 2 小规模 / 3 收据组织)+ `cost_price_bound`(该行围栏值),与 Tab 一一对应。', + }, + { + icon: 'mdi:fence', + kind: 'change', + text: '「物料弹窗 → 成本围栏」成本价上限按纳税身份 Tab 拆为三字段:`general_cost_price_bound`(一般纳税人)、`small_cost_price_bound`(小规模)、`individual_cost_price_bound`(收据);勾选/置灰读当前 Tab 对应值,各 Tab 独立互不影响。', + }, + { + icon: 'mdi:fence', + kind: 'add', + text: '「物料弹窗 → 成本围栏」勾选行对接 `POST /api/warehouse/rdc/set_cost_price_bound`(`warehouse_code` + `goods_code` + `org_type` + 该行围栏值);保存成功后本地更新当前 Tab 成本价上限,不重新拉列表。', + }, + { + icon: 'mdi:fence', + kind: 'change', + text: '「物料弹窗 → 成本围栏」按定价表三字段成本价上限驱动勾选与置灰:仅当对应值 >0 生效(默认 0 忽略);围栏值等于上限打勾,大于上限变灰,低于不变;打开页签时若已有上限同步展示。', + }, + { + icon: 'mdi:fence', + kind: 'change', + text: '「物料弹窗 → 成本围栏」单选勾选列移至表末(围栏值之后);勾选后不可取消,只能换选其他行;「供应商组织名称」「源采购组织名称」列宽恢复。', + }, + { + icon: 'mdi:fence', + kind: 'change', + text: '「物料弹窗 → 成本围栏」围栏值高于成本价上限的行置灰时,组织名称等 `.wops-name` 深蓝色字段同步变灰;样式限定在本表内,不影响其他页。', + }, + { + icon: 'mdi:fence', + kind: 'add', + text: '「物料弹窗 → 成本围栏」列表新增单选勾选列(方形圆角、蓝底白勾,与物料列表一致);同一时刻仅可选一行。', + }, + { + icon: 'mdi:fence', + kind: 'fix', + text: '「物料弹窗 → 成本围栏」修复表头在滚动容器外、表体在内导致纵向滚动条挤窄数据区、列 grid 错位(「源采购 RDC」等列最明显);表头移入同一滚动区并 sticky 吸顶,滚动与列对齐恢复正常。', + }, + { + icon: 'mdi:fence', + kind: 'change', + text: '「物料弹窗 → 成本围栏」「源采购 RDC」文本过长省略时悬停 tooltip 显示全称;表头「商品预估成本」「内部交易最低价」右对齐,「内部交易进项税率」居中。', + }, + { + icon: 'mdi:fence', + kind: 'add', + text: '「物料弹窗 → 成本围栏」新增「商品预估成本」列,读 `pre_price`(元),位于「供应商组织名称」之后。', + }, + { + icon: 'mdi:fence', + kind: 'change', + text: '「物料弹窗 → 成本围栏」列宽微调:「供应商组织名称」「源采购组织名称」各略收窄,让给「商品预估成本」,表头「商品预估成本」不换行。', + }, + { + icon: 'mdi:fence', + kind: 'change', + text: '「物料弹窗 → 成本围栏」表格去掉「商品名称」列;当前围栏数据去重后的商品名改显于弹窗顶栏「物料编码」后(多个以顿号连接,仅成本围栏页签时展示);省出列宽加给「供应商组织名称」「源采购组织名称」。', + }, + { + icon: 'mdi:fence', + kind: 'change', + text: '「物料弹窗 → 成本围栏」「供应商集团名称」「供应商组织名称」「源采购组织名称」文本过长省略时,悬停 tooltip 显示全称。', + }, + { + icon: 'mdi:fence', + kind: 'fix', + text: '「物料弹窗 → 成本围栏」修复定价表行 key 仅用 bloc+供应商编码致重复、切换 Tab 后 DOM 堆叠行数虚增;表头/数据区 flex 布局对齐仓库运维列表,数据超出可视区时出现纵向滚动。', + }, { icon: 'mdi:folder-cog-outline', kind: 'fix', text: '「录入商品 → 本集合商品」进项税率(专票):后端 `collection/goods/listed`(及 info 内嵌商品)的 `bills[]` 补回财务票据 `type`(1 一般纳税人法定增值税率 / 2 出口退税率 / 3 小规模征收率)与 `tax`;前端按集合推导票种 + 供货方组织类型匹配 `type=1|3` 显示税率(普票仍 0%、收据仍 /)。', }, + { + icon: 'mdi:format-list-bulleted', + kind: 'fix', + text: '「物料列表 → 物流配置」修复物流指数等常量显示 —:打开/切换物料时拉常量补全全局页签(finance/warehouse/cost/logistics)groups,不再只扫分类页签与选配。', + }, { icon: 'mdi:truck-delivery-outline', kind: 'fix', diff --git a/src/styles/warehouseOpsFlexTable.css b/src/styles/warehouseOpsFlexTable.css index 1164710..98d19b4 100644 --- a/src/styles/warehouseOpsFlexTable.css +++ b/src/styles/warehouseOpsFlexTable.css @@ -140,9 +140,9 @@ .wops-flex-table.is-cols-wh-labor-cost { --wops-cols: 8fr 10fr 8fr 10fr 7fr 9fr 7fr 10fr 7fr; } -/* 物料成本围栏(11 列):企业类型并入供应商/源采购组织名称(名称·类型) */ +/* 物料成本围栏(12 列):末列单选;商品名称提至弹窗顶栏;企业类型并入供应商/源采购组织名称 */ .wops-flex-table.is-cols-cost-fence { - --wops-cols: 10fr 12fr 10fr minmax(3.2em, 4fr) minmax(5.4em, 5.5fr) 12fr minmax(6.4em, 7.2fr) minmax(4.2em, 5.5fr) minmax(8.5em, 9fr) 9fr minmax(6.5em, 11fr); + --wops-cols: 10fr 16fr minmax(5.5em, 8.5fr) minmax(3.2em, 4fr) minmax(5.4em, 5.5fr) 16fr minmax(6.4em, 7.2fr) minmax(4.2em, 5.5fr) minmax(8.5em, 9fr) 9fr minmax(6.5em, 11fr) minmax(28px, 2.5fr); } /* 特殊定价 · 批量值按票据(3 列:票据 / 超额绩效 / 好处费) */ @@ -371,7 +371,8 @@ } .wops-col--code, -.wops-col--op { +.wops-col--op, +.wops-col--sel { display: flex; align-items: center; justify-content: center; diff --git a/src/views/material/MaterialList.vue b/src/views/material/MaterialList.vue index b6e5bee..dae053f 100755 --- a/src/views/material/MaterialList.vue +++ b/src/views/material/MaterialList.vue @@ -126,6 +126,7 @@
@@ -576,6 +577,10 @@
@@ -585,7 +590,9 @@ - + 保存
@@ -652,6 +659,7 @@