minitest2.0 0.0.5 → 0.0.7
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.
- package/package.json +1 -1
- package/public/images/dashboard/01-rmb-great-wall-bg.jpg +0 -0
- package/public/images/dashboard/02-city-clearing-skyline-bg.jpg +0 -0
- package/public/images/dashboard/03-cross-border-rmb-world-map-bg.jpg +0 -0
- package/public/images/dashboard/04-digital-currency-fintech-bg.jpg +0 -0
- package/public/images/dashboard/05-usd-liberty-bg.jpg +0 -0
- package/public/images/dashboard/06-eur-europe-architecture-bg.jpg +0 -0
- package/public/images/dashboard/07-hkd-hong-kong-skyline-bg.jpg +0 -0
- package/public/images/dashboard/08-jpy-mount-fuji-bg.jpg +0 -0
- package/src/api/announcement.ts +153 -0
- package/src/api/foreign-position.ts +213 -0
- package/src/router/index.ts +14 -2
- package/src/stores/user.ts +1 -1
- package/src/utils/foreign-forecast.ts +134 -0
- package/src/views/article-detail/index.vue +44 -1
- package/src/views/article-edit/index.vue +9 -0
- package/src/views/dashboard/index.vue +410 -2
- package/src/views/foreign-position/index.vue +1208 -11
- package/src/views/foreign-position-create/index.vue +363 -0
- package/src/views/foreign-position-detail/index.vue +390 -0
- package/src/views/opening-reserve-estimate/index.vue +135 -44
- package/src/views/warning/index.vue +566 -12
- package/vite.config.ts +3 -1
|
@@ -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>
|