imeik-bizui 2.5.4 → 2.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * 底价看板演示数据(模拟接口返回形态,经 normalize 映射)
3
3
  */
4
- import { DEFAULT_DETAIL_FORMULAS, DEFAULT_FOOTER_TEXT } from './utils'
5
4
 
6
5
  const DEMO_CUSTOMER = '大连公共交通建设投资集团有限公司'
7
6
 
@@ -11,7 +10,6 @@ export function createDemoOverview() {
11
10
  controlMethod: '申请后触底',
12
11
  applyTotalFee: 10000,
13
12
  year: '2026',
14
- footerText: DEFAULT_FOOTER_TEXT,
15
13
  customerName: DEMO_CUSTOMER,
16
14
  productBasePriceList: [
17
15
  {
@@ -68,15 +66,16 @@ export function createDemoOverview() {
68
66
 
69
67
  function buildSalesOrderRows() {
70
68
  const products = ['灵塑100', '塑菲200', '嗨体2.5ml']
71
- const types = ['药品', '耗材', '器械']
69
+ const types = ['GENUINE', 'FREE']
72
70
  const rows = []
73
71
  for (let i = 1; i <= 12; i++) {
74
72
  const productName = products[i % products.length]
75
73
  const qty = 20 + (i % 5) * 10
76
74
  rows.push({
77
- orderNo: `SO2026${String(300 + i).padStart(4, '0')}`,
75
+ orderNum: `XSCKD${String(136300 + i)}`,
78
76
  productName,
79
- productTypeName: types[i % types.length],
77
+ saleType: types[i % types.length],
78
+ qty,
80
79
  amount: qty * (productName === '塑菲200' ? 1040 : productName === '嗨体2.5ml' ? 680 : 820)
81
80
  })
82
81
  }
@@ -89,10 +88,10 @@ function buildNonSalesRows() {
89
88
  for (let i = 1; i <= 8; i++) {
90
89
  const qty = 2 + (i % 5)
91
90
  rows.push({
92
- outboundNo: `CK2026${String(100 + i).padStart(4, '0')}`,
91
+ orderNum: `QTCKD${String(136300 + i)}`,
93
92
  productName: products[i % products.length],
94
93
  qty,
95
- convertedFee: qty * 300
94
+ amount: qty * 300
96
95
  })
97
96
  }
98
97
  return rows
@@ -103,127 +102,26 @@ function buildExpenseRows() {
103
102
  const rows = []
104
103
  for (let i = 1; i <= 9; i++) {
105
104
  rows.push({
106
- orderNo: `FY2026${String(200 + i).padStart(4, '0')}`,
107
- typeName: types[(i - 1) % types.length],
108
- amount: 800 + i * 350
105
+ bizOrderCode: `FY2026${String(200 + i).padStart(4, '0')}`,
106
+ costTypeName: types[(i - 1) % types.length],
107
+ costAmount: 800 + i * 350
109
108
  })
110
109
  }
111
110
  rows.push({
112
- orderNo: '本次申请',
113
- typeName: '会议费用',
114
- amount: 10000,
111
+ bizOrderCode: '本次申请',
112
+ costTypeName: '会议费用',
113
+ costAmount: 10000,
115
114
  current: true
116
115
  })
117
116
  return rows
118
117
  }
119
118
 
120
- /** 模拟明细弹窗已规范化数据(useDemo 直接喂 DetailDialog) */
119
+ /** 模拟 getProductPriceDetail data */
121
120
  export function createDemoDetail(overview = {}) {
122
- const customerName = overview.customerName || DEMO_CUSTOMER
123
121
  return {
124
- title: `底价明细 · ${customerName}`,
125
- summaryColumns: [
126
- { prop: 'name', label: '产品', minWidth: 110, fixed: 'left' },
127
- { prop: 'currentPrice', label: '目前底价', type: 'number', digits: 0, minWidth: 88, align: 'right' },
128
- { prop: 'afterPrice', label: '申请后底价', type: 'number', digits: 0, minWidth: 96, align: 'right', bold: true },
129
- { prop: 'standard', label: '标准', type: 'number', digits: 0, minWidth: 80, align: 'right' },
130
- { prop: 'gap', label: '差距', type: 'gap', digits: 1, minWidth: 80, align: 'right' },
131
- { prop: 'orderAmount', label: '订单金额', type: 'number', digits: 0, minWidth: 96, align: 'right' },
132
- { prop: 'nonSalesFee', label: '非销促进费', type: 'number', digits: 0, minWidth: 96, align: 'right' },
133
- { prop: 'spentFee', label: '已花费用', type: 'number', digits: 0, minWidth: 88, align: 'right' },
134
- {
135
- prop: 'currentAllocation',
136
- label: '本次分摊',
137
- type: 'highlight',
138
- digits: 0,
139
- minWidth: 88,
140
- align: 'right',
141
- highlightTone: 'current'
142
- },
143
- { prop: 'qtyText', label: '数量 (正品+赠品)', minWidth: 120, align: 'center' },
144
- { prop: 'statusLabel', label: '状态', type: 'status', minWidth: 72, align: 'center' }
145
- ],
146
- summaryRows: [
147
- {
148
- name: '臻爱 灵塑100',
149
- currentPrice: 738,
150
- afterPrice: 706,
151
- standard: 730,
152
- orderAmount: 148000,
153
- nonSalesFee: 2400,
154
- spentFee: 3600,
155
- currentAllocation: 5208,
156
- applyAfterGenuineNum: 180,
157
- applyAfterFreeNum: 12
158
- },
159
- {
160
- name: '臻爱 塑菲200',
161
- currentPrice: 1014,
162
- afterPrice: 983,
163
- standard: 980,
164
- orderAmount: 96800,
165
- nonSalesFee: 1200,
166
- spentFee: 2100,
167
- currentAllocation: 4792,
168
- applyAfterGenuineNum: 90,
169
- applyAfterFreeNum: 6
170
- },
171
- {
172
- name: '嗨体 2.5ml',
173
- currentPrice: 612,
174
- afterPrice: 598,
175
- standard: 605,
176
- orderAmount: 45600,
177
- nonSalesFee: 900,
178
- spentFee: 1500,
179
- currentAllocation: 2100,
180
- applyAfterGenuineNum: 66,
181
- applyAfterFreeNum: 6
182
- }
183
- ],
184
- sections: [
185
- {
186
- key: 'salesOrders',
187
- title: '销售订单',
188
- subtitle: '(订单价 × 数量)',
189
- theme: 'purple',
190
- columns: [
191
- { prop: 'orderNo', label: '订单号', minWidth: 120 },
192
- { prop: 'productName', label: '产品', minWidth: 80 },
193
- { prop: 'productTypeName', label: '类型', width: 64 },
194
- { prop: 'amount', label: '金额', type: 'number', digits: 0, align: 'right', width: 88 }
195
- ],
196
- rows: buildSalesOrderRows(),
197
- footer: { label: '合计', theme: 'purple', values: ['金额 252,000'] }
198
- },
199
- {
200
- key: 'nonSales',
201
- title: '非销售出库',
202
- subtitle: '(折算非销促进费)',
203
- theme: 'danger',
204
- columns: [
205
- { prop: 'outboundNo', label: '出库单号', minWidth: 120 },
206
- { prop: 'productName', label: '产品', minWidth: 80 },
207
- { prop: 'qty', label: '数量', align: 'right', width: 56 },
208
- { prop: 'convertedFee', label: '折算费用', type: 'number', digits: 0, align: 'right', width: 88 }
209
- ],
210
- rows: buildNonSalesRows(),
211
- footer: { label: '合计', theme: 'danger', values: ['数量 32', '折算费用 9,600'] }
212
- },
213
- {
214
- key: 'salesExpenses',
215
- title: '销售费用',
216
- subtitle: '(费用申请单口径)',
217
- theme: 'danger',
218
- columns: [
219
- { prop: 'orderNo', label: '单号', minWidth: 100 },
220
- { prop: 'typeName', label: '类型', minWidth: 80 },
221
- { prop: 'amount', label: '金额', type: 'number', digits: 0, align: 'right', width: 88 }
222
- ],
223
- rows: buildExpenseRows(),
224
- footer: { label: '合计(含本次)', theme: 'danger', values: ['28,650'] }
225
- }
226
- ],
227
- formulas: DEFAULT_DETAIL_FORMULAS
122
+ customerName: overview.customerName || DEMO_CUSTOMER,
123
+ saleOrderList: buildSalesOrderRows(),
124
+ noSaleOrderList: buildNonSalesRows(),
125
+ costList: buildExpenseRows()
228
126
  }
229
127
  }
@@ -10,8 +10,28 @@ import {
10
10
  formatNumber
11
11
  } from './utils'
12
12
 
13
- /** getProductPrice.productBasePriceList → 卡片行 */
14
- export function mapOverviewProduct(row = {}) {
13
+ const SALE_TYPE_LABEL = {
14
+ GENUINE: '正品',
15
+ FREE: '赠品',
16
+ GIFT: '赠品'
17
+ }
18
+
19
+ function saleTypeLabel(value) {
20
+ if (value == null || value === '') return ''
21
+ const key = String(value).toUpperCase()
22
+ return SALE_TYPE_LABEL[key] || String(value)
23
+ }
24
+
25
+ /** 数量:applyAfterGenuineNum(正品)+ applyAfterFreeNum(赠品) */
26
+ function buildQtyText(genuine, free) {
27
+ if (genuine == null && free == null) return '-'
28
+ const g = Number(genuine) || 0
29
+ const f = Number(free) || 0
30
+ return `${g + f} (${g}+${f})`
31
+ }
32
+
33
+ /** getProductPrice.productBasePriceList → 卡片 / 明细汇总行 */
34
+ function mapOverviewProduct(row = {}) {
15
35
  const beforePrice = row.applyBeforePrice
16
36
  const afterPrice = row.applyAfterPrice
17
37
  const standard = row.productBasePrice
@@ -19,33 +39,26 @@ export function mapOverviewProduct(row = {}) {
19
39
 
20
40
  return {
21
41
  key: row.productCode || row.erpCode,
22
- erpCode: row.erpCode,
23
- productCode: row.productCode,
24
- name: row.productName,
25
- shortName: row.productShortName || row.productName,
42
+ name: row.productShortName || row.productName,
26
43
  beforePrice,
27
44
  afterPrice,
28
45
  standard,
29
- applyCount: row.applyCount,
30
- applyAfterFeeAmount: row.applyAfterFeeAmount,
31
- applyAfterFreeNum: row.applyAfterFreeNum,
32
- applyAfterGenuineNum: row.applyAfterGenuineNum,
33
- applyAfterNoSaleOrderAmount: row.applyAfterNoSaleOrderAmount,
34
- applyAfterSaleOrderAmount: row.applyAfterSaleOrderAmount,
35
- applyThisFeeAmount: row.applyThisFeeAmount,
36
46
  gap,
37
47
  gapLabel,
38
- hit
48
+ hit,
49
+ statusLabel: hit ? '触底' : '正常',
50
+ orderAmount: row.applyAfterSaleOrderAmount,
51
+ nonSalesFee: row.applyAfterNoSaleOrderAmount,
52
+ spentFee: row.applyAfterFeeAmount,
53
+ currentAllocation: row.applyThisFeeAmount,
54
+ qtyText: buildQtyText(row.applyAfterGenuineNum, row.applyAfterFreeNum)
39
55
  }
40
56
  }
41
57
 
42
58
  function buildMetaText(data = {}) {
43
- if (data.metaText) return data.metaText
44
59
  const parts = []
45
60
  if (data.applyTotalFee != null && data.applyTotalFee !== '') {
46
61
  parts.push(`本次申请费用 ${formatNumber(data.applyTotalFee)} 元`)
47
- } else if (data.meetingFee != null && data.meetingFee !== '') {
48
- parts.push(`本次申请费用 ${formatNumber(data.meetingFee)} 元`)
49
62
  }
50
63
  if (data.year != null && data.year !== '') {
51
64
  parts.push(`统计周期 ${data.year} 年`)
@@ -57,8 +70,7 @@ function buildMetaText(data = {}) {
57
70
  * getProductPrice data → FloorPriceBoard
58
71
  */
59
72
  export function normalizeOverview(data = {}, options = {}) {
60
- const list = data.productBasePriceList || []
61
- const products = list.map(mapOverviewProduct)
73
+ const products = (data.productBasePriceList || []).map(mapOverviewProduct)
62
74
  const controlMethod = String(data.controlMethod || '').trim()
63
75
  const anyHit = products.some((p) => p.hit)
64
76
  const status = controlMethod
@@ -74,17 +86,16 @@ export function normalizeOverview(data = {}, options = {}) {
74
86
  statusLabel: controlMethod || DEFAULT_STATUS_LABEL[status] || '',
75
87
  products,
76
88
  metaText: buildMetaText(data),
77
- footerText: data.footerText || options.footerText || DEFAULT_FOOTER_TEXT,
78
- customerName: data.customerName || options.customerName || '',
79
- controlMethod: data.controlMethod,
80
- rawProductList: products
89
+ footerText: DEFAULT_FOOTER_TEXT,
90
+ customerName: options.customerName || data.customerName || '',
91
+ controlMethod: data.controlMethod
81
92
  }
82
93
  }
83
94
 
84
- function defaultSummaryColumns() {
95
+ function summaryColumns() {
85
96
  return [
86
97
  { prop: 'name', label: '产品', minWidth: 110, fixed: 'left' },
87
- { prop: 'currentPrice', label: '目前底价', type: 'number', digits: 0, minWidth: 88, align: 'right' },
98
+ { prop: 'beforePrice', label: '目前底价', type: 'number', digits: 0, minWidth: 88, align: 'right' },
88
99
  { prop: 'afterPrice', label: '申请后底价', type: 'number', digits: 0, minWidth: 96, align: 'right', bold: true },
89
100
  { prop: 'standard', label: '标准', type: 'number', digits: 0, minWidth: 80, align: 'right' },
90
101
  { prop: 'gap', label: '差距', type: 'gap', digits: 1, minWidth: 80, align: 'right' },
@@ -105,85 +116,20 @@ function defaultSummaryColumns() {
105
116
  ]
106
117
  }
107
118
 
108
- /**
109
- * 数量展示对齐原型:合计 (正品+赠品)
110
- * 来源:applyAfterGenuineNum(正品)+ applyAfterFreeNum(赠品)
111
- */
112
- function buildQtyText(row = {}) {
113
- const genuine = row.applyAfterGenuineNum
114
- const free = row.applyAfterFreeNum
115
- if (genuine != null || free != null) {
116
- const g = Number(genuine) || 0
117
- const f = Number(free) || 0
118
- return `${g + f} (${g}+${f})`
119
- }
120
- if (row.qtyText != null && row.qtyText !== '') return row.qtyText
121
- if (row.applyCount != null && row.applyCount !== '') {
122
- return String(row.applyCount)
123
- }
124
- return '-'
125
- }
126
-
127
- /** 明细汇总行:优先用概览产品;差距 / 触底由前端重算 */
128
- function mapSummaryRow(row = {}) {
129
- const afterPrice = row.afterPrice != null ? row.afterPrice : row.applyAfterPrice
130
- const standard = row.standard != null ? row.standard : row.productBasePrice
131
- const { gap, gapLabel, hit } = computePriceGap(afterPrice, standard, { digits: 1 })
132
-
133
- return {
134
- name: row.name || row.productName || row.shortName,
135
- currentPrice:
136
- row.currentPrice != null
137
- ? row.currentPrice
138
- : row.beforePrice != null
139
- ? row.beforePrice
140
- : row.applyBeforePrice,
141
- afterPrice,
142
- standard,
143
- gap,
144
- gapLabel,
145
- orderAmount: row.orderAmount != null ? row.orderAmount : row.applyAfterSaleOrderAmount,
146
- nonSalesFee: row.nonSalesFee != null ? row.nonSalesFee : row.applyAfterNoSaleOrderAmount,
147
- spentFee: row.spentFee != null ? row.spentFee : row.applyAfterFeeAmount,
148
- currentAllocation:
149
- row.currentAllocation != null ? row.currentAllocation : row.applyThisFeeAmount,
150
- qtyText: buildQtyText(row),
151
- hit,
152
- statusLabel: hit ? '触底' : '正常'
153
- }
154
- }
155
-
156
119
  function sumBy(rows, prop) {
157
120
  return (rows || []).reduce((sum, row) => sum + (Number(row[prop]) || 0), 0)
158
121
  }
159
122
 
160
123
  /** getProductPriceDetail:saleOrderList / noSaleOrderList / costList */
161
- function buildSectionsFromApi(data = {}) {
162
- if (data.sections) return data.sections
163
-
124
+ function buildSections(data = {}) {
164
125
  const saleRows = (data.saleOrderList || []).map((r) => ({
165
- orderNo: r.bizOrderCode,
166
- productName: r.productName,
167
- productTypeName: r.productTypeName || '',
168
- amount: r.amount,
169
- productOrderPrice: r.productOrderPrice
170
- }))
171
-
172
- const nonSaleRows = (data.noSaleOrderList || []).map((r) => ({
173
- outboundNo: r.bizOrderCode,
174
- productName: r.productName,
175
- qty: r.num,
176
- convertedFee: r.amount
126
+ ...r,
127
+ saleTypeLabel: saleTypeLabel(r.saleType)
177
128
  }))
178
-
179
- const expenseRows = (data.costList || []).map((r) => ({
180
- orderNo: r.bizOrderCode || '',
181
- typeName: r.costTypeName,
182
- amount: r.costAmount,
183
- current: !!r.current
184
- }))
185
-
129
+ const nonSaleRows = data.noSaleOrderList || []
130
+ const expenseRows = data.costList || []
186
131
  const sections = []
132
+
187
133
  if (saleRows.length) {
188
134
  sections.push({
189
135
  key: 'salesOrders',
@@ -191,13 +137,14 @@ function buildSectionsFromApi(data = {}) {
191
137
  subtitle: '(订单价 × 数量)',
192
138
  theme: 'purple',
193
139
  columns: [
194
- { prop: 'orderNo', label: '订单号', minWidth: 120 },
140
+ { prop: 'orderNum', label: '订单号', minWidth: 120 },
195
141
  { prop: 'productName', label: '产品', minWidth: 80 },
196
- { prop: 'productTypeName', label: '类型', width: 64 },
142
+ { prop: 'saleTypeLabel', label: '类型', width: 64 },
143
+ { prop: 'qty', label: '数量', align: 'right', width: 56 },
197
144
  { prop: 'amount', label: '金额', type: 'number', digits: 0, align: 'right', width: 88 }
198
145
  ],
199
146
  rows: saleRows,
200
- footer: data.saleOrderFooter || {
147
+ footer: {
201
148
  label: '合计',
202
149
  theme: 'purple',
203
150
  values: [`金额 ${formatNumber(sumBy(saleRows, 'amount'))}`]
@@ -211,18 +158,18 @@ function buildSectionsFromApi(data = {}) {
211
158
  subtitle: '(折算非销促进费)',
212
159
  theme: 'danger',
213
160
  columns: [
214
- { prop: 'outboundNo', label: '出库单号', minWidth: 120 },
161
+ { prop: 'orderNum', label: '出库单号', minWidth: 120 },
215
162
  { prop: 'productName', label: '产品', minWidth: 80 },
216
163
  { prop: 'qty', label: '数量', align: 'right', width: 56 },
217
- { prop: 'convertedFee', label: '折算费用', type: 'number', digits: 0, align: 'right', width: 88 }
164
+ { prop: 'amount', label: '折算费用', type: 'number', digits: 0, align: 'right', width: 88 }
218
165
  ],
219
166
  rows: nonSaleRows,
220
- footer: data.noSaleOrderFooter || {
167
+ footer: {
221
168
  label: '合计',
222
169
  theme: 'danger',
223
170
  values: [
224
171
  `数量 ${formatNumber(sumBy(nonSaleRows, 'qty'))}`,
225
- `折算费用 ${formatNumber(sumBy(nonSaleRows, 'convertedFee'))}`
172
+ `折算费用 ${formatNumber(sumBy(nonSaleRows, 'amount'))}`
226
173
  ]
227
174
  }
228
175
  })
@@ -234,15 +181,15 @@ function buildSectionsFromApi(data = {}) {
234
181
  subtitle: '(费用申请单口径)',
235
182
  theme: 'danger',
236
183
  columns: [
237
- { prop: 'orderNo', label: '单号', minWidth: 100 },
238
- { prop: 'typeName', label: '类型', minWidth: 80 },
239
- { prop: 'amount', label: '金额', type: 'number', digits: 0, align: 'right', width: 88 }
184
+ { prop: 'bizOrderCode', label: '单号', minWidth: 100 },
185
+ { prop: 'costTypeName', label: '类型', minWidth: 80 },
186
+ { prop: 'costAmount', label: '金额', type: 'number', digits: 0, align: 'right', width: 88 }
240
187
  ],
241
188
  rows: expenseRows,
242
- footer: data.costFooter || {
189
+ footer: {
243
190
  label: '合计(含本次)',
244
191
  theme: 'danger',
245
- values: [formatNumber(sumBy(expenseRows, 'amount'))]
192
+ values: [formatNumber(sumBy(expenseRows, 'costAmount'))]
246
193
  }
247
194
  })
248
195
  }
@@ -252,28 +199,22 @@ function buildSectionsFromApi(data = {}) {
252
199
  /**
253
200
  * getProductPriceDetail data → DetailDialog
254
201
  *
255
- * 顶部汇总表:优先用概览 productBasePriceList / options.overviewProducts(与看板同源)
202
+ * 顶部汇总:概览 productBasePriceList(与看板同源)
256
203
  * 明细三区:saleOrderList / noSaleOrderList / costList
257
204
  */
258
205
  export function normalizeDetail(data = {}, options = {}) {
259
- const customerName = data.customerName || options.customerName || ''
260
- const title = data.title || (customerName ? `底价明细 · ${customerName}` : '底价明细')
261
-
262
- let summaryRows = []
206
+ const customerName = options.customerName || data.customerName || ''
263
207
  const overviewProducts = options.overviewProducts
264
- if (Array.isArray(overviewProducts) && overviewProducts.length) {
265
- summaryRows = overviewProducts.map(mapSummaryRow)
266
- } else if (Array.isArray(data.productBasePriceList) && data.productBasePriceList.length) {
267
- summaryRows = data.productBasePriceList.map((r) => mapSummaryRow(mapOverviewProduct(r)))
268
- } else if (Array.isArray(data.summaryList) && data.summaryList.length) {
269
- summaryRows = data.summaryList.map(mapSummaryRow)
270
- }
208
+ const summaryRows =
209
+ Array.isArray(overviewProducts) && overviewProducts.length
210
+ ? overviewProducts
211
+ : (data.productBasePriceList || []).map(mapOverviewProduct)
271
212
 
272
213
  return {
273
- title,
274
- summaryColumns: data.summaryColumns || defaultSummaryColumns(),
214
+ title: customerName ? `底价明细 · ${customerName}` : '底价明细',
215
+ summaryColumns: summaryColumns(),
275
216
  summaryRows,
276
- sections: buildSectionsFromApi(data),
277
- formulas: data.formulas || data.formulaList || DEFAULT_DETAIL_FORMULAS
217
+ sections: buildSections(data),
218
+ formulas: DEFAULT_DETAIL_FORMULAS
278
219
  }
279
220
  }