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
|
@@ -1,26 +1,1223 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import {
|
|
3
|
+
buildForeignForecastQueryPayload,
|
|
4
|
+
queryForeignForecastPage,
|
|
5
|
+
type ForeignForecastQueryForm,
|
|
6
|
+
type ForeignForecastRecord,
|
|
7
|
+
} from '@/api/foreign-position'
|
|
8
|
+
import { useAppStore } from '@/stores/app'
|
|
9
|
+
import {
|
|
10
|
+
allForeignBusinessOption,
|
|
11
|
+
allForeignStatusOption,
|
|
12
|
+
foreignBusinessOptions,
|
|
13
|
+
foreignCurrencyOptions,
|
|
14
|
+
foreignStatusOptions,
|
|
15
|
+
formatForeignForecastAmount,
|
|
16
|
+
formatForeignForecastDate,
|
|
17
|
+
resolveForeignBusinessType,
|
|
18
|
+
resolveForeignCurrency,
|
|
19
|
+
resolveForeignStatus,
|
|
20
|
+
toTenThousandAmount,
|
|
21
|
+
type ForeignOption,
|
|
22
|
+
type ForeignStatusOption,
|
|
23
|
+
} from '@/utils/foreign-forecast'
|
|
24
|
+
import { showAppToast } from '@/utils/toast'
|
|
25
|
+
|
|
26
|
+
defineOptions({ name: 'ForeignPositionPage' })
|
|
27
|
+
|
|
28
|
+
type Forecast = {
|
|
29
|
+
id: number
|
|
30
|
+
institution: string
|
|
31
|
+
date: string
|
|
32
|
+
currency: string
|
|
33
|
+
currencyUnit: string
|
|
34
|
+
business: string
|
|
35
|
+
counterparty: string
|
|
36
|
+
amount: number
|
|
37
|
+
predRefNo: string
|
|
38
|
+
status: string
|
|
39
|
+
statusClass: string
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
type PendingForecast = {
|
|
43
|
+
institution: string
|
|
44
|
+
date: string
|
|
45
|
+
currency: string
|
|
46
|
+
business: string
|
|
47
|
+
counterparty: string
|
|
48
|
+
amount: number
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
type SelectorType = 'currency' | 'business' | 'status'
|
|
52
|
+
|
|
53
|
+
type SelectorOption = ForeignOption | ForeignStatusOption
|
|
54
|
+
|
|
55
|
+
const router = useRouter()
|
|
56
|
+
const appStore = useAppStore()
|
|
57
|
+
const defaultTranDate = appStore.systemWorkDate
|
|
58
|
+
const heroImage = `${import.meta.env.BASE_URL}images/12a73787-86a9-4891-a65f-66104746f6a8.png`
|
|
59
|
+
const pageSize = 10
|
|
60
|
+
const backTopThreshold = 520
|
|
61
|
+
const pendingForecastKey = 'foreign-position:pending-create'
|
|
62
|
+
|
|
63
|
+
const queryForm = reactive({
|
|
64
|
+
institution: '',
|
|
65
|
+
currency: '',
|
|
66
|
+
business: '',
|
|
67
|
+
status: '',
|
|
68
|
+
minAmount: '',
|
|
69
|
+
maxAmount: '',
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
const selectedDate = ref(defaultTranDate)
|
|
73
|
+
const showDatePicker = ref(false)
|
|
74
|
+
const datePickerValue = ref(selectedDate.value.split('-'))
|
|
75
|
+
const minDate = new Date(2020, 0, 1)
|
|
76
|
+
const maxDate = new Date(2030, 11, 31)
|
|
77
|
+
const isFilterExpanded = ref(true)
|
|
78
|
+
|
|
79
|
+
const forecastList = ref<Forecast[]>([])
|
|
80
|
+
const forecastTotal = ref(0)
|
|
81
|
+
const currentPage = ref(1)
|
|
82
|
+
const listLoading = ref(false)
|
|
83
|
+
const listFinished = ref(false)
|
|
84
|
+
const listError = ref(false)
|
|
85
|
+
const requesting = ref(false)
|
|
86
|
+
const showBackTop = ref(false)
|
|
87
|
+
const selectorType = ref<SelectorType>('currency')
|
|
88
|
+
const showSelector = ref(false)
|
|
89
|
+
|
|
90
|
+
const selectedCurrency = computed(() => resolveForeignCurrency(queryForm.currency))
|
|
91
|
+
const selectedBusiness = computed(
|
|
92
|
+
() =>
|
|
93
|
+
foreignBusinessOptions.find((item) => item.value === queryForm.business) ||
|
|
94
|
+
allForeignBusinessOption,
|
|
95
|
+
)
|
|
96
|
+
const selectedStatus = computed(
|
|
97
|
+
() =>
|
|
98
|
+
foreignStatusOptions.find((item) => item.value === queryForm.status) || allForeignStatusOption,
|
|
99
|
+
)
|
|
100
|
+
const selectorTitle = computed(() => {
|
|
101
|
+
const titleMap: Record<SelectorType, string> = {
|
|
102
|
+
currency: '选择币种',
|
|
103
|
+
business: '选择业务类型',
|
|
104
|
+
status: '选择预报状态',
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return titleMap[selectorType.value]
|
|
108
|
+
})
|
|
109
|
+
const selectorOptions = computed<SelectorOption[]>(() => {
|
|
110
|
+
if (selectorType.value === 'currency') return foreignCurrencyOptions
|
|
111
|
+
if (selectorType.value === 'business') return foreignBusinessOptions
|
|
112
|
+
|
|
113
|
+
return foreignStatusOptions
|
|
114
|
+
})
|
|
115
|
+
const filterSummary = computed(() => {
|
|
116
|
+
const institution = queryForm.institution.trim() || '全部机构'
|
|
117
|
+
const currency = selectedCurrency.value.shortLabel
|
|
118
|
+
|
|
119
|
+
return `${selectedDate.value} · ${institution} · ${currency}`
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
onMounted(async () => {
|
|
123
|
+
updateBackTopVisibility()
|
|
124
|
+
window.addEventListener('scroll', updateBackTopVisibility, { passive: true })
|
|
125
|
+
await resetAndLoad()
|
|
126
|
+
consumePendingForecast()
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
onUnmounted(() => {
|
|
130
|
+
window.removeEventListener('scroll', updateBackTopVisibility)
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
function mapForeignRecordToForecast(record: ForeignForecastRecord, index: number): Forecast {
|
|
134
|
+
const currencyValue = getRecordCurrency(record)
|
|
135
|
+
const currency = resolveForeignCurrency(currencyValue)
|
|
136
|
+
const status = resolveForeignStatus(record.predStatus)
|
|
137
|
+
const counterparty =
|
|
138
|
+
record.competName || record.clientName || record.bankSwift || record.swiftCode || '-'
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
id: index + 1,
|
|
142
|
+
institution: record.groundBranch || '未填写机构',
|
|
143
|
+
date: formatForeignForecastDate(record.tranDate),
|
|
144
|
+
currency: currency.shortLabel,
|
|
145
|
+
currencyUnit: currency.unit,
|
|
146
|
+
business: resolveForeignBusinessType(record.busiType),
|
|
147
|
+
counterparty,
|
|
148
|
+
amount: toTenThousandAmount(record.predAmt),
|
|
149
|
+
predRefNo: record.predRefNo || '',
|
|
150
|
+
status: status.label,
|
|
151
|
+
statusClass: status.className,
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function buildQueryForm(): ForeignForecastQueryForm {
|
|
156
|
+
return {
|
|
157
|
+
tranDate: selectedDate.value,
|
|
158
|
+
groundBranch: queryForm.institution.trim(),
|
|
159
|
+
currency: queryForm.currency,
|
|
160
|
+
busiType: queryForm.business,
|
|
161
|
+
predStatus: queryForm.status,
|
|
162
|
+
startTranAmt: queryForm.minAmount,
|
|
163
|
+
endTranAmt: queryForm.maxAmount,
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function parseAmount(value: string) {
|
|
168
|
+
if (!value.trim()) return undefined
|
|
169
|
+
|
|
170
|
+
const amount = Number(value)
|
|
171
|
+
return Number.isFinite(amount) ? amount : undefined
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function updateBackTopVisibility() {
|
|
175
|
+
showBackTop.value = window.scrollY > backTopThreshold
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function scrollToTop() {
|
|
179
|
+
showBackTop.value = false
|
|
180
|
+
window.scrollTo({
|
|
181
|
+
top: 0,
|
|
182
|
+
behavior: 'smooth',
|
|
183
|
+
})
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function openCreatePage() {
|
|
187
|
+
router.push({
|
|
188
|
+
name: 'ForeignPositionCreate',
|
|
189
|
+
query: { tranDate: selectedDate.value },
|
|
190
|
+
})
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function openSelector(type: SelectorType) {
|
|
194
|
+
selectorType.value = type
|
|
195
|
+
showSelector.value = true
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function chooseSelectorOption(option: SelectorOption) {
|
|
199
|
+
if (selectorType.value === 'currency') {
|
|
200
|
+
queryForm.currency = option.value
|
|
201
|
+
} else if (selectorType.value === 'business') {
|
|
202
|
+
queryForm.business = option.value
|
|
203
|
+
} else {
|
|
204
|
+
queryForm.status = option.value
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
showSelector.value = false
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async function handleSearch() {
|
|
211
|
+
const minAmount = parseAmount(queryForm.minAmount)
|
|
212
|
+
const maxAmount = parseAmount(queryForm.maxAmount)
|
|
213
|
+
|
|
214
|
+
if (
|
|
215
|
+
(queryForm.minAmount && minAmount === undefined) ||
|
|
216
|
+
(queryForm.maxAmount && maxAmount === undefined)
|
|
217
|
+
) {
|
|
218
|
+
showAppToast('请输入正确的金额')
|
|
219
|
+
return
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (minAmount !== undefined && maxAmount !== undefined && minAmount > maxAmount) {
|
|
223
|
+
showAppToast('下限金额不能大于上限金额')
|
|
224
|
+
return
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
await resetAndLoad()
|
|
228
|
+
|
|
229
|
+
if (!listError.value) {
|
|
230
|
+
showAppToast(`查询到 ${forecastTotal.value} 笔预报`)
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
async function handleReset() {
|
|
235
|
+
selectedDate.value = defaultTranDate
|
|
236
|
+
datePickerValue.value = selectedDate.value.split('-')
|
|
237
|
+
queryForm.institution = ''
|
|
238
|
+
queryForm.currency = ''
|
|
239
|
+
queryForm.business = ''
|
|
240
|
+
queryForm.status = ''
|
|
241
|
+
queryForm.minAmount = ''
|
|
242
|
+
queryForm.maxAmount = ''
|
|
243
|
+
|
|
244
|
+
await resetAndLoad()
|
|
245
|
+
|
|
246
|
+
if (!listError.value) {
|
|
247
|
+
showAppToast('已重置筛选条件')
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async function resetAndLoad() {
|
|
252
|
+
currentPage.value = 1
|
|
253
|
+
forecastList.value = []
|
|
254
|
+
forecastTotal.value = 0
|
|
255
|
+
listFinished.value = false
|
|
256
|
+
listError.value = false
|
|
257
|
+
|
|
258
|
+
await loadNextPage()
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
async function loadNextPage() {
|
|
262
|
+
if (requesting.value || listFinished.value) {
|
|
263
|
+
listLoading.value = false
|
|
264
|
+
return
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
requesting.value = true
|
|
268
|
+
listLoading.value = true
|
|
269
|
+
listError.value = false
|
|
270
|
+
|
|
271
|
+
const payload = buildForeignForecastQueryPayload({
|
|
272
|
+
form: buildQueryForm(),
|
|
273
|
+
pageIndex: currentPage.value,
|
|
274
|
+
pageSize,
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
console.log('[外币预报查询入参]', payload)
|
|
278
|
+
|
|
279
|
+
try {
|
|
280
|
+
const response = await queryForeignForecastPage(payload)
|
|
281
|
+
const records = filterRecordsByCurrency(response.body.resultList)
|
|
282
|
+
const nextList = records.map((item, index) =>
|
|
283
|
+
mapForeignRecordToForecast(item, forecastList.value.length + index),
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
forecastList.value.push(...nextList)
|
|
287
|
+
forecastTotal.value = queryForm.currency
|
|
288
|
+
? forecastList.value.length
|
|
289
|
+
: Number(response.body.totalCount) || forecastList.value.length
|
|
290
|
+
currentPage.value += 1
|
|
291
|
+
listFinished.value = queryForm.currency
|
|
292
|
+
? response.body.resultList.length < pageSize
|
|
293
|
+
: forecastList.value.length >= forecastTotal.value ||
|
|
294
|
+
response.body.resultList.length < pageSize
|
|
295
|
+
} catch (error) {
|
|
296
|
+
console.error('[外币预报查询失败]', error)
|
|
297
|
+
listError.value = true
|
|
298
|
+
showAppToast('数据加载失败,请重试')
|
|
299
|
+
} finally {
|
|
300
|
+
requesting.value = false
|
|
301
|
+
listLoading.value = false
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function filterRecordsByCurrency(records: ForeignForecastRecord[]) {
|
|
306
|
+
const currency = queryForm.currency.trim().toUpperCase()
|
|
307
|
+
|
|
308
|
+
if (!currency) return records
|
|
309
|
+
|
|
310
|
+
return records.filter((item) => getRecordCurrency(item) === currency)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function getRecordCurrency(record: ForeignForecastRecord) {
|
|
314
|
+
return (
|
|
315
|
+
record.ccy ||
|
|
316
|
+
record.currency ||
|
|
317
|
+
record.currencyCode ||
|
|
318
|
+
record.currCode ||
|
|
319
|
+
record.curType ||
|
|
320
|
+
record.tranCcy ||
|
|
321
|
+
''
|
|
322
|
+
)
|
|
323
|
+
.trim()
|
|
324
|
+
.toUpperCase()
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function confirmDate() {
|
|
328
|
+
selectedDate.value = datePickerValue.value.join('-')
|
|
329
|
+
showDatePicker.value = false
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function openDatePicker() {
|
|
333
|
+
datePickerValue.value = selectedDate.value.split('-')
|
|
334
|
+
showDatePicker.value = true
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function openDetail(item: Forecast) {
|
|
338
|
+
if (!item.predRefNo) {
|
|
339
|
+
showAppToast('暂无预报流水号')
|
|
340
|
+
return
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
router.push({
|
|
344
|
+
name: 'ForeignPositionDetail',
|
|
345
|
+
params: { predRefNo: item.predRefNo },
|
|
346
|
+
})
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function consumePendingForecast() {
|
|
350
|
+
const payload = sessionStorage.getItem(pendingForecastKey)
|
|
351
|
+
|
|
352
|
+
if (!payload) return
|
|
353
|
+
|
|
354
|
+
sessionStorage.removeItem(pendingForecastKey)
|
|
355
|
+
|
|
356
|
+
try {
|
|
357
|
+
const forecast = JSON.parse(payload) as Partial<PendingForecast>
|
|
358
|
+
|
|
359
|
+
if (!isPendingForecast(forecast)) return
|
|
360
|
+
|
|
361
|
+
const currency = resolveForeignCurrency(forecast.currency)
|
|
362
|
+
const status = resolveForeignStatus('1')
|
|
363
|
+
|
|
364
|
+
forecastList.value.unshift({
|
|
365
|
+
id: Date.now(),
|
|
366
|
+
institution: forecast.institution,
|
|
367
|
+
date: forecast.date,
|
|
368
|
+
currency: currency.shortLabel,
|
|
369
|
+
currencyUnit: currency.unit,
|
|
370
|
+
business: forecast.business,
|
|
371
|
+
counterparty: forecast.counterparty,
|
|
372
|
+
amount: forecast.amount,
|
|
373
|
+
predRefNo: '',
|
|
374
|
+
status: status.label,
|
|
375
|
+
statusClass: status.className,
|
|
376
|
+
})
|
|
377
|
+
forecastTotal.value += 1
|
|
378
|
+
} catch (error) {
|
|
379
|
+
console.error('[外币预报新增结果解析失败]', error)
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function isPendingForecast(value: Partial<PendingForecast> | null): value is PendingForecast {
|
|
384
|
+
return Boolean(
|
|
385
|
+
value &&
|
|
386
|
+
typeof value.institution === 'string' &&
|
|
387
|
+
typeof value.date === 'string' &&
|
|
388
|
+
typeof value.currency === 'string' &&
|
|
389
|
+
typeof value.business === 'string' &&
|
|
390
|
+
typeof value.counterparty === 'string' &&
|
|
391
|
+
typeof value.amount === 'number' &&
|
|
392
|
+
Number.isFinite(value.amount),
|
|
393
|
+
)
|
|
394
|
+
}
|
|
395
|
+
</script>
|
|
396
|
+
|
|
1
397
|
<template>
|
|
2
|
-
<main class="page">
|
|
3
|
-
<section class="
|
|
4
|
-
<
|
|
398
|
+
<main class="foreign-page">
|
|
399
|
+
<section class="query-card">
|
|
400
|
+
<div class="hero-panel">
|
|
401
|
+
<div class="hero-copy">
|
|
402
|
+
<h2>外币预报信息查询</h2>
|
|
403
|
+
<p>查询外币预报及交易明细</p>
|
|
404
|
+
</div>
|
|
405
|
+
<img class="hero-image" :src="heroImage" alt="外币预报信息查询" />
|
|
406
|
+
</div>
|
|
5
407
|
</section>
|
|
408
|
+
|
|
409
|
+
<section class="filter-sticky">
|
|
410
|
+
<div class="filter-panel" :class="{ 'is-collapsed': !isFilterExpanded }">
|
|
411
|
+
<div class="filter-toolbar">
|
|
412
|
+
<div class="filter-heading">
|
|
413
|
+
<strong>筛选条件</strong>
|
|
414
|
+
<span>{{ filterSummary }}</span>
|
|
415
|
+
</div>
|
|
416
|
+
<button
|
|
417
|
+
class="filter-toggle"
|
|
418
|
+
type="button"
|
|
419
|
+
:aria-expanded="isFilterExpanded"
|
|
420
|
+
@click="isFilterExpanded = !isFilterExpanded"
|
|
421
|
+
>
|
|
422
|
+
<van-icon :name="isFilterExpanded ? 'arrow-up' : 'arrow-down'" />
|
|
423
|
+
<span>{{ isFilterExpanded ? '收起' : '展开' }}</span>
|
|
424
|
+
</button>
|
|
425
|
+
</div>
|
|
426
|
+
|
|
427
|
+
<Transition name="filter-body">
|
|
428
|
+
<div v-show="isFilterExpanded" class="filter-body">
|
|
429
|
+
<button class="filter-row" type="button" @click="openDatePicker">
|
|
430
|
+
<span class="row-icon row-icon-calendar"><van-icon name="calendar-o" /></span>
|
|
431
|
+
<span class="row-label">起息日</span>
|
|
432
|
+
<span class="row-value">{{ selectedDate }}</span>
|
|
433
|
+
<van-icon class="row-calendar" name="calendar-o" />
|
|
434
|
+
<van-icon class="row-arrow" name="arrow" />
|
|
435
|
+
</button>
|
|
436
|
+
|
|
437
|
+
<label class="filter-row">
|
|
438
|
+
<span class="row-icon row-icon-bank"><van-icon name="hotel-o" /></span>
|
|
439
|
+
<span class="row-label">落地机构</span>
|
|
440
|
+
<input
|
|
441
|
+
v-model.trim="queryForm.institution"
|
|
442
|
+
class="row-input"
|
|
443
|
+
type="text"
|
|
444
|
+
inputmode="text"
|
|
445
|
+
placeholder="请输入落地机构关键字"
|
|
446
|
+
/>
|
|
447
|
+
<van-icon class="row-arrow" name="arrow" />
|
|
448
|
+
</label>
|
|
449
|
+
|
|
450
|
+
<button class="filter-row" type="button" @click="openSelector('currency')">
|
|
451
|
+
<span class="row-icon row-icon-money"><van-icon name="gold-coin-o" /></span>
|
|
452
|
+
<span class="row-label">币种</span>
|
|
453
|
+
<span class="row-value muted-value">{{ selectedCurrency.label }}</span>
|
|
454
|
+
<van-icon class="row-arrow" name="arrow" />
|
|
455
|
+
</button>
|
|
456
|
+
|
|
457
|
+
<button class="filter-row" type="button" @click="openSelector('business')">
|
|
458
|
+
<span class="row-icon row-icon-briefcase"><van-icon name="bag-o" /></span>
|
|
459
|
+
<span class="row-label">业务类型</span>
|
|
460
|
+
<span class="row-value muted-value">{{ selectedBusiness.label }}</span>
|
|
461
|
+
<van-icon class="row-arrow" name="arrow" />
|
|
462
|
+
</button>
|
|
463
|
+
|
|
464
|
+
<button class="filter-row" type="button" @click="openSelector('status')">
|
|
465
|
+
<span class="row-icon row-icon-status"><van-icon name="orders-o" /></span>
|
|
466
|
+
<span class="row-label">预报状态</span>
|
|
467
|
+
<span class="row-value muted-value">{{ selectedStatus.label }}</span>
|
|
468
|
+
<van-icon class="row-arrow" name="arrow" />
|
|
469
|
+
</button>
|
|
470
|
+
|
|
471
|
+
<div class="filter-row amount-row">
|
|
472
|
+
<span class="row-icon row-icon-money"><van-icon name="gold-coin-o" /></span>
|
|
473
|
+
<span class="row-label">金额区间</span>
|
|
474
|
+
<input
|
|
475
|
+
v-model.trim="queryForm.minAmount"
|
|
476
|
+
class="amount-input"
|
|
477
|
+
type="number"
|
|
478
|
+
inputmode="decimal"
|
|
479
|
+
placeholder="请输入下限金额"
|
|
480
|
+
/>
|
|
481
|
+
<span class="amount-divider">-</span>
|
|
482
|
+
<input
|
|
483
|
+
v-model.trim="queryForm.maxAmount"
|
|
484
|
+
class="amount-input"
|
|
485
|
+
type="number"
|
|
486
|
+
inputmode="decimal"
|
|
487
|
+
placeholder="请输入上限金额"
|
|
488
|
+
/>
|
|
489
|
+
</div>
|
|
490
|
+
|
|
491
|
+
<div class="filter-actions">
|
|
492
|
+
<button class="reset-btn" type="button" @click="handleReset">
|
|
493
|
+
<van-icon name="replay" />
|
|
494
|
+
<span>重置</span>
|
|
495
|
+
</button>
|
|
496
|
+
<button class="search-btn" type="button" @click="handleSearch">
|
|
497
|
+
<van-icon name="search" />
|
|
498
|
+
<span>查询</span>
|
|
499
|
+
</button>
|
|
500
|
+
</div>
|
|
501
|
+
</div>
|
|
502
|
+
</Transition>
|
|
503
|
+
</div>
|
|
504
|
+
</section>
|
|
505
|
+
|
|
506
|
+
<section class="forecast-card" aria-label="预报列表">
|
|
507
|
+
<van-list
|
|
508
|
+
v-model:loading="listLoading"
|
|
509
|
+
v-model:error="listError"
|
|
510
|
+
:finished="listFinished"
|
|
511
|
+
:immediate-check="false"
|
|
512
|
+
error-text="加载失败,点击重试"
|
|
513
|
+
finished-text="没有更多了"
|
|
514
|
+
loading-text="加载中..."
|
|
515
|
+
@load="loadNextPage"
|
|
516
|
+
>
|
|
517
|
+
<button
|
|
518
|
+
v-for="item in forecastList"
|
|
519
|
+
:key="item.id"
|
|
520
|
+
class="forecast-item"
|
|
521
|
+
type="button"
|
|
522
|
+
@click="openDetail(item)"
|
|
523
|
+
>
|
|
524
|
+
<span class="forecast-avatar"><van-icon name="shop-o" /></span>
|
|
525
|
+
<span class="forecast-main">
|
|
526
|
+
<strong>{{ item.institution }}</strong>
|
|
527
|
+
<span class="forecast-date">{{ item.date }}</span>
|
|
528
|
+
<span class="forecast-business">
|
|
529
|
+
{{ item.business }} | {{ item.currency }} | {{ item.counterparty }}
|
|
530
|
+
</span>
|
|
531
|
+
</span>
|
|
532
|
+
<span class="forecast-side">
|
|
533
|
+
<span class="forecast-amount">
|
|
534
|
+
{{ formatForeignForecastAmount(item.amount) }} <em>{{ item.currencyUnit }}</em>
|
|
535
|
+
</span>
|
|
536
|
+
<span class="status-tag" :class="item.statusClass">{{ item.status }}</span>
|
|
537
|
+
</span>
|
|
538
|
+
<van-icon class="forecast-arrow" name="arrow" />
|
|
539
|
+
</button>
|
|
540
|
+
|
|
541
|
+
<van-empty
|
|
542
|
+
v-if="!listLoading && forecastList.length === 0"
|
|
543
|
+
image-size="72"
|
|
544
|
+
description="暂无外币预报信息"
|
|
545
|
+
/>
|
|
546
|
+
</van-list>
|
|
547
|
+
</section>
|
|
548
|
+
|
|
549
|
+
<button class="fab" type="button" aria-label="新增外币预报" @click="openCreatePage">
|
|
550
|
+
<van-icon name="plus" />
|
|
551
|
+
</button>
|
|
552
|
+
|
|
553
|
+
<Transition name="back-top">
|
|
554
|
+
<button
|
|
555
|
+
v-show="showBackTop"
|
|
556
|
+
class="back-top-btn"
|
|
557
|
+
type="button"
|
|
558
|
+
aria-label="返回顶部"
|
|
559
|
+
@click="scrollToTop"
|
|
560
|
+
>
|
|
561
|
+
<van-icon name="arrow-up" />
|
|
562
|
+
</button>
|
|
563
|
+
</Transition>
|
|
564
|
+
|
|
565
|
+
<van-popup v-model:show="showDatePicker" position="bottom" round safe-area-inset-bottom>
|
|
566
|
+
<van-date-picker
|
|
567
|
+
v-model="datePickerValue"
|
|
568
|
+
title="选择起息日"
|
|
569
|
+
:min-date="minDate"
|
|
570
|
+
:max-date="maxDate"
|
|
571
|
+
@cancel="showDatePicker = false"
|
|
572
|
+
@confirm="confirmDate"
|
|
573
|
+
/>
|
|
574
|
+
</van-popup>
|
|
575
|
+
|
|
576
|
+
<van-popup v-model:show="showSelector" position="bottom" round safe-area-inset-bottom>
|
|
577
|
+
<section class="selector-panel">
|
|
578
|
+
<header class="selector-header">
|
|
579
|
+
<button type="button" @click="showSelector = false">取消</button>
|
|
580
|
+
<strong>{{ selectorTitle }}</strong>
|
|
581
|
+
<span></span>
|
|
582
|
+
</header>
|
|
583
|
+
<button
|
|
584
|
+
v-for="item in selectorOptions"
|
|
585
|
+
:key="item.value || item.label"
|
|
586
|
+
class="selector-option"
|
|
587
|
+
type="button"
|
|
588
|
+
@click="chooseSelectorOption(item)"
|
|
589
|
+
>
|
|
590
|
+
<span>{{ item.label }}</span>
|
|
591
|
+
<van-icon
|
|
592
|
+
v-if="
|
|
593
|
+
(selectorType === 'currency' && item.value === queryForm.currency) ||
|
|
594
|
+
(selectorType === 'business' && item.value === queryForm.business) ||
|
|
595
|
+
(selectorType === 'status' && item.value === queryForm.status)
|
|
596
|
+
"
|
|
597
|
+
name="success"
|
|
598
|
+
/>
|
|
599
|
+
</button>
|
|
600
|
+
</section>
|
|
601
|
+
</van-popup>
|
|
6
602
|
</main>
|
|
7
603
|
</template>
|
|
8
604
|
|
|
9
605
|
<style scoped>
|
|
10
|
-
.page {
|
|
606
|
+
.foreign-page {
|
|
607
|
+
position: relative;
|
|
608
|
+
width: 100%;
|
|
11
609
|
max-width: 750px;
|
|
12
610
|
min-height: var(--app-page-min-height, 100vh);
|
|
13
611
|
margin: 0 auto;
|
|
14
|
-
padding:
|
|
15
|
-
|
|
612
|
+
padding: 13px 13px calc(env(safe-area-inset-bottom, 0px) + 118px);
|
|
613
|
+
color: #080d1f;
|
|
614
|
+
background:
|
|
615
|
+
radial-gradient(circle at 50% 0%, rgba(255, 242, 243, 0.92), transparent 118px),
|
|
616
|
+
linear-gradient(180deg, #ffffff 0%, #fbfcfd 54%, #ffffff 100%);
|
|
617
|
+
box-sizing: border-box;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
button,
|
|
621
|
+
input {
|
|
622
|
+
font: inherit;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
button {
|
|
626
|
+
border: 0;
|
|
627
|
+
padding: 0;
|
|
628
|
+
background: transparent;
|
|
629
|
+
appearance: none;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
.query-card {
|
|
633
|
+
overflow: hidden;
|
|
634
|
+
border-radius: 13px;
|
|
635
|
+
box-shadow: 0 10px 24px rgba(31, 41, 55, 0.06);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
.hero-panel {
|
|
639
|
+
position: relative;
|
|
640
|
+
height: 143px;
|
|
641
|
+
overflow: hidden;
|
|
642
|
+
border-radius: 13px 13px 0 0;
|
|
643
|
+
background:
|
|
644
|
+
radial-gradient(circle at 72% 80%, rgba(255, 255, 255, 0.16), transparent 74px),
|
|
645
|
+
radial-gradient(circle at 92% 14%, rgba(255, 255, 255, 0.13), transparent 58px),
|
|
646
|
+
linear-gradient(135deg, #ff2736 0%, #ff3a42 42%, #ff454b 100%);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
.hero-panel::after {
|
|
650
|
+
position: absolute;
|
|
651
|
+
right: 15px;
|
|
652
|
+
bottom: 14px;
|
|
653
|
+
width: 26px;
|
|
654
|
+
height: 64px;
|
|
655
|
+
border-radius: 3px;
|
|
656
|
+
background: rgba(255, 137, 137, 0.34);
|
|
657
|
+
box-shadow: -21px 14px 0 rgba(255, 137, 137, 0.28);
|
|
658
|
+
content: '';
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
.hero-copy {
|
|
662
|
+
position: relative;
|
|
663
|
+
z-index: 2;
|
|
664
|
+
padding: 32px 0 0 15px;
|
|
665
|
+
color: #ffffff;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
.hero-copy h2 {
|
|
669
|
+
margin: 0;
|
|
670
|
+
font-size: 22px;
|
|
671
|
+
font-weight: 800;
|
|
672
|
+
line-height: 1.2;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
.hero-copy p {
|
|
676
|
+
margin: 18px 0 0;
|
|
677
|
+
font-size: 13px;
|
|
678
|
+
font-weight: 600;
|
|
679
|
+
line-height: 1.2;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
.hero-image {
|
|
683
|
+
position: absolute;
|
|
684
|
+
right: 9px;
|
|
685
|
+
bottom: -8px;
|
|
686
|
+
z-index: 1;
|
|
687
|
+
width: 132px;
|
|
688
|
+
height: 132px;
|
|
689
|
+
object-fit: contain;
|
|
690
|
+
filter: drop-shadow(0 12px 20px rgba(165, 0, 18, 0.16));
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
.filter-sticky {
|
|
694
|
+
position: sticky;
|
|
695
|
+
top: calc(50px + env(safe-area-inset-top, 0px));
|
|
696
|
+
z-index: 15;
|
|
697
|
+
margin-top: -18px;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
.filter-panel {
|
|
701
|
+
position: relative;
|
|
702
|
+
z-index: 1;
|
|
703
|
+
padding: 12px 13px 14px;
|
|
704
|
+
border-radius: 13px;
|
|
705
|
+
background: #ffffff;
|
|
706
|
+
box-shadow: 0 -4px 18px rgba(255, 66, 76, 0.06);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
.filter-panel.is-collapsed {
|
|
710
|
+
padding-bottom: 12px;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
.filter-toolbar {
|
|
714
|
+
display: grid;
|
|
715
|
+
grid-template-columns: minmax(0, 1fr) 72px;
|
|
716
|
+
column-gap: 10px;
|
|
717
|
+
align-items: center;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
.filter-heading {
|
|
721
|
+
min-width: 0;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
.filter-heading strong,
|
|
725
|
+
.filter-heading span {
|
|
726
|
+
display: block;
|
|
727
|
+
overflow: hidden;
|
|
728
|
+
text-overflow: ellipsis;
|
|
729
|
+
white-space: nowrap;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
.filter-heading strong {
|
|
733
|
+
color: #101525;
|
|
734
|
+
font-size: 14px;
|
|
735
|
+
font-weight: 750;
|
|
736
|
+
line-height: 1.25;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
.filter-heading span {
|
|
740
|
+
margin-top: 3px;
|
|
741
|
+
color: #818899;
|
|
742
|
+
font-size: 11px;
|
|
743
|
+
line-height: 1.25;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
.filter-toggle {
|
|
747
|
+
display: inline-flex;
|
|
748
|
+
align-items: center;
|
|
749
|
+
justify-content: center;
|
|
750
|
+
gap: 4px;
|
|
751
|
+
height: 30px;
|
|
752
|
+
border: 1px solid #ffd5da;
|
|
753
|
+
border-radius: 15px;
|
|
754
|
+
color: #f42b3a;
|
|
755
|
+
font-size: 12px;
|
|
756
|
+
font-weight: 650;
|
|
757
|
+
background: #fff7f8;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
.filter-toggle .van-icon {
|
|
761
|
+
font-size: 14px;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
.filter-body {
|
|
765
|
+
overflow: hidden;
|
|
766
|
+
margin-top: 8px;
|
|
767
|
+
border-top: 1px solid #eef0f4;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
.filter-body-enter-active,
|
|
771
|
+
.filter-body-leave-active {
|
|
772
|
+
transition:
|
|
773
|
+
opacity 0.16s ease,
|
|
774
|
+
transform 0.16s ease;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
.filter-body-enter-from,
|
|
778
|
+
.filter-body-leave-to {
|
|
779
|
+
opacity: 0;
|
|
780
|
+
transform: translateY(-6px);
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
.filter-row {
|
|
784
|
+
display: flex;
|
|
785
|
+
align-items: center;
|
|
786
|
+
width: 100%;
|
|
787
|
+
min-height: 48px;
|
|
788
|
+
color: #080d1f;
|
|
789
|
+
text-align: left;
|
|
790
|
+
box-sizing: border-box;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
.filter-row + .filter-row {
|
|
794
|
+
border-top: 1px solid #eef0f4;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
.row-icon {
|
|
798
|
+
display: flex;
|
|
799
|
+
align-items: center;
|
|
800
|
+
justify-content: center;
|
|
801
|
+
width: 24px;
|
|
802
|
+
height: 24px;
|
|
803
|
+
flex: 0 0 24px;
|
|
804
|
+
border-radius: 6px;
|
|
805
|
+
color: #f42b3a;
|
|
806
|
+
font-size: 16px;
|
|
807
|
+
background: linear-gradient(135deg, #fff2f3 0%, #ffe4e6 100%);
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
.row-label {
|
|
811
|
+
width: 92px;
|
|
812
|
+
flex: 0 0 92px;
|
|
813
|
+
margin-left: 11px;
|
|
814
|
+
color: #151827;
|
|
815
|
+
font-size: 14px;
|
|
816
|
+
font-weight: 650;
|
|
817
|
+
line-height: 1.2;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
.row-value {
|
|
821
|
+
overflow: hidden;
|
|
822
|
+
margin-left: auto;
|
|
823
|
+
color: #202638;
|
|
824
|
+
font-size: 14px;
|
|
825
|
+
line-height: 1.2;
|
|
826
|
+
text-align: right;
|
|
827
|
+
text-overflow: ellipsis;
|
|
828
|
+
white-space: nowrap;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
.muted-value {
|
|
832
|
+
color: #8d95a6;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
.row-input {
|
|
836
|
+
width: 0;
|
|
837
|
+
min-width: 0;
|
|
838
|
+
flex: 1;
|
|
839
|
+
border: 0;
|
|
840
|
+
outline: 0;
|
|
841
|
+
color: #202638;
|
|
842
|
+
font-size: 13px;
|
|
843
|
+
text-align: right;
|
|
844
|
+
background: transparent;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
.row-input::placeholder,
|
|
848
|
+
.amount-input::placeholder {
|
|
849
|
+
color: #a6adbd;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
.amount-input::placeholder {
|
|
853
|
+
font-size: 10px;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
.row-calendar {
|
|
857
|
+
margin-left: 12px;
|
|
858
|
+
color: #a7adba;
|
|
859
|
+
font-size: 16px;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
.row-arrow {
|
|
863
|
+
margin-left: 12px;
|
|
864
|
+
color: #9fa6b5;
|
|
865
|
+
font-size: 15px;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
.amount-row {
|
|
869
|
+
gap: 0;
|
|
870
|
+
min-height: 52px;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
.amount-row .row-label {
|
|
874
|
+
width: 58px;
|
|
875
|
+
flex-basis: 58px;
|
|
876
|
+
margin-right: 8px;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
.amount-input {
|
|
880
|
+
width: 0;
|
|
881
|
+
min-width: 0;
|
|
882
|
+
flex: 1 1 0;
|
|
883
|
+
height: 28px;
|
|
884
|
+
border: 1px solid #e5e8ef;
|
|
885
|
+
border-radius: 6px;
|
|
886
|
+
padding: 0 4px;
|
|
887
|
+
color: #202638;
|
|
888
|
+
font-size: 11px;
|
|
889
|
+
text-align: center;
|
|
890
|
+
outline: 0;
|
|
891
|
+
background: #ffffff;
|
|
892
|
+
box-sizing: border-box;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
.amount-divider {
|
|
896
|
+
flex: 0 0 18px;
|
|
897
|
+
color: #8c94a5;
|
|
898
|
+
font-size: 13px;
|
|
899
|
+
text-align: center;
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
.filter-actions {
|
|
903
|
+
display: grid;
|
|
904
|
+
grid-template-columns: 92px minmax(0, 1fr);
|
|
905
|
+
gap: 10px;
|
|
906
|
+
margin-top: 12px;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
.reset-btn,
|
|
910
|
+
.search-btn {
|
|
911
|
+
display: flex;
|
|
912
|
+
align-items: center;
|
|
913
|
+
justify-content: center;
|
|
914
|
+
gap: 8px;
|
|
915
|
+
width: 100%;
|
|
916
|
+
height: 34px;
|
|
917
|
+
border-radius: 18px;
|
|
918
|
+
font-size: 14px;
|
|
919
|
+
font-weight: 650;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
.reset-btn {
|
|
923
|
+
border: 1px solid #ffd5da;
|
|
924
|
+
color: #f42b3a;
|
|
925
|
+
background: #fff7f8;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
.search-btn {
|
|
929
|
+
color: #ffffff;
|
|
930
|
+
background: linear-gradient(135deg, #ff3340 0%, #ff1d34 100%);
|
|
931
|
+
box-shadow: 0 8px 16px rgba(255, 36, 52, 0.18);
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
.reset-btn .van-icon,
|
|
935
|
+
.search-btn .van-icon {
|
|
936
|
+
font-size: 17px;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
.forecast-card {
|
|
940
|
+
margin-top: 9px;
|
|
941
|
+
overflow: hidden;
|
|
942
|
+
border-radius: 13px;
|
|
943
|
+
background: rgba(255, 255, 255, 0.95);
|
|
944
|
+
box-shadow: 0 7px 22px rgba(21, 28, 45, 0.055);
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
.forecast-item {
|
|
948
|
+
position: relative;
|
|
949
|
+
display: grid;
|
|
950
|
+
grid-template-columns: 42px minmax(0, 1fr) auto 14px;
|
|
951
|
+
column-gap: 10px;
|
|
952
|
+
align-items: center;
|
|
953
|
+
width: 100%;
|
|
954
|
+
min-height: 91px;
|
|
955
|
+
padding: 14px 17px;
|
|
956
|
+
color: #080d1f;
|
|
957
|
+
text-align: left;
|
|
958
|
+
box-sizing: border-box;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
.forecast-item + .forecast-item {
|
|
962
|
+
border-top: 1px solid #edf0f3;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
.forecast-avatar {
|
|
966
|
+
display: flex;
|
|
967
|
+
align-items: center;
|
|
968
|
+
justify-content: center;
|
|
969
|
+
width: 36px;
|
|
970
|
+
height: 36px;
|
|
971
|
+
border-radius: 50%;
|
|
972
|
+
color: #111728;
|
|
973
|
+
font-size: 24px;
|
|
974
|
+
background: #fff0f1;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
.forecast-avatar .van-icon {
|
|
978
|
+
transform: translateY(1px);
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
.forecast-main {
|
|
982
|
+
min-width: 0;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
.forecast-main strong {
|
|
986
|
+
display: block;
|
|
987
|
+
overflow: hidden;
|
|
988
|
+
color: #080d1f;
|
|
989
|
+
font-size: 14px;
|
|
990
|
+
font-weight: 760;
|
|
991
|
+
line-height: 1.35;
|
|
992
|
+
text-overflow: ellipsis;
|
|
993
|
+
white-space: nowrap;
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
.forecast-date,
|
|
997
|
+
.forecast-business {
|
|
998
|
+
display: block;
|
|
999
|
+
overflow: hidden;
|
|
1000
|
+
margin-top: 7px;
|
|
1001
|
+
font-size: 12px;
|
|
1002
|
+
line-height: 1.2;
|
|
1003
|
+
text-overflow: ellipsis;
|
|
1004
|
+
white-space: nowrap;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
.forecast-date {
|
|
1008
|
+
color: #8a92a3;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
.forecast-business {
|
|
1012
|
+
color: #111728;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
.forecast-side {
|
|
1016
|
+
display: flex;
|
|
1017
|
+
align-items: flex-end;
|
|
1018
|
+
flex-direction: column;
|
|
1019
|
+
min-width: 100px;
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
.forecast-amount {
|
|
1023
|
+
color: #20ad5b;
|
|
1024
|
+
font-size: 15px;
|
|
1025
|
+
font-weight: 650;
|
|
1026
|
+
line-height: 1.2;
|
|
1027
|
+
white-space: nowrap;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
.forecast-amount em {
|
|
1031
|
+
color: #20ad5b;
|
|
1032
|
+
font-size: 11px;
|
|
1033
|
+
font-style: normal;
|
|
1034
|
+
font-weight: 400;
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
.status-tag {
|
|
1038
|
+
display: inline-flex;
|
|
1039
|
+
align-items: center;
|
|
1040
|
+
justify-content: center;
|
|
1041
|
+
min-height: 20px;
|
|
1042
|
+
max-width: 128px;
|
|
1043
|
+
margin-top: 10px;
|
|
1044
|
+
border: 1px solid transparent;
|
|
1045
|
+
border-radius: 4px;
|
|
1046
|
+
padding: 2px 7px;
|
|
1047
|
+
font-size: 11px;
|
|
1048
|
+
font-weight: 650;
|
|
1049
|
+
line-height: 1.25;
|
|
1050
|
+
text-align: right;
|
|
1051
|
+
white-space: nowrap;
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
.status-tag-confirmed {
|
|
1055
|
+
border-color: #d8e1ee;
|
|
1056
|
+
color: #687083;
|
|
1057
|
+
background: #ffffff;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
.status-tag-pending {
|
|
1061
|
+
border-color: #ffd99a;
|
|
1062
|
+
color: #a05a00;
|
|
1063
|
+
background: #fff4d8;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
.status-tag-cancelled {
|
|
1067
|
+
border-color: #d6dbe3;
|
|
1068
|
+
color: #687083;
|
|
1069
|
+
background: #f3f5f8;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
.status-tag-verified {
|
|
1073
|
+
border-color: #b7ebc9;
|
|
1074
|
+
color: #0b8f3a;
|
|
1075
|
+
background: #e4f8eb;
|
|
16
1076
|
}
|
|
17
|
-
|
|
18
|
-
|
|
1077
|
+
|
|
1078
|
+
.status-tag-rejected {
|
|
1079
|
+
border-color: #ffc0cb;
|
|
1080
|
+
color: #bf2549;
|
|
1081
|
+
background: #ffe8ed;
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
.status-tag-unknown {
|
|
1085
|
+
border-color: #d6dbe3;
|
|
1086
|
+
color: #687083;
|
|
1087
|
+
background: #f3f5f8;
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
.forecast-arrow {
|
|
1091
|
+
color: #9da4b3;
|
|
1092
|
+
font-size: 15px;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
.fab {
|
|
1096
|
+
position: fixed;
|
|
1097
|
+
right: max(25px, calc((100vw - 750px) / 2 + 25px));
|
|
1098
|
+
bottom: calc(env(safe-area-inset-bottom, 0px) + 35px);
|
|
1099
|
+
z-index: 20;
|
|
1100
|
+
display: flex;
|
|
1101
|
+
align-items: center;
|
|
1102
|
+
justify-content: center;
|
|
1103
|
+
width: 52px;
|
|
1104
|
+
height: 52px;
|
|
1105
|
+
border-radius: 50%;
|
|
1106
|
+
color: #ffffff;
|
|
1107
|
+
font-size: 31px;
|
|
1108
|
+
background: linear-gradient(145deg, #ff4050 0%, #f50f2d 100%);
|
|
1109
|
+
box-shadow: 0 13px 28px rgba(244, 29, 48, 0.34);
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
.back-top-btn {
|
|
1113
|
+
position: fixed;
|
|
1114
|
+
right: max(31px, calc((100vw - 750px) / 2 + 31px));
|
|
1115
|
+
bottom: calc(env(safe-area-inset-bottom, 0px) + 99px);
|
|
1116
|
+
z-index: 20;
|
|
1117
|
+
display: flex;
|
|
1118
|
+
align-items: center;
|
|
1119
|
+
justify-content: center;
|
|
1120
|
+
width: 40px;
|
|
1121
|
+
height: 40px;
|
|
1122
|
+
border: 1px solid rgba(226, 232, 240, 0.9);
|
|
1123
|
+
border-radius: 50%;
|
|
1124
|
+
color: #f22f3d;
|
|
1125
|
+
font-size: 20px;
|
|
1126
|
+
background: rgba(255, 255, 255, 0.94);
|
|
1127
|
+
box-shadow: 0 10px 24px rgba(21, 28, 45, 0.13);
|
|
1128
|
+
backdrop-filter: blur(12px);
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
.back-top-enter-active,
|
|
1132
|
+
.back-top-leave-active {
|
|
1133
|
+
transition:
|
|
1134
|
+
opacity 0.18s ease,
|
|
1135
|
+
transform 0.18s ease;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
.back-top-enter-from,
|
|
1139
|
+
.back-top-leave-to {
|
|
1140
|
+
opacity: 0;
|
|
1141
|
+
transform: translateY(8px) scale(0.92);
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
.selector-panel {
|
|
1145
|
+
padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 8px);
|
|
1146
|
+
background: #ffffff;
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
.selector-header {
|
|
1150
|
+
display: grid;
|
|
1151
|
+
grid-template-columns: 56px minmax(0, 1fr) 56px;
|
|
1152
|
+
align-items: center;
|
|
1153
|
+
height: 48px;
|
|
1154
|
+
border-bottom: 1px solid #eef0f4;
|
|
1155
|
+
padding: 0 12px;
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
.selector-header button {
|
|
1159
|
+
color: #6f7788;
|
|
1160
|
+
font-size: 14px;
|
|
1161
|
+
text-align: left;
|
|
19
1162
|
}
|
|
20
|
-
|
|
1163
|
+
|
|
1164
|
+
.selector-header strong {
|
|
1165
|
+
color: #111728;
|
|
1166
|
+
font-size: 15px;
|
|
1167
|
+
font-weight: 700;
|
|
21
1168
|
text-align: center;
|
|
22
|
-
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
.selector-option {
|
|
1172
|
+
display: flex;
|
|
1173
|
+
align-items: center;
|
|
1174
|
+
justify-content: space-between;
|
|
1175
|
+
width: 100%;
|
|
1176
|
+
min-height: 48px;
|
|
1177
|
+
border-bottom: 1px solid #f1f3f6;
|
|
1178
|
+
padding: 0 18px;
|
|
1179
|
+
color: #151827;
|
|
23
1180
|
font-size: 14px;
|
|
24
|
-
|
|
1181
|
+
text-align: left;
|
|
1182
|
+
box-sizing: border-box;
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
.selector-option .van-icon {
|
|
1186
|
+
color: #f42b3a;
|
|
1187
|
+
font-size: 18px;
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
@media (max-width: 340px) {
|
|
1191
|
+
.amount-row {
|
|
1192
|
+
gap: 5px;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
.amount-input {
|
|
1196
|
+
width: 0;
|
|
1197
|
+
padding: 0 5px;
|
|
1198
|
+
font-size: 11px;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
.row-label {
|
|
1202
|
+
width: 80px;
|
|
1203
|
+
flex-basis: 80px;
|
|
1204
|
+
margin-left: 9px;
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
.amount-row .row-label {
|
|
1208
|
+
width: 52px;
|
|
1209
|
+
flex-basis: 52px;
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
.forecast-item {
|
|
1213
|
+
grid-template-columns: 38px minmax(0, 1fr) auto 12px;
|
|
1214
|
+
column-gap: 8px;
|
|
1215
|
+
padding-right: 12px;
|
|
1216
|
+
padding-left: 12px;
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
.forecast-side {
|
|
1220
|
+
min-width: 88px;
|
|
1221
|
+
}
|
|
25
1222
|
}
|
|
26
1223
|
</style>
|