minitest2.0 0.0.6 → 0.0.8

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 (32) hide show
  1. package/package.json +1 -1
  2. package/public/images/dashboard/01-rmb-great-wall-bg.jpg +0 -0
  3. package/public/images/dashboard/02-city-clearing-skyline-bg.jpg +0 -0
  4. package/public/images/dashboard/03-cross-border-rmb-world-map-bg.jpg +0 -0
  5. package/public/images/dashboard/04-digital-currency-fintech-bg.jpg +0 -0
  6. package/public/images/dashboard/05-usd-liberty-bg.jpg +0 -0
  7. package/public/images/dashboard/06-eur-europe-architecture-bg.jpg +0 -0
  8. package/public/images/dashboard/07-hkd-hong-kong-skyline-bg.jpg +0 -0
  9. package/public/images/dashboard/08-jpy-mount-fuji-bg.jpg +0 -0
  10. package/src/api/dashboard.ts +44 -0
  11. package/src/api/foreign-position.ts +71 -0
  12. package/src/router/index.ts +29 -17
  13. package/src/utils/foreign-forecast.ts +37 -5
  14. package/src/views/{article-detail → announcement/detail}/index.vue +35 -1
  15. package/src/views/dashboard/index.vue +535 -2
  16. package/src/views/foreign/position/create/index.vue +363 -0
  17. package/src/views/foreign/position/detail/index.vue +390 -0
  18. package/src/views/foreign/position/index.vue +1223 -0
  19. package/src/views/foreign-position/index.vue +0 -26
  20. /package/src/views/{article-edit → announcement/edit}/index.vue +0 -0
  21. /package/src/views/{articles → announcement}/index.vue +0 -0
  22. /package/src/views/{settings → mine/settings}/index.vue +0 -0
  23. /package/src/views/{clearing-detail → rmb/clearing-detail}/index.vue +0 -0
  24. /package/src/views/{net-debit → rmb/net-debit}/index.vue +0 -0
  25. /package/src/views/{pbc-position → rmb/pbc-position}/index.vue +0 -0
  26. /package/src/views/{rmb-position-create → rmb/position/create}/index.vue +0 -0
  27. /package/src/views/{rmb-position-detail → rmb/position/detail}/index.vue +0 -0
  28. /package/src/views/{rmb-position → rmb/position}/index.vue +0 -0
  29. /package/src/views/{position-estimate → rmb/position-estimate}/index.vue +0 -0
  30. /package/src/views/{opening-reserve-estimate → rmb/position-estimate/opening-reserve}/index.vue +0 -0
  31. /package/src/views/{position-estimate-report → rmb/position-estimate/report}/index.vue +0 -0
  32. /package/src/views/{warning → rmb/warning}/index.vue +0 -0
@@ -0,0 +1,363 @@
1
+ <script setup lang="ts">
2
+ import { useAppStore } from '@/stores/app'
3
+ import {
4
+ defaultForeignBusinessOption,
5
+ foreignBusinessOptions,
6
+ foreignCurrencyOptions,
7
+ resolveForeignCurrency,
8
+ } from '@/utils/foreign-forecast'
9
+ import { showAppToast } from '@/utils/toast'
10
+
11
+ defineOptions({ name: 'ForeignPositionCreatePage' })
12
+
13
+ type PendingForecast = {
14
+ institution: string
15
+ date: string
16
+ currency: string
17
+ business: string
18
+ counterparty: string
19
+ amount: number
20
+ }
21
+
22
+ type SelectorType = 'currency' | 'business'
23
+
24
+ const route = useRoute()
25
+ const router = useRouter()
26
+ const appStore = useAppStore()
27
+ const pendingForecastKey = 'foreign-position:pending-create'
28
+
29
+ const form = reactive({
30
+ tranDate: getInitialTranDate(),
31
+ institution: '北京银行总行核算中心',
32
+ currency: 'USD',
33
+ business: '1',
34
+ counterparty: '',
35
+ amount: '',
36
+ })
37
+
38
+ const submitting = ref(false)
39
+ const selectorType = ref<SelectorType>('currency')
40
+ const showSelector = ref(false)
41
+
42
+ const selectedCurrency = computed(() => resolveForeignCurrency(form.currency))
43
+ const selectedBusiness = computed(
44
+ () =>
45
+ foreignBusinessOptions.find((item) => item.value === form.business) ||
46
+ defaultForeignBusinessOption,
47
+ )
48
+ const selectorTitle = computed(() =>
49
+ selectorType.value === 'currency' ? '选择币种' : '选择业务类型',
50
+ )
51
+ const selectorOptions = computed(() =>
52
+ selectorType.value === 'currency'
53
+ ? foreignCurrencyOptions.filter((item) => item.value)
54
+ : foreignBusinessOptions.filter((item) => item.value),
55
+ )
56
+
57
+ function getInitialTranDate() {
58
+ const tranDate = route.query.tranDate
59
+
60
+ if (typeof tranDate === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(tranDate)) {
61
+ return tranDate
62
+ }
63
+
64
+ return appStore.systemWorkDate
65
+ }
66
+
67
+ function parseAmount(value: string) {
68
+ if (!value.trim()) return undefined
69
+
70
+ const amount = Number(value)
71
+ return Number.isFinite(amount) ? amount : undefined
72
+ }
73
+
74
+ function openSelector(type: SelectorType) {
75
+ selectorType.value = type
76
+ showSelector.value = true
77
+ }
78
+
79
+ function chooseSelectorOption(value: string) {
80
+ if (selectorType.value === 'currency') {
81
+ form.currency = value
82
+ } else {
83
+ form.business = value
84
+ }
85
+
86
+ showSelector.value = false
87
+ }
88
+
89
+ function submitCreate() {
90
+ if (submitting.value) return
91
+
92
+ const amount = parseAmount(form.amount)
93
+
94
+ if (!form.tranDate) {
95
+ showAppToast('请选择交易日期')
96
+ return
97
+ }
98
+
99
+ if (!form.institution.trim() || !form.business || !form.currency) {
100
+ showAppToast('请补充落地机构、币种和业务类型')
101
+ return
102
+ }
103
+
104
+ if (form.amount && amount === undefined) {
105
+ showAppToast('请输入正确的预报金额')
106
+ return
107
+ }
108
+
109
+ submitting.value = true
110
+
111
+ const pendingForecast: PendingForecast = {
112
+ institution: form.institution.trim(),
113
+ date: form.tranDate,
114
+ currency: form.currency,
115
+ business: selectedBusiness.value.label,
116
+ counterparty: form.counterparty.trim() || '未填写交易对手',
117
+ amount: amount ?? 0,
118
+ }
119
+
120
+ sessionStorage.setItem(pendingForecastKey, JSON.stringify(pendingForecast))
121
+ showAppToast('新增外币预报成功')
122
+ router.replace({ name: 'ForeignPosition' })
123
+ }
124
+ </script>
125
+
126
+ <template>
127
+ <main class="create-page">
128
+ <section class="form-card" aria-label="新增外币预报表单">
129
+ <van-cell-group inset class="forecast-form">
130
+ <van-field
131
+ v-model="form.tranDate"
132
+ label="交易日期"
133
+ type="date"
134
+ inputmode="numeric"
135
+ placeholder="请选择交易日期"
136
+ />
137
+ <van-field v-model.trim="form.institution" label="落地机构" placeholder="请输入落地机构" />
138
+ <van-field
139
+ :model-value="selectedCurrency.label"
140
+ label="币种"
141
+ readonly
142
+ is-link
143
+ placeholder="请选择币种"
144
+ @click="openSelector('currency')"
145
+ />
146
+ <van-field
147
+ :model-value="selectedBusiness.label"
148
+ label="业务类型"
149
+ readonly
150
+ is-link
151
+ placeholder="请选择业务类型"
152
+ @click="openSelector('business')"
153
+ />
154
+ <van-field
155
+ v-model.trim="form.counterparty"
156
+ label="交易对手"
157
+ placeholder="请输入交易对手或清算行"
158
+ />
159
+ <van-field
160
+ v-model.trim="form.amount"
161
+ label="预报金额"
162
+ type="number"
163
+ inputmode="decimal"
164
+ placeholder="请输入金额"
165
+ >
166
+ <template #right-icon>{{ selectedCurrency.unit }}</template>
167
+ </van-field>
168
+ </van-cell-group>
169
+ </section>
170
+
171
+ <footer class="action-bar">
172
+ <button class="cancel-btn" type="button" @click="router.back()">取消</button>
173
+ <button class="submit-btn" type="button" :disabled="submitting" @click="submitCreate">
174
+ 确认新增
175
+ </button>
176
+ </footer>
177
+
178
+ <van-popup v-model:show="showSelector" position="bottom" round safe-area-inset-bottom>
179
+ <section class="selector-panel">
180
+ <header class="selector-header">
181
+ <button type="button" @click="showSelector = false">取消</button>
182
+ <strong>{{ selectorTitle }}</strong>
183
+ <span></span>
184
+ </header>
185
+ <button
186
+ v-for="item in selectorOptions"
187
+ :key="item.value"
188
+ class="selector-option"
189
+ type="button"
190
+ @click="chooseSelectorOption(item.value)"
191
+ >
192
+ <span>{{ item.label }}</span>
193
+ <van-icon
194
+ v-if="
195
+ (selectorType === 'currency' && item.value === form.currency) ||
196
+ (selectorType === 'business' && item.value === form.business)
197
+ "
198
+ name="success"
199
+ />
200
+ </button>
201
+ </section>
202
+ </van-popup>
203
+ </main>
204
+ </template>
205
+
206
+ <style scoped>
207
+ .create-page {
208
+ width: 100%;
209
+ max-width: 750px;
210
+ min-height: var(--app-page-min-height, 100vh);
211
+ margin: 0 auto;
212
+ padding: 13px 13px calc(env(safe-area-inset-bottom, 0px) + 88px);
213
+ color: #080d1f;
214
+ background:
215
+ radial-gradient(circle at 50% 0%, rgba(255, 242, 243, 0.92), transparent 118px),
216
+ linear-gradient(180deg, #ffffff 0%, #fbfcfd 54%, #ffffff 100%);
217
+ box-sizing: border-box;
218
+ }
219
+
220
+ button {
221
+ border: 0;
222
+ padding: 0;
223
+ font: inherit;
224
+ background: transparent;
225
+ appearance: none;
226
+ }
227
+
228
+ .form-card {
229
+ margin-top: 15px;
230
+ border-radius: 13px;
231
+ padding: 8px 0;
232
+ background: rgba(255, 255, 255, 0.96);
233
+ box-shadow: 0 7px 22px rgba(21, 28, 45, 0.055);
234
+ }
235
+
236
+ .forecast-form {
237
+ margin: 0;
238
+ overflow: hidden;
239
+ border-radius: 13px;
240
+ background: transparent;
241
+ }
242
+
243
+ .forecast-form :deep(.van-cell) {
244
+ min-height: 56px;
245
+ align-items: center;
246
+ padding: 10px 14px;
247
+ background: transparent;
248
+ }
249
+
250
+ .forecast-form :deep(.van-field__label) {
251
+ width: 72px;
252
+ color: #111728;
253
+ font-size: 14px;
254
+ font-weight: 650;
255
+ }
256
+
257
+ .forecast-form :deep(.van-field__control) {
258
+ color: #202638;
259
+ font-size: 14px;
260
+ text-align: right;
261
+ }
262
+
263
+ .forecast-form :deep(.van-field__right-icon) {
264
+ min-width: 52px;
265
+ color: #7f8798;
266
+ font-size: 12px;
267
+ text-align: right;
268
+ }
269
+
270
+ .action-bar {
271
+ position: fixed;
272
+ right: max(13px, calc((100vw - 750px) / 2 + 13px));
273
+ bottom: calc(env(safe-area-inset-bottom, 0px) + 14px);
274
+ left: max(13px, calc((100vw - 750px) / 2 + 13px));
275
+ z-index: 20;
276
+ display: grid;
277
+ grid-template-columns: 96px 1fr;
278
+ gap: 10px;
279
+ }
280
+
281
+ .cancel-btn,
282
+ .submit-btn {
283
+ display: flex;
284
+ align-items: center;
285
+ justify-content: center;
286
+ height: 44px;
287
+ border-radius: 22px;
288
+ font-size: 15px;
289
+ font-weight: 650;
290
+ }
291
+
292
+ .cancel-btn {
293
+ border: 1px solid #e5e8ef;
294
+ color: #303747;
295
+ background: rgba(255, 255, 255, 0.94);
296
+ }
297
+
298
+ .submit-btn {
299
+ color: #ffffff;
300
+ background: linear-gradient(135deg, #ff3340 0%, #ff1d34 100%);
301
+ box-shadow: 0 9px 18px rgba(255, 36, 52, 0.18);
302
+ }
303
+
304
+ .submit-btn:disabled {
305
+ opacity: 0.68;
306
+ }
307
+
308
+ .selector-panel {
309
+ padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 8px);
310
+ background: #ffffff;
311
+ }
312
+
313
+ .selector-header {
314
+ display: grid;
315
+ grid-template-columns: 56px minmax(0, 1fr) 56px;
316
+ align-items: center;
317
+ height: 48px;
318
+ border-bottom: 1px solid #eef0f4;
319
+ padding: 0 12px;
320
+ }
321
+
322
+ .selector-header button {
323
+ color: #6f7788;
324
+ font-size: 14px;
325
+ text-align: left;
326
+ }
327
+
328
+ .selector-header strong {
329
+ color: #111728;
330
+ font-size: 15px;
331
+ font-weight: 700;
332
+ text-align: center;
333
+ }
334
+
335
+ .selector-option {
336
+ display: flex;
337
+ align-items: center;
338
+ justify-content: space-between;
339
+ width: 100%;
340
+ min-height: 48px;
341
+ border-bottom: 1px solid #f1f3f6;
342
+ padding: 0 18px;
343
+ color: #151827;
344
+ font-size: 14px;
345
+ text-align: left;
346
+ box-sizing: border-box;
347
+ }
348
+
349
+ .selector-option .van-icon {
350
+ color: #f42b3a;
351
+ font-size: 18px;
352
+ }
353
+
354
+ @media (max-width: 340px) {
355
+ .forecast-form :deep(.van-field__label) {
356
+ width: 64px;
357
+ }
358
+
359
+ .action-bar {
360
+ grid-template-columns: 82px 1fr;
361
+ }
362
+ }
363
+ </style>