minitest2.0 0.0.0

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 (89) hide show
  1. package/.editorconfig +8 -0
  2. package/.env.development +3 -0
  3. package/.env.production +3 -0
  4. package/.env.test +3 -0
  5. package/.gitattributes +1 -0
  6. package/.oxlintrc.json +10 -0
  7. package/.prettierrc.json +6 -0
  8. package/.vscode/extensions.json +9 -0
  9. package/.vscode/settings.json +13 -0
  10. package/README.md +179 -0
  11. package/auto-imports.d.ts +629 -0
  12. package/components.d.ts +21 -0
  13. package/design-qa.md +36 -0
  14. package/docs/MX_API.md +244 -0
  15. package/docs/REQUEST.md +217 -0
  16. package/docs/jsapi.js +515 -0
  17. package/docs/package-json-guide.md +132 -0
  18. package/env.d.ts +15 -0
  19. package/eslint.config.ts +26 -0
  20. package/index.html +16 -0
  21. package/package.json +83 -0
  22. package/plugins/bump-version.ts +61 -0
  23. package/postcss.config.ts +15 -0
  24. package/public/favicon.ico +0 -0
  25. package/public/images/12a73787-86a9-4891-a65f-66104746f6a8.png +0 -0
  26. package/public/images/5798d7aa-ba8b-4605-8079-58b35495ac55.png +0 -0
  27. package/public/images/73fef1e4-0fd0-4a1a-9b8b-a70a5b6acbbc.png +0 -0
  28. package/public/images/bc685b4c-0cca-4a79-924c-a8ee10e6f8eb.png +0 -0
  29. package/public/images/c3dbbd9d-be56-490e-b9f4-6ee17ebefffc.png +0 -0
  30. package/public/images/ea745a10-42aa-4f44-8d7f-3ab02cc0adcd.png +0 -0
  31. package/public/images/f5876785-b927-4347-ba19-999114240649.png +0 -0
  32. package/public/images/img.png +0 -0
  33. package/public/images/opening-reserve-estimate.png +0 -0
  34. package/public/images/position-estimate-report.png +0 -0
  35. package/src/App.vue +131 -0
  36. package/src/api/announcement.ts +405 -0
  37. package/src/api/health.ts +13 -0
  38. package/src/api/pbc-position.ts +178 -0
  39. package/src/api/rmb-position.ts +233 -0
  40. package/src/api/tam.ts +173 -0
  41. package/src/api/user.ts +92 -0
  42. package/src/auto-imports.d.ts +633 -0
  43. package/src/components/AppTitleBar.vue +376 -0
  44. package/src/components.d.ts +33 -0
  45. package/src/config/config.properties +3 -0
  46. package/src/config/env.ts +6 -0
  47. package/src/config/plugin.properties.pro +6 -0
  48. package/src/config/plugin.properties.test +6 -0
  49. package/src/core/mxApi/index.ts +451 -0
  50. package/src/core/request/index.ts +135 -0
  51. package/src/main.ts +40 -0
  52. package/src/router/index.ts +144 -0
  53. package/src/stores/app.ts +103 -0
  54. package/src/stores/counter.ts +12 -0
  55. package/src/stores/user.ts +137 -0
  56. package/src/styles/nprogress.css +22 -0
  57. package/src/styles/vant-overrides.css +42 -0
  58. package/src/types/api.d.ts +14 -0
  59. package/src/types/nprogress.d.ts +8 -0
  60. package/src/utils/auth-token.ts +241 -0
  61. package/src/utils/code-highlight.ts +165 -0
  62. package/src/utils/copy.ts +36 -0
  63. package/src/utils/query.ts +27 -0
  64. package/src/utils/request.ts +238 -0
  65. package/src/utils/rmb-forecast.ts +84 -0
  66. package/src/utils/toast.ts +61 -0
  67. package/src/views/article-detail/index.vue +289 -0
  68. package/src/views/article-edit/index.vue +600 -0
  69. package/src/views/articles/index.vue +293 -0
  70. package/src/views/clearing-detail/index.vue +26 -0
  71. package/src/views/dashboard/index.vue +18 -0
  72. package/src/views/foreign-position/index.vue +26 -0
  73. package/src/views/home/index.vue +213 -0
  74. package/src/views/login/index.vue +275 -0
  75. package/src/views/mine/index.vue +147 -0
  76. package/src/views/net-debit/index.vue +26 -0
  77. package/src/views/opening-reserve-estimate/index.vue +28 -0
  78. package/src/views/pbc-position/index.vue +357 -0
  79. package/src/views/position-estimate/index.vue +67 -0
  80. package/src/views/position-estimate-report/index.vue +28 -0
  81. package/src/views/rmb-position/index.vue +1013 -0
  82. package/src/views/rmb-position-create/index.vue +221 -0
  83. package/src/views/rmb-position-detail/index.vue +355 -0
  84. package/src/views/settings/index.vue +67 -0
  85. package/src/views/warning/index.vue +26 -0
  86. package/tsconfig.app.json +18 -0
  87. package/tsconfig.json +11 -0
  88. package/tsconfig.node.json +28 -0
  89. package/vite.config.ts +100 -0
@@ -0,0 +1,84 @@
1
+ export type ForecastStatusCode = '1' | '2' | '3' | '4' | '5' | '6'
2
+
3
+ export type ForecastStatusOption = {
4
+ label: string
5
+ className: string
6
+ }
7
+
8
+ export const businessTypeMap: Record<string, string> = {
9
+ '1': '对公/个人客户收付',
10
+ '2': '本票签发/兑付',
11
+ '3': '三省一市汇票签发/兑付',
12
+ '4': '分行资金调拨',
13
+ '5': '同业业务',
14
+ '6': '资金业务',
15
+ '7': '理财业务',
16
+ '8': '债券承销业务',
17
+ '9': '司库业务',
18
+ '10': '现金出入库',
19
+ '11': '财政准备金退缴',
20
+ '12': '同业清算款项收付',
21
+ '13': '财政零余额业务',
22
+ '14': '银行承兑汇票',
23
+ '15': '托管业务',
24
+ '16': '票据业务',
25
+ }
26
+
27
+ export const clientTypeMap: Record<string, string> = {
28
+ '1': '公客户',
29
+ '2': '个人客户',
30
+ }
31
+
32
+ export const forecastStatusMap: Record<ForecastStatusCode, ForecastStatusOption> = {
33
+ '1': { label: '正常预报', className: 'status-tag-normal' },
34
+ '2': { label: '延迟预报', className: 'status-tag-delayed' },
35
+ '3': { label: '正常预报正常删除', className: 'status-tag-normal-delete' },
36
+ '4': { label: '正常预报延迟删除', className: 'status-tag-normal-delay-delete' },
37
+ '5': { label: '延迟预报延时删除', className: 'status-tag-delayed-late-delete' },
38
+ '6': { label: '延时预报延迟删除', className: 'status-tag-late-delay-delete' },
39
+ }
40
+
41
+ export const unknownForecastStatus: ForecastStatusOption = {
42
+ label: '未知状态',
43
+ className: 'status-tag-unknown',
44
+ }
45
+
46
+ export function resolveBusinessType(value?: string) {
47
+ return value ? businessTypeMap[value] || `业务类型${value}` : '业务类型-'
48
+ }
49
+
50
+ export function resolveClientType(value?: string) {
51
+ return value ? clientTypeMap[value] || '其他用户' : '其他用户'
52
+ }
53
+
54
+ export function resolveForecastStatus(value?: string): ForecastStatusOption {
55
+ if (value && value in forecastStatusMap) {
56
+ return forecastStatusMap[value as ForecastStatusCode]
57
+ }
58
+
59
+ return unknownForecastStatus
60
+ }
61
+
62
+ export function formatRmbForecastDate(value?: string) {
63
+ if (value && /^\d{8}$/.test(value)) {
64
+ return `${value.slice(0, 4)}-${value.slice(4, 6)}-${value.slice(6, 8)}`
65
+ }
66
+
67
+ return value || '-'
68
+ }
69
+
70
+ export function toBillionYuan(value?: string) {
71
+ const amount = Number(value || 0)
72
+ return Number.isFinite(amount) ? amount / 100000000 : 0
73
+ }
74
+
75
+ export function formatForecastAmount(value: number) {
76
+ const sign = value >= 0 ? '+' : '-'
77
+ return `${sign}${Math.abs(value).toFixed(2)}`
78
+ }
79
+
80
+ export function formatForecastAmountText(value?: string) {
81
+ if (!value?.trim()) return '-'
82
+
83
+ return `${formatForecastAmount(toBillionYuan(value))} 亿元`
84
+ }
@@ -0,0 +1,61 @@
1
+ import {
2
+ closeToast,
3
+ setToastDefaultOptions,
4
+ showLoadingToast,
5
+ showToast,
6
+ type ToastOptions,
7
+ type ToastWrapperInstance,
8
+ } from 'vant'
9
+
10
+ const DEFAULT_TOAST_OPTIONS: ToastOptions = {
11
+ duration: 2200,
12
+ position: 'middle',
13
+ wordBreak: 'break-word',
14
+ teleport: 'body',
15
+ zIndex: 9999,
16
+ }
17
+
18
+ setToastDefaultOptions(DEFAULT_TOAST_OPTIONS)
19
+ setToastDefaultOptions('loading', {
20
+ ...DEFAULT_TOAST_OPTIONS,
21
+ duration: 0,
22
+ forbidClick: true,
23
+ })
24
+
25
+ export type AppToastOptions = string | ToastOptions
26
+
27
+ export function showAppToast(options: AppToastOptions): ToastWrapperInstance {
28
+ if (typeof options === 'string') {
29
+ return showToast({
30
+ ...DEFAULT_TOAST_OPTIONS,
31
+ message: options,
32
+ })
33
+ }
34
+
35
+ return showToast({
36
+ ...DEFAULT_TOAST_OPTIONS,
37
+ ...options,
38
+ })
39
+ }
40
+
41
+ export function showAppLoadingToast(options: AppToastOptions = '加载中...'): ToastWrapperInstance {
42
+ if (typeof options === 'string') {
43
+ return showLoadingToast({
44
+ ...DEFAULT_TOAST_OPTIONS,
45
+ message: options,
46
+ duration: 0,
47
+ forbidClick: true,
48
+ })
49
+ }
50
+
51
+ return showLoadingToast({
52
+ ...DEFAULT_TOAST_OPTIONS,
53
+ duration: 0,
54
+ forbidClick: true,
55
+ ...options,
56
+ })
57
+ }
58
+
59
+ export function closeAppToast(all?: boolean) {
60
+ closeToast(all)
61
+ }
@@ -0,0 +1,289 @@
1
+ <script setup lang="ts">
2
+ import { highlightArticleHtml } from '@/utils/code-highlight'
3
+ import { copyText } from '@/utils/copy'
4
+ import { formatArticleDate, getArticleDetail, type Article } from '@/api/announcement'
5
+ import { showAppToast } from '@/utils/toast'
6
+
7
+ defineOptions({ name: 'ArticleDetailPage' })
8
+
9
+ const route = useRoute()
10
+ const router = useRouter()
11
+
12
+ const articleId = computed(() => String(route.params.id || ''))
13
+ const article = ref<Article>()
14
+ const loading = ref(false)
15
+ const loadError = ref(false)
16
+ const highlightedContent = computed(() =>
17
+ article.value ? highlightArticleHtml(article.value.content) : '',
18
+ )
19
+
20
+ onMounted(() => {
21
+ loadArticle()
22
+ })
23
+
24
+ async function loadArticle() {
25
+ if (!articleId.value) {
26
+ loadError.value = true
27
+ return
28
+ }
29
+
30
+ loading.value = true
31
+ loadError.value = false
32
+
33
+ try {
34
+ article.value = await getArticleDetail(articleId.value)
35
+ } catch (error) {
36
+ console.error('[公告] 详情加载失败', error)
37
+ loadError.value = true
38
+ } finally {
39
+ loading.value = false
40
+ }
41
+ }
42
+
43
+ function editArticle() {
44
+ if (article.value) {
45
+ router.push(`/articles/${article.value.id}/edit`)
46
+ }
47
+ }
48
+
49
+ async function copyArticleContent() {
50
+ if (!article.value) {
51
+ return
52
+ }
53
+
54
+ const text = articleContentToText(article.value.content)
55
+
56
+ if (!text) {
57
+ showAppToast('暂无可复制内容')
58
+ return
59
+ }
60
+
61
+ try {
62
+ await copyText(text)
63
+ showAppToast('已复制内容')
64
+ } catch (error) {
65
+ console.error('[公告] 内容复制失败', error)
66
+ showAppToast('复制失败')
67
+ }
68
+ }
69
+
70
+ function articleContentToText(html: string) {
71
+ const doc = new DOMParser().parseFromString(`<article>${html}</article>`, 'text/html')
72
+ const root = doc.body.firstElementChild
73
+
74
+ if (!root) {
75
+ return ''
76
+ }
77
+
78
+ root.querySelectorAll('pre code').forEach((code) => {
79
+ code.replaceWith(doc.createTextNode(`\n${code.textContent || ''}\n`))
80
+ })
81
+
82
+ return root.textContent?.replace(/\n{3,}/g, '\n\n').trim() || ''
83
+ }
84
+ </script>
85
+
86
+ <template>
87
+ <main class="article-detail-page">
88
+ <section v-if="loading" class="state-section">
89
+ <van-loading vertical>加载中...</van-loading>
90
+ </section>
91
+
92
+ <article v-else-if="article" class="article-detail">
93
+ <div class="article-title-block">
94
+ <h2>{{ article.title }}</h2>
95
+ <div class="article-meta">
96
+ <span><van-icon name="manager-o" />{{ article.author }}</span>
97
+ <span><van-icon name="clock-o" />{{ formatArticleDate(article.updatedAt) }}</span>
98
+ </div>
99
+ </div>
100
+
101
+ <div class="article-content" v-html="highlightedContent"></div>
102
+
103
+ <footer class="detail-actions">
104
+ <van-button block plain type="danger" icon="description-o" @click="copyArticleContent">
105
+ 复制内容
106
+ </van-button>
107
+ <van-button block type="danger" @click="editArticle">编辑公告</van-button>
108
+ </footer>
109
+ </article>
110
+
111
+ <van-empty v-else image="error" :description="loadError ? '公告加载失败' : '公告不存在'">
112
+ <van-button v-if="loadError" round type="danger" @click="loadArticle">重试</van-button>
113
+ <van-button v-else round type="danger" @click="router.replace('/articles')">
114
+ 返回公告列表
115
+ </van-button>
116
+ </van-empty>
117
+ </main>
118
+ </template>
119
+
120
+ <style scoped>
121
+ .article-detail-page {
122
+ width: 100%;
123
+ max-width: 750px;
124
+ min-height: var(--app-page-min-height, 100vh);
125
+ margin: 0 auto;
126
+ padding: 0 11px 28px;
127
+ color: #171b20;
128
+ background: #f7f8fa;
129
+ box-sizing: border-box;
130
+ }
131
+
132
+ .article-detail {
133
+ margin-top: 12px;
134
+ padding: 18px 15px 20px;
135
+ border: 1px solid #edf0f4;
136
+ border-radius: 8px;
137
+ background: #ffffff;
138
+ box-shadow: 0 6px 20px rgba(23, 27, 32, 0.05);
139
+ }
140
+
141
+ .state-section {
142
+ display: flex;
143
+ justify-content: center;
144
+ padding-top: 120px;
145
+ }
146
+
147
+ .article-title-block h2 {
148
+ margin: 0;
149
+ color: #171b20;
150
+ font-size: 23px;
151
+ font-weight: 800;
152
+ line-height: 1.32;
153
+ }
154
+
155
+ .article-meta {
156
+ display: flex;
157
+ flex-wrap: wrap;
158
+ gap: 8px 14px;
159
+ margin-top: 12px;
160
+ color: #8a929f;
161
+ font-size: 12px;
162
+ line-height: 1.3;
163
+ }
164
+
165
+ .article-meta span {
166
+ display: inline-flex;
167
+ align-items: center;
168
+ gap: 4px;
169
+ }
170
+
171
+ .article-content {
172
+ margin-top: 20px;
173
+ color: #252a31;
174
+ font-size: 16px;
175
+ line-height: 1.78;
176
+ word-break: break-word;
177
+ }
178
+
179
+ .article-content :deep(h2),
180
+ .article-content :deep(h3),
181
+ .article-content :deep(p),
182
+ .article-content :deep(blockquote),
183
+ .article-content :deep(pre),
184
+ .article-content :deep(ul),
185
+ .article-content :deep(ol) {
186
+ margin: 0 0 14px;
187
+ }
188
+
189
+ .article-content :deep(h2) {
190
+ padding-top: 4px;
191
+ color: #171b20;
192
+ font-size: 20px;
193
+ font-weight: 800;
194
+ line-height: 1.4;
195
+ }
196
+
197
+ .article-content :deep(h3) {
198
+ color: #171b20;
199
+ font-size: 18px;
200
+ font-weight: 750;
201
+ line-height: 1.4;
202
+ }
203
+
204
+ .article-content :deep(blockquote) {
205
+ padding: 10px 12px;
206
+ border-left: 4px solid #d8dde6;
207
+ border-radius: 6px;
208
+ color: #596273;
209
+ background: #f6f7f9;
210
+ }
211
+
212
+ .article-content :deep(ul),
213
+ .article-content :deep(ol) {
214
+ padding-left: 22px;
215
+ }
216
+
217
+ .article-content :deep(a) {
218
+ color: #1989fa;
219
+ text-decoration: none;
220
+ }
221
+
222
+ .article-content :deep(pre) {
223
+ position: relative;
224
+ overflow-x: auto;
225
+ padding: 13px 14px;
226
+ border: 1px solid #202c38;
227
+ border-radius: 7px;
228
+ background: #101820;
229
+ }
230
+
231
+ .article-content :deep(pre code) {
232
+ display: block;
233
+ min-width: max-content;
234
+ color: #eef3f7;
235
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', monospace;
236
+ font-size: 13px;
237
+ line-height: 1.65;
238
+ white-space: pre;
239
+ }
240
+
241
+ .article-content :deep(pre code[data-language]::before) {
242
+ content: attr(data-language);
243
+ position: sticky;
244
+ left: 0;
245
+ display: block;
246
+ width: fit-content;
247
+ margin-bottom: 8px;
248
+ color: #7f8b99;
249
+ font-family: inherit;
250
+ font-size: 11px;
251
+ line-height: 1;
252
+ }
253
+
254
+ .article-content :deep(.hl-keyword) {
255
+ color: #ff8aa0;
256
+ }
257
+
258
+ .article-content :deep(.hl-string) {
259
+ color: #8bdc8b;
260
+ }
261
+
262
+ .article-content :deep(.hl-number) {
263
+ color: #ffd36e;
264
+ }
265
+
266
+ .article-content :deep(.hl-comment) {
267
+ color: #7f8b99;
268
+ }
269
+
270
+ .article-content :deep(.hl-tag) {
271
+ color: #7cc7ff;
272
+ }
273
+
274
+ .detail-actions {
275
+ display: grid;
276
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
277
+ gap: 10px;
278
+ margin-top: 28px;
279
+ }
280
+
281
+ .detail-actions :deep(.van-button) {
282
+ height: 42px;
283
+ font-weight: 600;
284
+ }
285
+
286
+ :deep(.van-empty) {
287
+ padding-top: 96px;
288
+ }
289
+ </style>