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,293 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ extractTextFromHtml,
4
+ formatArticleDate,
5
+ queryArticles,
6
+ type Article,
7
+ } from '@/api/announcement'
8
+
9
+ defineOptions({ name: 'ArticleListPage' })
10
+
11
+ const router = useRouter()
12
+ const articles = ref<Article[]>([])
13
+ const keyword = ref('')
14
+ const loading = ref(false)
15
+ const loadError = ref(false)
16
+
17
+ const filteredArticles = computed(() => {
18
+ const key = keyword.value.trim().toLowerCase()
19
+
20
+ if (!key) {
21
+ return articles.value
22
+ }
23
+
24
+ return articles.value.filter((article) => {
25
+ const searchableText = `${article.title} ${extractTextFromHtml(article.content)}`
26
+
27
+ return searchableText.toLowerCase().includes(key)
28
+ })
29
+ })
30
+
31
+ const emptyDescription = computed(() => {
32
+ if (loadError.value) return '公告加载失败'
33
+
34
+ return keyword.value.trim() ? '未找到匹配公告' : '暂无公告'
35
+ })
36
+
37
+ onMounted(() => {
38
+ loadArticles()
39
+ })
40
+
41
+ async function loadArticles() {
42
+ loading.value = true
43
+ loadError.value = false
44
+
45
+ try {
46
+ articles.value = await queryArticles()
47
+ } catch (error) {
48
+ console.error('[公告] 列表加载失败', error)
49
+ loadError.value = true
50
+ } finally {
51
+ loading.value = false
52
+ }
53
+ }
54
+
55
+ function openPublish() {
56
+ router.push('/articles/new')
57
+ }
58
+
59
+ function openDetail(article: Article) {
60
+ router.push(`/articles/${article.id}`)
61
+ }
62
+
63
+ function openEdit(article: Article) {
64
+ router.push(`/articles/${article.id}/edit`)
65
+ }
66
+ </script>
67
+
68
+ <template>
69
+ <main class="article-list-page">
70
+ <header class="article-hero">
71
+ <div class="hero-copy">
72
+ <p>Announcement Center</p>
73
+ <h2>公告中心</h2>
74
+ <span>{{ articles.length }} 篇内容</span>
75
+ </div>
76
+ <van-button size="small" type="danger" icon="plus" @click="openPublish">发布</van-button>
77
+ </header>
78
+
79
+ <section class="search-section">
80
+ <van-search v-model="keyword" shape="round" placeholder="搜索标题或正文" />
81
+ </section>
82
+
83
+ <section v-if="loading" class="state-section">
84
+ <van-loading vertical>加载中...</van-loading>
85
+ </section>
86
+
87
+ <section v-else-if="filteredArticles.length" class="article-list" aria-label="公告列表">
88
+ <article
89
+ v-for="article in filteredArticles"
90
+ :key="article.id"
91
+ class="article-card"
92
+ tabindex="0"
93
+ role="button"
94
+ @click="openDetail(article)"
95
+ @keydown.enter="openDetail(article)"
96
+ >
97
+ <div class="article-card-main">
98
+ <h2>{{ article.title }}</h2>
99
+ <div class="article-meta">
100
+ <span><van-icon name="clock-o" />{{ formatArticleDate(article.updatedAt) }}</span>
101
+ <span><van-icon name="manager-o" />{{ article.author }}</span>
102
+ </div>
103
+ </div>
104
+ <button
105
+ class="article-edit-btn"
106
+ type="button"
107
+ aria-label="编辑公告"
108
+ @click.stop="openEdit(article)"
109
+ >
110
+ <van-icon name="edit" />
111
+ </button>
112
+ </article>
113
+ </section>
114
+
115
+ <van-empty v-else image="search" :description="emptyDescription">
116
+ <van-button v-if="loadError" round type="danger" icon="replay" @click="loadArticles">
117
+ 重试
118
+ </van-button>
119
+ <van-button v-else round type="danger" icon="plus" @click="openPublish">发布公告</van-button>
120
+ </van-empty>
121
+ </main>
122
+ </template>
123
+
124
+ <style scoped>
125
+ .article-list-page {
126
+ width: 100%;
127
+ max-width: 750px;
128
+ min-height: var(--app-page-min-height, 100vh);
129
+ margin: 0 auto;
130
+ padding: 14px 11px 86px;
131
+ color: #171b20;
132
+ background:
133
+ radial-gradient(circle at 12% 0%, rgba(242, 47, 61, 0.11), transparent 118px),
134
+ linear-gradient(180deg, #fffafa 0%, #f7f8fa 38%, #f7f8fa 100%);
135
+ box-sizing: border-box;
136
+ }
137
+
138
+ .article-hero {
139
+ display: flex;
140
+ align-items: center;
141
+ justify-content: space-between;
142
+ gap: 12px;
143
+ min-height: 96px;
144
+ padding: 18px 16px;
145
+ border-radius: 8px;
146
+ background: linear-gradient(135deg, #ffffff 0%, #fff4f5 100%);
147
+ box-shadow: 0 8px 24px rgba(23, 27, 32, 0.06);
148
+ box-sizing: border-box;
149
+ }
150
+
151
+ .hero-copy {
152
+ min-width: 0;
153
+ }
154
+
155
+ .hero-copy p,
156
+ .hero-copy h2 {
157
+ margin: 0;
158
+ }
159
+
160
+ .hero-copy p {
161
+ color: #f22f3d;
162
+ font-size: 12px;
163
+ font-weight: 700;
164
+ line-height: 1.2;
165
+ }
166
+
167
+ .hero-copy h2 {
168
+ margin-top: 6px;
169
+ color: #171b20;
170
+ font-size: 24px;
171
+ font-weight: 800;
172
+ line-height: 1.18;
173
+ }
174
+
175
+ .hero-copy span {
176
+ display: block;
177
+ margin-top: 7px;
178
+ color: #6b7280;
179
+ font-size: 13px;
180
+ line-height: 1.4;
181
+ }
182
+
183
+ .article-hero :deep(.van-button) {
184
+ flex: 0 0 auto;
185
+ min-width: 78px;
186
+ border: 0;
187
+ background: linear-gradient(135deg, #f22f3d 0%, #ff6069 100%);
188
+ box-shadow: 0 8px 16px rgba(242, 47, 61, 0.22);
189
+ }
190
+
191
+ .search-section {
192
+ margin-top: 12px;
193
+ }
194
+
195
+ .search-section :deep(.van-search) {
196
+ padding: 0;
197
+ background: transparent;
198
+ }
199
+
200
+ .search-section :deep(.van-search__content) {
201
+ background: #ffffff;
202
+ border: 1px solid #eef0f3;
203
+ }
204
+
205
+ .article-list {
206
+ display: grid;
207
+ gap: 10px;
208
+ margin-top: 12px;
209
+ }
210
+
211
+ .state-section {
212
+ display: flex;
213
+ justify-content: center;
214
+ padding-top: 96px;
215
+ }
216
+
217
+ .article-card {
218
+ display: flex;
219
+ align-items: flex-start;
220
+ gap: 10px;
221
+ padding: 15px 14px;
222
+ border: 1px solid #edf0f4;
223
+ border-radius: 8px;
224
+ background: #ffffff;
225
+ box-shadow: 0 5px 16px rgba(23, 27, 32, 0.04);
226
+ cursor: pointer;
227
+ box-sizing: border-box;
228
+ }
229
+
230
+ .article-card:active {
231
+ transform: scale(0.995);
232
+ }
233
+
234
+ .article-card-main {
235
+ flex: 1;
236
+ min-width: 0;
237
+ }
238
+
239
+ .article-card h2 {
240
+ margin: 0;
241
+ color: #171b20;
242
+ font-size: 17px;
243
+ font-weight: 750;
244
+ line-height: 1.35;
245
+ overflow: hidden;
246
+ text-overflow: ellipsis;
247
+ white-space: nowrap;
248
+ }
249
+
250
+ .article-meta {
251
+ display: flex;
252
+ flex-wrap: wrap;
253
+ gap: 6px 12px;
254
+ margin-top: 10px;
255
+ color: #8a929f;
256
+ font-size: 12px;
257
+ line-height: 1.3;
258
+ }
259
+
260
+ .article-meta span {
261
+ display: inline-flex;
262
+ align-items: center;
263
+ gap: 4px;
264
+ min-width: 0;
265
+ }
266
+
267
+ .article-edit-btn {
268
+ display: flex;
269
+ align-items: center;
270
+ justify-content: center;
271
+ width: 34px;
272
+ height: 34px;
273
+ flex: 0 0 34px;
274
+ border: 1px solid #ffe0e3;
275
+ border-radius: 50%;
276
+ color: #f22f3d;
277
+ font-size: 16px;
278
+ background: #fff6f7;
279
+ cursor: pointer;
280
+ }
281
+
282
+ .article-edit-btn:active {
283
+ background: #ffecee;
284
+ }
285
+
286
+ :deep(.van-empty) {
287
+ padding-top: 80px;
288
+ }
289
+
290
+ :deep(.van-empty .van-button) {
291
+ min-width: 122px;
292
+ }
293
+ </style>
@@ -0,0 +1,26 @@
1
+ <template>
2
+ <main class="page">
3
+ <section class="page-body">
4
+ <p class="placeholder">页面建设中…</p>
5
+ </section>
6
+ </main>
7
+ </template>
8
+
9
+ <style scoped>
10
+ .page {
11
+ max-width: 750px;
12
+ min-height: var(--app-page-min-height, 100vh);
13
+ margin: 0 auto;
14
+ padding: 0 11px;
15
+ background: #f7f8fa;
16
+ }
17
+ .page-body {
18
+ padding: 24px 0;
19
+ }
20
+ .placeholder {
21
+ text-align: center;
22
+ color: #969799;
23
+ font-size: 14px;
24
+ padding-top: 120px;
25
+ }
26
+ </style>
@@ -0,0 +1,18 @@
1
+ <script setup lang="ts">
2
+ defineOptions({ name: 'DashboardPage' })
3
+ </script>
4
+
5
+ <template>
6
+ <main class="dashboard-page"></main>
7
+ </template>
8
+
9
+ <style scoped>
10
+ .dashboard-page {
11
+ width: 100%;
12
+ max-width: 750px;
13
+ min-height: var(--app-page-min-height, 100vh);
14
+ margin: 0 auto;
15
+ background: #f7f8fa;
16
+ box-sizing: border-box;
17
+ }
18
+ </style>
@@ -0,0 +1,26 @@
1
+ <template>
2
+ <main class="page">
3
+ <section class="page-body">
4
+ <p class="placeholder">页面建设中…</p>
5
+ </section>
6
+ </main>
7
+ </template>
8
+
9
+ <style scoped>
10
+ .page {
11
+ max-width: 750px;
12
+ min-height: var(--app-page-min-height, 100vh);
13
+ margin: 0 auto;
14
+ padding: 0 11px;
15
+ background: #f7f8fa;
16
+ }
17
+ .page-body {
18
+ padding: 24px 0;
19
+ }
20
+ .placeholder {
21
+ text-align: center;
22
+ color: #969799;
23
+ font-size: 14px;
24
+ padding-top: 120px;
25
+ }
26
+ </style>
@@ -0,0 +1,213 @@
1
+ <script setup lang="ts" name="HomePage">
2
+ import { healthCheckApi } from '@/api/health'
3
+
4
+ type HomeFeature = {
5
+ title: string[]
6
+ image: string
7
+ route: string
8
+ }
9
+
10
+ defineOptions({ name: 'HomePage' })
11
+
12
+ const bannerImage = `${import.meta.env.BASE_URL}images/img.png`
13
+
14
+ const router = useRouter()
15
+
16
+ onMounted(() => {
17
+ healthCheckApi()
18
+ .then((res) => {
19
+ console.log('[首页] 健康检查成功', res)
20
+ })
21
+ .catch((error) => {
22
+ console.error('[首页] 健康检查失败', error)
23
+ })
24
+ })
25
+
26
+ const features: HomeFeature[] = [
27
+ {
28
+ title: ['人民币头寸', '预报及查询'],
29
+ image: `${import.meta.env.BASE_URL}images/f5876785-b927-4347-ba19-999114240649.png`,
30
+ route: '/rmb-position',
31
+ },
32
+ {
33
+ title: ['外币头寸', '预报及查询'],
34
+ image: `${import.meta.env.BASE_URL}images/12a73787-86a9-4891-a65f-66104746f6a8.png`,
35
+ route: '/foreign-position',
36
+ },
37
+ {
38
+ title: ['人行头寸', '余额查询'],
39
+ image: `${import.meta.env.BASE_URL}images/73fef1e4-0fd0-4a1a-9b8b-a70a5b6acbbc.png`,
40
+ route: '/pbc-position',
41
+ },
42
+ {
43
+ title: ['头寸匡算'],
44
+ image: `${import.meta.env.BASE_URL}images/ea745a10-42aa-4f44-8d7f-3ab02cc0adcd.png`,
45
+ route: '/position-estimate',
46
+ },
47
+ {
48
+ title: ['人民币净借记', '可用额度管理'],
49
+ image: `${import.meta.env.BASE_URL}images/5798d7aa-ba8b-4605-8079-58b35495ac55.png`,
50
+ route: '/net-debit',
51
+ },
52
+ {
53
+ title: ['人民币小额网银', '清算场次明细'],
54
+ image: `${import.meta.env.BASE_URL}images/c3dbbd9d-be56-490e-b9f4-6ee17ebefffc.png`,
55
+ route: '/clearing-detail',
56
+ },
57
+ {
58
+ title: ['预警信息'],
59
+ image: `${import.meta.env.BASE_URL}images/bc685b4c-0cca-4a79-924c-a8ee10e6f8eb.png`,
60
+ route: '/warning',
61
+ },
62
+ ]
63
+ </script>
64
+
65
+ <template>
66
+ <main class="home-page">
67
+ <section class="banner-section" aria-label="欢迎登录头寸管理系统">
68
+ <img class="banner-image" :src="bannerImage" alt="欢迎登录头寸管理系统" />
69
+ </section>
70
+
71
+ <section class="feature-section">
72
+ <div class="section-title">
73
+ <span class="section-title-mark"></span>
74
+ <h2>常用功能</h2>
75
+ </div>
76
+
77
+ <div class="feature-grid">
78
+ <button
79
+ v-for="item in features"
80
+ :key="item.title.join('')"
81
+ class="feature-card"
82
+ type="button"
83
+ @click="router.push(item.route)"
84
+ >
85
+ <span class="feature-title">
86
+ <span v-for="line in item.title" :key="line">{{ line }}</span>
87
+ </span>
88
+ <span class="feature-arrow" aria-hidden="true"><van-icon name="arrow" /></span>
89
+ <img class="feature-image" :src="item.image" :alt="item.title.join('')" />
90
+ </button>
91
+ </div>
92
+ </section>
93
+ </main>
94
+ </template>
95
+
96
+ <style scoped>
97
+ .home-page {
98
+ width: 100%;
99
+ max-width: 750px;
100
+ min-height: var(--app-page-min-height, 100vh);
101
+ margin: 0 auto;
102
+ padding: 7px 11px 16px;
103
+ color: #171b20;
104
+ background:
105
+ radial-gradient(circle at 50% 12%, rgba(255, 245, 245, 0.88), transparent 124px),
106
+ #ffffff;
107
+ box-sizing: border-box;
108
+ }
109
+
110
+ .feature-card {
111
+ border: 0;
112
+ padding: 0;
113
+ font: inherit;
114
+ background: transparent;
115
+ appearance: none;
116
+ }
117
+
118
+ .banner-image {
119
+ display: block;
120
+ width: 100%;
121
+ height: auto;
122
+ border-radius: 9px;
123
+ }
124
+
125
+ .feature-section {
126
+ margin-top: 18px;
127
+ }
128
+
129
+ .section-title {
130
+ display: flex;
131
+ align-items: center;
132
+ gap: 8px;
133
+ height: 28px;
134
+ margin-bottom: 11px;
135
+ padding-left: 2px;
136
+ }
137
+
138
+ .section-title-mark {
139
+ width: 5px;
140
+ height: 16px;
141
+ border-radius: 8px;
142
+ background: linear-gradient(180deg, #ff1826 0%, #ff4854 100%);
143
+ }
144
+
145
+ .section-title h2 {
146
+ margin: 0;
147
+ color: #171b20;
148
+ font-size: 16px;
149
+ font-weight: 700;
150
+ line-height: 1;
151
+ }
152
+
153
+ .feature-grid {
154
+ display: grid;
155
+ grid-template-columns: repeat(2, minmax(0, 1fr));
156
+ gap: 8px;
157
+ }
158
+
159
+ .feature-card {
160
+ position: relative;
161
+ height: 104px;
162
+ overflow: hidden;
163
+ text-align: left;
164
+ border: 1px solid #ffdfe1;
165
+ border-radius: 9px;
166
+ background:
167
+ radial-gradient(circle at 78% 74%, rgba(255, 225, 225, 0.52), transparent 58px),
168
+ linear-gradient(135deg, #fff4f4 0%, #ffffff 52%, #fffafa 100%);
169
+ box-shadow: 0 4px 14px rgba(250, 70, 78, 0.035);
170
+ }
171
+
172
+ .feature-title {
173
+ position: absolute;
174
+ top: 16px;
175
+ left: 12px;
176
+ z-index: 1;
177
+ display: flex;
178
+ flex-direction: column;
179
+ width: 104px;
180
+ padding: 0;
181
+ color: #15191d;
182
+ font-size: 15px;
183
+ font-weight: 700;
184
+ line-height: 1.42;
185
+ word-break: keep-all;
186
+ }
187
+
188
+ .feature-arrow {
189
+ position: absolute;
190
+ left: 12px;
191
+ bottom: 20px;
192
+ z-index: 2;
193
+ display: flex;
194
+ align-items: center;
195
+ justify-content: center;
196
+ width: 18px;
197
+ height: 18px;
198
+ border-radius: 50%;
199
+ background: linear-gradient(135deg, #ff3443 0%, #ff5b62 100%);
200
+ color: #ffffff;
201
+ font-size: 11px;
202
+ }
203
+
204
+ .feature-image {
205
+ position: absolute;
206
+ right: -4px;
207
+ bottom: -7px;
208
+ width: 94px;
209
+ height: 94px;
210
+ object-fit: contain;
211
+ pointer-events: none;
212
+ }
213
+ </style>