vue3-element-plus-app-chu 0.0.13 → 0.0.14

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.
Files changed (30) hide show
  1. package/README.md +4 -4
  2. package/package.json +3 -2
  3. package/src/layout/index.vue +21 -36
  4. package/src/router/index.ts +110 -0
  5. package/src/router/menus.ts +16 -0
  6. package/src/view/dcps/README.md +59 -0
  7. package/src/view/dcps/auth/components/AuthorizationDetailDialog.vue +2 -0
  8. package/src/view/dcps/auth/index.vue +4 -6
  9. package/src/view/dcps/balance/index.vue +4 -6
  10. package/src/view/dcps/common.css +7 -0
  11. package/src/view/dcps/estimation/api.ts +17 -142
  12. package/src/view/dcps/estimation/components/EstimationDetailDialog.vue +4 -1
  13. package/src/view/dcps/estimation/index.vue +7 -8
  14. package/src/view/dcps/estimation/utils.ts +113 -0
  15. package/src/view/dcps/forecast/api.ts +9 -201
  16. package/src/view/dcps/forecast/components/ForecastDetailView.vue +19 -9
  17. package/src/view/dcps/forecast/components/ForecastEditorView.vue +25 -14
  18. package/src/view/dcps/forecast/components/ForecastListView.vue +8 -11
  19. package/src/view/dcps/forecast/components/ForecastPageShell.vue +3 -7
  20. package/src/view/dcps/forecast/index.vue +22 -50
  21. package/src/view/dcps/forecast/list-refresh.ts +11 -0
  22. package/src/view/dcps/forecast/row-cache.ts +18 -0
  23. package/src/view/dcps/forecast/utils.ts +169 -0
  24. package/src/view/dcps/injection/components/InjectionDetailDialog.vue +2 -0
  25. package/src/view/dcps/injection/index.vue +4 -6
  26. package/src/view/dcps/write-off/api.ts +9 -164
  27. package/src/view/dcps/write-off/components/FlowWriteOffView.vue +4 -3
  28. package/src/view/dcps/write-off/components/ForecastWriteOffView.vue +4 -3
  29. package/src/view/dcps/write-off/index.vue +6 -7
  30. package/src/view/dcps/write-off/utils.ts +131 -0
@@ -0,0 +1,131 @@
1
+ import type { ApiResponse } from '@/utils/request'
2
+
3
+ /** 列表查询结果:当前页记录和符合条件的总条数。 */
4
+ export interface WriteOffPage<T> {
5
+ data: T[] // 当前页的记录列表。
6
+ total: number // 符合查询条件的总记录数。
7
+ }
8
+
9
+ /** 校验公共响应头;retCode 不是 00000 时按服务端提示或 fallback 抛错。 */
10
+ export function ensureSuccess<T>(response: ApiResponse<T>, fallback: string): T | null {
11
+ if (response?.head?.retCode !== '00000') {
12
+ throw new Error(response?.head?.retMsg?.trim() || fallback)
13
+ }
14
+ return response.body
15
+ }
16
+
17
+ /**
18
+ * 校验查询响应并取出当前页数据;两个查询接口共享同一种 body 结构。
19
+ * @param body 查询接口返回的业务数据。
20
+ * @param fallback body 缺少 resultList 时的错误提示文本。
21
+ */
22
+ export function readPage<R extends { resultList?: unknown[]; totalCount?: unknown }>(
23
+ body: R | null,
24
+ fallback: string,
25
+ ): WriteOffPage<NonNullable<R['resultList']>[number]> {
26
+ if (!body || !Array.isArray(body.resultList)) throw new Error(fallback)
27
+ const totalText = String(body.totalCount ?? body.resultList.length)
28
+ const total = Number(totalText)
29
+ if (!/^\d+$/.test(totalText) || !Number.isSafeInteger(total)) throw new Error('查询返回的总条数无效')
30
+ return { data: body.resultList, total }
31
+ }
32
+
33
+ /** 提取可展示的错误信息;无法识别时给出统一提示。 */
34
+ export function getErrorMessage(error: unknown): string {
35
+ if (error instanceof Error && error.message.trim()) return error.message
36
+ if (typeof error === 'string' && error.trim()) return error
37
+ return '操作失败,请稍后重试'
38
+ }
39
+
40
+ /** 业务日期始终按上海时区生成,不依赖浏览器所在时区,格式为 YYYYMMDD。 */
41
+ export function getToday(): string {
42
+ const parts = new Intl.DateTimeFormat('en-CA', {
43
+ timeZone: 'Asia/Shanghai', year: 'numeric', month: '2-digit', day: '2-digit',
44
+ }).formatToParts(new Date())
45
+ const read = (type: Intl.DateTimeFormatPartTypes) => parts.find((part) => part.type === type)?.value || ''
46
+ return read('year') + read('month') + read('day')
47
+ }
48
+
49
+ /** 判断是否为合法的 YYYYMMDD 日期。 */
50
+ export function isValidDate(value: string): boolean {
51
+ if (!/^\d{8}$/.test(value)) return false
52
+ const year = Number(value.slice(0, 4))
53
+ const month = Number(value.slice(4, 6))
54
+ const day = Number(value.slice(6, 8))
55
+ if (year < 1 || month < 1 || month > 12 || day < 1) return false
56
+ const leap = year % 400 === 0 || (year % 4 === 0 && year % 100 !== 0)
57
+ const monthDays = [31, leap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
58
+ return day <= (monthDays[month - 1] || 0)
59
+ }
60
+
61
+ /** 把 YYYYMMDD 格式化为 YYYY-MM-DD 展示;空值显示横线,其他格式保留原文。 */
62
+ export function formatDate(value?: string): string {
63
+ const text = value?.trim() || ''
64
+ if (!text) return '—'
65
+ return isValidDate(text) ? `${text.slice(0, 4)}-${text.slice(4, 6)}-${text.slice(6, 8)}` : text
66
+ }
67
+
68
+ /** 只接受十进制文本和合法千分位;保留全部小数位,避免改变输入精度。 */
69
+ export function normalizeAmount(value: string): string {
70
+ let text = value.trim()
71
+ if (text.includes(',')) {
72
+ if (!/^[+-]?\d{1,3}(?:,\d{3})+(?:\.\d*)?$/.test(text)) return ''
73
+ text = text.replace(/,/g, '')
74
+ }
75
+ const match = /^([+-]?)(\d*)(?:\.(\d*))?$/.exec(text)
76
+ if (!match || (!match[2] && !match[3])) return ''
77
+ const integer = (match[2] || '0').replace(/^0+(?=\d)/, '')
78
+ const fraction = match[3] || ''
79
+ const negative = match[1] === '-' && /[1-9]/.test(integer + fraction)
80
+ return `${negative ? '-' : ''}${integer}${fraction ? `.${fraction}` : ''}`
81
+ }
82
+
83
+ /** 千分位展示至少两位小数,超过两位的小数原样保留,不做浮点数转换或舍入。 */
84
+ export function formatAmount(value?: string): string {
85
+ const original = value?.trim() || ''
86
+ if (!original) return '—'
87
+ const normalized = normalizeAmount(original)
88
+ if (!normalized) return original
89
+ const negative = normalized.startsWith('-')
90
+ const [integer = '0', fraction = ''] = (negative ? normalized.slice(1) : normalized).split('.')
91
+ const grouped = integer.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
92
+ return `${negative ? '-' : ''}${grouped}.${fraction.padEnd(2, '0')}`
93
+ }
94
+
95
+ /** 按统一小数位数转为 BigInt,任意长度的十进制金额都可精确比较。 */
96
+ export function compareAmounts(left: string, right: string): number {
97
+ const leftText = normalizeAmount(left)
98
+ const rightText = normalizeAmount(right)
99
+ if (!leftText || !rightText) {
100
+ if (!leftText && !rightText) return 0
101
+ return leftText ? -1 : 1
102
+ }
103
+ const parts = (text: string) => {
104
+ const negative = text.startsWith('-')
105
+ const [integer = '0', fraction = ''] = (negative ? text.slice(1) : text).split('.')
106
+ return { negative, integer, fraction }
107
+ }
108
+ const a = parts(leftText)
109
+ const b = parts(rightText)
110
+ const precision = Math.max(a.fraction.length, b.fraction.length)
111
+ const toInteger = (amount: ReturnType<typeof parts>) =>
112
+ BigInt(amount.integer + amount.fraction.padEnd(precision, '0')) * (amount.negative ? -1n : 1n)
113
+ const aValue = toInteger(a)
114
+ const bValue = toInteger(b)
115
+ return aValue < bValue ? -1 : aValue > bValue ? 1 : 0
116
+ }
117
+
118
+ /** 业务类型字典:201 兑回、202 兑出、203 付现;超出字典的值保留原文。 */
119
+ export const busiTypeLabels: Record<string, string> = { '201': '兑回', '202': '兑出', '203': '付现' }
120
+ /** 收支方向字典:I 来账、O 往账。 */
121
+ export const payDirecLabels: Record<string, string> = { I: '来账', O: '往账' }
122
+ /** 支付渠道:核销预报为 8、流水为 5,均表示数字人民币;其余渠道保留原文。 */
123
+ export const payChannelLabels: Record<string, string> = { '5': '数字人民币', '8': '数字人民币' }
124
+ /** 核销状态字典:1 未核销、2 已核销。 */
125
+ export const verifyStatusLabels: Record<string, string> = { '1': '未核销', '2': '已核销' }
126
+
127
+ /** 读取字典文案;空值显示横线,未登记的代码保留原文展示。 */
128
+ export function dictLabel(labels: Record<string, string>, value?: string): string {
129
+ const text = value?.trim() || ''
130
+ return text ? labels[text] ?? text : '—'
131
+ }