@uxda/appkit 4.0.3 → 4.0.12

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 (114) hide show
  1. package/.eslintrc.mjs +7 -7
  2. package/README.md +187 -187
  3. package/babel.config.js +12 -12
  4. package/dist/appkit.css +144 -5
  5. package/dist/index.js +730 -248
  6. package/package.json +75 -80
  7. package/project.config.json +15 -15
  8. package/project.tt.json +13 -13
  9. package/rollup.config.mjs +56 -56
  10. package/src/Appkit.ts +65 -65
  11. package/src/balance/api/endpoints.ts +126 -126
  12. package/src/balance/api/index.ts +82 -82
  13. package/src/balance/components/AccountView.vue +748 -748
  14. package/src/balance/components/BalanceCard.vue +205 -209
  15. package/src/balance/components/BalanceReminder.vue +85 -85
  16. package/src/balance/components/ConsumptionFilter.vue +218 -218
  17. package/src/balance/components/ConsumptionRules.vue +68 -68
  18. package/src/balance/components/DateFilter.vue +226 -226
  19. package/src/balance/components/DateRange.vue +62 -0
  20. package/src/balance/components/ListFilter.vue +40 -0
  21. package/src/balance/components/ListFilterPicker.vue +195 -0
  22. package/src/balance/components/SecondBalance.vue +71 -71
  23. package/src/balance/components/Tip.vue +45 -45
  24. package/src/balance/components/index.ts +13 -9
  25. package/src/balance/types.ts +91 -90
  26. package/src/components/bt-cropper/index.vue +774 -774
  27. package/src/components/bt-cropper/utils/calcCropper.js +42 -42
  28. package/src/components/bt-cropper/utils/calcImagePosition.js +23 -23
  29. package/src/components/bt-cropper/utils/calcImageSize.js +37 -37
  30. package/src/components/bt-cropper/utils/calcPointDistance.js +12 -12
  31. package/src/components/bt-cropper/utils/calcRightAndBottom.js +7 -7
  32. package/src/components/bt-cropper/utils/ratio.js +3 -3
  33. package/src/components/bt-cropper/utils/tools.js +25 -25
  34. package/src/components/dd-area/index.vue +225 -225
  35. package/src/components/dd-icon/doc.md +21 -21
  36. package/src/components/dd-icon/index.vue +23 -23
  37. package/src/components/dd-notice-bar/index.vue +78 -78
  38. package/src/components/dd-search/doc.md +34 -34
  39. package/src/components/dd-search/index.vue +168 -168
  40. package/src/components/dd-selector/index.vue +124 -124
  41. package/src/components/dd-skeleton/doc.md +19 -19
  42. package/src/components/dd-skeleton/index.vue +36 -36
  43. package/src/components/ocr-id/index.vue +114 -114
  44. package/src/components/ocr-id/types.d.ts +12 -12
  45. package/src/global.ts +6 -6
  46. package/src/index.ts +89 -89
  47. package/src/main.scss +1 -1
  48. package/src/notice/api/endpoints.ts +17 -17
  49. package/src/notice/api/index.ts +82 -82
  50. package/src/notice/components/NoticeBanner.vue +243 -243
  51. package/src/notice/components/NoticeEntry.vue +99 -99
  52. package/src/notice/components/NoticeList.vue +315 -315
  53. package/src/notice/components/NoticePopup.vue +161 -161
  54. package/src/notice/components/index.ts +5 -5
  55. package/src/notice/components/useCommonList.ts +86 -86
  56. package/src/notice/components/useNotice.ts +35 -35
  57. package/src/notice/index.ts +1 -1
  58. package/src/notice/types.ts +25 -25
  59. package/src/payment/api/config.ts +7 -7
  60. package/src/payment/api/endpoints.ts +103 -103
  61. package/src/payment/api/index.ts +71 -71
  62. package/src/payment/components/AmountPicker.vue +93 -93
  63. package/src/payment/components/RechargeResult.vue +69 -69
  64. package/src/payment/components/RechargeView.vue +154 -154
  65. package/src/payment/components/RightsPicker.vue +105 -105
  66. package/src/payment/components/TradeView.vue +298 -298
  67. package/src/payment/components/UserAgreement.vue +234 -234
  68. package/src/payment/components/index.ts +22 -22
  69. package/src/payment/index.ts +5 -5
  70. package/src/payment/services/index.ts +16 -16
  71. package/src/payment/services/invoke-recharge.ts +25 -25
  72. package/src/payment/services/request-payment.ts +58 -58
  73. package/src/payment/types.ts +28 -28
  74. package/src/register/components/SelfRegistration.vue +254 -254
  75. package/src/register/components/index.ts +2 -2
  76. package/src/shared/components/AppDrawer.vue +58 -58
  77. package/src/shared/components/AppVerify.vue +84 -0
  78. package/src/shared/components/DeviceVersion.vue +68 -68
  79. package/src/shared/components/EmptyView.vue +33 -33
  80. package/src/shared/components/OcrIcon.vue +119 -0
  81. package/src/shared/components/PageHeader.vue +79 -79
  82. package/src/shared/components/index.ts +7 -5
  83. package/src/shared/composables/index.ts +6 -5
  84. package/src/shared/composables/useCountdown.ts +46 -46
  85. package/src/shared/composables/useDragBox.ts +97 -97
  86. package/src/shared/composables/useEncode.ts +43 -43
  87. package/src/shared/composables/useSafeArea.ts +46 -46
  88. package/src/shared/composables/useTabbar.ts +24 -24
  89. package/src/shared/composables/useUpload.ts +55 -0
  90. package/src/shared/composables/useValidator.ts +31 -31
  91. package/src/shared/http/Http.ts +136 -136
  92. package/src/shared/http/index.ts +1 -1
  93. package/src/shared/http/types.ts +157 -157
  94. package/src/shared/index.ts +3 -3
  95. package/src/shared/weixin/payment.ts +38 -38
  96. package/src/styles/fonts.scss +2 -2
  97. package/src/styles/vars.scss +3 -3
  98. package/src/user/api/endpoints.ts +17 -17
  99. package/src/user/api/index.ts +87 -87
  100. package/src/user/components/LoginSetting.vue +114 -114
  101. package/src/user/components/UserBinding.vue +307 -307
  102. package/src/user/components/UserBindingSuccess.vue +80 -80
  103. package/src/user/components/UserEntry.vue +142 -142
  104. package/src/user/components/UserFeedback.vue +440 -440
  105. package/src/user/components/UserFeedbackEntry.vue +192 -192
  106. package/src/user/components/UserHeadCrop.vue +65 -65
  107. package/src/user/components/UserInfo.vue +632 -632
  108. package/src/user/components/UserResourceEmpty.vue +75 -75
  109. package/src/user/components/index.ts +21 -21
  110. package/src/user/index.ts +1 -1
  111. package/tsconfig.json +30 -30
  112. package/types/global.d.ts +21 -21
  113. package/types/vue.d.ts +10 -10
  114. package/dist/assets/asset-3B_CoPto +0 -1
@@ -1,748 +1,748 @@
1
- <template>
2
- <div class="account-view">
3
- <div class="scroll-content">
4
- <page-header
5
- color-mode="dark"
6
- title="我的账户"
7
- :class="{ 'with-background': scrolled > 0 }"
8
- @close="onPageHeaderClose"
9
- />
10
- <div class="row jusify-right">
11
- <div class="small-bean-button" @click="onSecondBalanceButtonClick">
12
- <label>收支明细</label>
13
- </div>
14
- </div>
15
- <div class="balance">
16
- <div class="bean-box spa-between">
17
- <div class="bean-img">
18
- <img
19
- class="bean-icon"
20
- src="https://cdn.ddjf.com/static/images/bpms-workBench/gold-bean.png"
21
- />
22
- <div class="bean-tag tag">云豆</div>
23
- </div>
24
- <div class="rule" @click="rulesPopupOpen = true">规则说明</div>
25
- </div>
26
- <div class="bean-counts spa-between">
27
- <div class="counts number">{{ balance.total || 0 }}</div>
28
- <div class="pay" @click="gotoRecharge">云豆充值</div>
29
- </div>
30
- </div>
31
- <Tip />
32
- <div class="rights-card" v-if="balance.privileges.corporateStar">
33
- <div class="title">企明星权益</div>
34
- <div class="list">
35
- <div
36
- class="item star-item"
37
- v-for="(item, index) in balance.privileges.corporateStar"
38
- :key="index"
39
- >
40
- <div class="item-count">
41
- <span>{{ item.count }}</span
42
- ><span>{{ item.unit }}</span>
43
- </div>
44
- <div class="item-title">
45
- <div>{{ item.title }}</div>
46
- <div class="item-title-button" v-if="item.id">
47
- <div @click="gotoTrade(item)">超值换购</div>
48
- <img
49
- class="button-icon"
50
- src="https://cdn.ddjf.com/static/images/bpms-workBench/button-hg.svg"
51
- />
52
- </div>
53
- </div>
54
- </div>
55
- </div>
56
- </div>
57
- <div class="rights-card" v-if="balance.privileges.aiapprove">
58
- <div class="title">AI审批权益</div>
59
- <div class="list">
60
- <div class="item" v-for="(item, index) in balance.privileges.aiapprove" :key="index">
61
- <div class="item-count">{{ item.count }}{{ item.unit }}</div>
62
- <div class="item-title">{{ item.title }}</div>
63
- </div>
64
- </div>
65
- </div>
66
- </div>
67
- </div>
68
- <nut-popup
69
- pop-class="consumption-rules-popup"
70
- v-model:visible="rulesPopupOpen"
71
- :close-on-click-overlay="false"
72
- >
73
- <consumption-rules @complete="rulesPopupOpen = false" />
74
- </nut-popup>
75
- <nut-popup
76
- position="bottom"
77
- :style="{ height: '40%' }"
78
- round
79
- :close-on-click-overlay="true"
80
- v-model:visible="datePickerOpen"
81
- >
82
- <date-filter
83
- v-if="datePickerOpen"
84
- :from="filtering.dateFrom"
85
- :to="filtering.dateTo"
86
- @reset="onDateReset"
87
- @complete="onDateFilterComplete"
88
- />
89
- </nut-popup>
90
- <nut-popup
91
- position="bottom"
92
- :style="{ height: '75%' }"
93
- round
94
- :close-on-click-overlay="true"
95
- v-model:visible="filterOpen"
96
- >
97
- <consumption-filter
98
- :modelValue="[
99
- filtering.账户类型,
100
- filtering.收入还是支出,
101
- filtering.交易类型,
102
- filtering.权益类目,
103
- ]"
104
- @complete="onFilterComplete"
105
- />
106
- </nut-popup>
107
- <app-drawer v-model="secondBalanceOpen" title="收支明细">
108
- <div class="operation-box">
109
- <div class="operation-title spa-between" :class="{ 'with-shadow': scrolled > 0 }">
110
- <div class="search-time">
111
- <div class="title">收支明细</div>
112
- <div class="time" @click="openDateFilter" v-show="filtering.dateFrom">
113
- <div class="text number">{{ dateRangeDisplay }}</div>
114
- <img
115
- style="margin-top: -2px"
116
- class="time-icon"
117
- src="https://cdn.ddjf.com/static/images/bpms-workBench/clound-bean-down.png"
118
- />
119
- </div>
120
- </div>
121
- <div class="search" @click="filterOpen = true">
122
- <span class="text">筛选</span>
123
- <img
124
- class="time-icon"
125
- src="https://cdn.ddjf.com/static/images/bpms-workBench/clound-bean-select-icon.png"
126
- />
127
- </div>
128
- </div>
129
- <div class="operation-list">
130
- <scroll-view
131
- class="operation-scroll"
132
- :scroll-y="true"
133
- @scroll="onScroll"
134
- :lower-threshold="50"
135
- @scrolltolower="onReachBottom"
136
- >
137
- <div class="box" v-if="consumptionGroups.length > 0">
138
- <div class="box-detail" v-for="(item, index) in consumptionGroups" :key="index">
139
- <div class="title number">{{ item.date }}</div>
140
- <div class="item" v-for="(it, i) in item.consumptions" :key="i">
141
- <div class="item-type">
142
- {{ it.交易类型 }}
143
- </div>
144
- <div class="item-detail">
145
- <div class="item-info spa-between">
146
- <div>
147
- <div class="item-info-type">
148
- {{ it.账户类型 }}
149
- </div>
150
- <div class="item-info-title">{{ it.title }}</div>
151
- </div>
152
- <div class="item-info-amount number">
153
- {{ it.收入还是支出 == '支出' ? '-' : '+' }}{{ it.amount }}
154
- </div>
155
- </div>
156
- <div class="item-detail-remark">{{ it.description }}</div>
157
- </div>
158
- </div>
159
- </div>
160
- <div class="box-not-text" v-if="reachedLastPage">没有更多了</div>
161
- </div>
162
- <empty-view v-else></empty-view>
163
- </scroll-view>
164
- </div>
165
- </div>
166
- </app-drawer>
167
- </template>
168
-
169
- <script lang="ts" setup>
170
- import Taro, { useDidShow } from '@tarojs/taro'
171
- import { computed, onMounted, reactive, ref, watch } from 'vue'
172
- import { endpoints, useHttp } from '../api'
173
- import { 账户流水筛选项, Balance, ConsumptionGroup, 账户流水 } from '../types'
174
- import ConsumptionFilter from './ConsumptionFilter.vue'
175
- import DateFilter from './DateFilter.vue'
176
- import ConsumptionRules from './ConsumptionRules.vue'
177
- import { AppDrawer, PageHeader, WithPaging } from '../../shared'
178
- import dayjs from 'dayjs'
179
- import EmptyView from '../../shared/components/EmptyView.vue'
180
- import Tip from './Tip.vue'
181
- import groupBy from 'lodash/groupBy'
182
-
183
- const refreshing = ref(false)
184
-
185
- type AccountViewProps = {
186
- app: string
187
- }
188
- const props = withDefaults(defineProps<AccountViewProps>(), {
189
- app: '',
190
- })
191
-
192
- const emit = defineEmits(['recharge', 'trade'])
193
-
194
- const filterOpen = ref<boolean>(false)
195
-
196
- // 规则说明弹窗
197
- const rulesPopupOpen = ref<boolean>(false)
198
-
199
- const filtering = reactive<账户流水筛选项>({
200
- 账户类型: '全部',
201
- 收入还是支出: '全部',
202
- 交易类型: '全部',
203
- 权益类目: '',
204
- dateFrom: '',
205
- dateTo: '',
206
- page: 1,
207
- pageSize: 20,
208
- })
209
-
210
- const reachedLastPage = ref<boolean>(false)
211
-
212
- /**
213
- * 全新搜索
214
- * 重置某些查询条件
215
- */
216
- function restartSearch() {
217
- // 从第一页开始
218
- filtering.page = 1
219
- consumptionGroups.value = []
220
- loadConsumptions()
221
- }
222
-
223
- // 时间筛选
224
- const datePickerOpen = ref<boolean>(false)
225
-
226
- function openDateFilter() {
227
- datePickerOpen.value = true
228
- }
229
-
230
- const dateRangeDisplay = computed(() => {
231
- let startTime = (filtering.dateFrom || '').replace(/-/g, '.').substring(2)
232
- let endTime = (filtering.dateTo || '').replace(/-/g, '.').substring(2)
233
- return startTime + ' - ' + endTime
234
- })
235
-
236
- const consumptionGroups = ref<ConsumptionGroup[]>([])
237
-
238
- /**
239
- * 余额数据
240
- */
241
- const balance = ref<Balance>({
242
- total: 0,
243
- privileges: {},
244
- })
245
-
246
- useDidShow(() => {
247
- Taro.setNavigationBarTitle({
248
- title: '我的账户',
249
- })
250
- loadBalance()
251
- })
252
-
253
- function groupDataByDate(data: 账户流水[]): ConsumptionGroup[] {
254
- // 按照日期分组
255
- const groups: ConsumptionGroup[] = consumptionGroups.value || []
256
- // [
257
- // date: '2023-10-1',
258
- // consumptions: []
259
- // ]
260
- // 组装成按日期分组
261
- data.forEach((d) => {
262
- const date = dayjs(d.time).format('YYYY-MM-DD')
263
- let group: ConsumptionGroup | undefined = groups.find((g) => g.date === date)
264
- if (!group) {
265
- group = {
266
- date,
267
- consumptions: [],
268
- }
269
- groups.push(group)
270
- }
271
- group.consumptions.push(d)
272
- })
273
- groups.sort((a, b) => (a.date > b.date ? -1 : 1))
274
- return groups
275
- }
276
-
277
- /**
278
- * 请求账户流水
279
- * @param append 搜索结果累加 上拉分页时
280
- */
281
- async function loadConsumptions(append: boolean = false) {
282
- if (!append) {
283
- consumptionGroups.value = []
284
- }
285
- const $http = useHttp()
286
- Taro.showLoading({
287
- title: `加载中...`,
288
- mask: true,
289
- })
290
- $http
291
- .get<WithPaging<账户流水[]>>(endpoints.获取账户流水, {
292
- app: props.app,
293
- ...filtering,
294
- })
295
- .then((response) => {
296
- consumptionGroups.value = groupDataByDate(response.data)
297
- if (filtering.page >= response.totalPages) {
298
- filtering.page = response.totalPages
299
- reachedLastPage.value = true
300
- } else {
301
- reachedLastPage.value = false
302
- }
303
- Taro.hideLoading()
304
- })
305
- }
306
-
307
- /**
308
- * 获取账户余额明细
309
- */
310
- async function loadBalance() {
311
- const $http = useHttp()
312
- $http
313
- .get<Balance>(endpoints.获取余额明细, {
314
- app: props.app,
315
- })
316
- .then((data) => {
317
- const { privileges, total } = data
318
- const filterData = {
319
- total,
320
- privileges: groupBy(privileges, 'appCode'),
321
- }
322
- balance.value = filterData
323
- })
324
- }
325
-
326
- const onFilterComplete = (value) => {
327
- filtering.账户类型 = value[0]
328
- filtering.收入还是支出 = value[1]
329
- filtering.交易类型 = value[2]
330
- filtering.权益类目 = value[3]
331
- filterOpen.value = false
332
- restartSearch()
333
- }
334
-
335
- const onDateFilterComplete = (value) => {
336
- filtering.dateFrom = value.from
337
- filtering.dateTo = value.to
338
- datePickerOpen.value = false
339
- restartSearch()
340
- }
341
-
342
- function gotoRecharge() {
343
- emit('recharge')
344
- }
345
-
346
- function gotoTrade(item: any) {
347
- emit('trade', item)
348
- }
349
-
350
- function loadNextPage() {
351
- filtering.page++
352
- loadConsumptions(true)
353
- }
354
-
355
- // 以下这一大段处理下拉刷新、上滑分页以及弹窗与页面滑动的逻辑
356
- const scrolled = ref<number>(0)
357
-
358
- /**
359
- * 记录 scroll-view 滚动的位置
360
- */
361
- const onScroll = (e) => {
362
- const { scrollTop } = e
363
- scrolled.value = scrollTop
364
- }
365
-
366
- /**
367
- * 下拉刷新 pull down refresh
368
- */
369
- const onPullDownRefresh = () => {
370
- refreshing.value = true
371
- // 重新请求余额数据
372
- loadBalance()
373
- setTimeout(() => {
374
- refreshing.value = false
375
- }, 500)
376
- }
377
-
378
- /**
379
- * 滑动到底部请求下一页并合并查询结果
380
- */
381
- const onReachBottom = () => {
382
- console.log('到底了')
383
- if (reachedLastPage.value) {
384
- return false
385
- }
386
- loadNextPage()
387
- }
388
-
389
- /**
390
- * 浮窗弹出时不允许
391
- * 1. 下拉刷新
392
- * 2. 上滑分页
393
- */
394
- const isAnyPopupOpen = computed(() => {
395
- return rulesPopupOpen.value || datePickerOpen.value || filterOpen.value || secondBalanceOpen.value
396
- })
397
-
398
- const secondBalanceOpen = ref<boolean>(false)
399
-
400
- function onSecondBalanceButtonClick() {
401
- secondBalanceOpen.value = true
402
- loadConsumptions()
403
- }
404
-
405
- watch(secondBalanceOpen, () => {
406
- Taro.setNavigationBarColor({
407
- frontColor: secondBalanceOpen.value ? '#000000' : '#ffffff',
408
- backgroundColor: '#ffffff',
409
- animation: {
410
- duration: 400,
411
- timingFunc: 'easeIn',
412
- },
413
- })
414
- })
415
-
416
- function onDateReset() {
417
- resetDateRange()
418
- }
419
-
420
- /**
421
- * 充值筛选的起止时间
422
- */
423
- function resetDateRange() {
424
- // 筛选的起止时间
425
- filtering.dateTo = dayjs().format('YYYY-MM-DD')
426
- filtering.dateFrom = dayjs().add(-3, 'M').format('YYYY-MM-DD')
427
- }
428
-
429
- function onPageHeaderClose() {
430
- Taro.navigateBack({
431
- delta: 1,
432
- })
433
- }
434
-
435
- onMounted(() => {
436
- resetDateRange()
437
- })
438
- </script>
439
-
440
- <style lang="scss">
441
- .account-view {
442
- height: 100vh;
443
- .scroll-content {
444
- background-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNzUwIiBoZWlnaHQ9IjQwNiIgdmlld0JveD0iMCAwIDc1MCA0MDYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik0zODIuNSA0MDcuNUM1NzcuMzU3IDQwNy41IDc1My43ODggMzgxLjI0OCA4ODEuNTIxIDMzOC43OTFDOTQ1LjM4NiAzMTcuNTYzIDk5Ny4xMDQgMjkyLjI3NiAxMDMyLjg3IDI2NC4xNzFDMTA2OC42MiAyMzYuMDcxIDEwODguNSAyMDUuMDk3IDEwODguNSAxNzIuNUMxMDg4LjUgMTM5LjkwMyAxMDY4LjYyIDEwOC45MjkgMTAzMi44NyA4MC44Mjg5Qzk5Ny4xMDQgNTIuNzI0NSA5NDUuMzg2IDI3LjQzNjcgODgxLjUyMSA2LjIwODk4Qzc1My43ODggLTM2LjI0ODIgNTc3LjM1NyAtNjIuNSAzODIuNSAtNjIuNUMxODcuNjQzIC02Mi41IDExLjIxMjIgLTM2LjI0ODIgLTExNi41MjIgNi4yMDg5OEMtMTgwLjM4NiAyNy40MzY3IC0yMzIuMTA0IDUyLjcyNDUgLTI2Ny44NjcgODAuODI4OUMtMzAzLjYyNCAxMDguOTI5IC0zMjMuNSAxMzkuOTAzIC0zMjMuNSAxNzIuNUMtMzIzLjUgMjA1LjA5NyAtMzAzLjYyNCAyMzYuMDcxIC0yNjcuODY3IDI2NC4xNzFDLTIzMi4xMDQgMjkyLjI3NiAtMTgwLjM4NiAzMTcuNTYzIC0xMTYuNTIyIDMzOC43OTFDMTEuMjEyMiAzODEuMjQ4IDE4Ny42NDMgNDA3LjUgMzgyLjUgNDA3LjVaIiBmaWxsPSIjM0IzOTNDIiBzdHJva2U9ImJsYWNrIi8+Cjwvc3ZnPgo=');
445
- background-position: center -200px;
446
- background-repeat: no-repeat;
447
- }
448
- .page-header {
449
- background-color: #3b393c;
450
- position: sticky;
451
- top: 0;
452
- z-index: 1;
453
- transition: background-color 0.3s;
454
- &.with-background {
455
- background-color: #3b393c;
456
- }
457
- }
458
- .spa-between {
459
- display: flex;
460
- justify-content: space-between;
461
- align-items: center;
462
- }
463
- .row {
464
- display: flex;
465
- flex-direction: row;
466
- margin: 10px 15px;
467
- }
468
- .jusify-right {
469
- justify-content: flex-end;
470
- }
471
- .small-bean-button {
472
- height: 22px;
473
- border-radius: 11px;
474
- border: 1px solid #ffffff10;
475
- background-color: #ffffff18;
476
- display: inline-block;
477
- padding: 0 10px;
478
- line-height: 22px;
479
- color: #e7caad;
480
- font-size: 12px;
481
- label {
482
- background-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTEwIDZMMTYgMTJMMTAgMThWNloiIGZpbGw9IiNFN0NBQUQiIHN0cm9rZT0iI0U3Q0FBRCIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+Cjwvc3ZnPgo=');
483
- padding-right: 18px;
484
- background-repeat: no-repeat;
485
- background-position: right center;
486
- background-size: 16px;
487
- }
488
- }
489
- .balance {
490
- border-radius: 15px;
491
- background: linear-gradient(105deg, #f4e2ce 1.88%, #debb9b 98.18%);
492
- box-shadow: 0px -10px 9px -3px rgba(0, 0, 0, 0.33);
493
- height: 112px;
494
- padding: 20px;
495
- margin: 0 15px;
496
- box-sizing: border-box;
497
- font-size: 10px;
498
- .bean-img {
499
- display: flex;
500
- justify-content: flex-start;
501
- align-items: center;
502
- .bean-icon {
503
- display: block;
504
- font-size: 0;
505
- width: 20px;
506
- height: 20px;
507
- margin-right: 4px;
508
- }
509
- .bean-tag {
510
- color: #353535;
511
- height: 15px;
512
- line-height: 15px;
513
- border-radius: 30px 50px 50px 0px;
514
- background: rgba(#ff8320, 0.3);
515
- padding-left: 5px;
516
- padding-right: 10px;
517
- }
518
- .tag {
519
- background: #ff8320;
520
- color: #fff;
521
- }
522
- }
523
- .rule {
524
- color: #353535;
525
- opacity: 0.5;
526
- }
527
- }
528
- .bean-counts {
529
- margin-top: 12px;
530
- .counts {
531
- color: #3d3835;
532
- font-size: 32px;
533
- font-weight: 700;
534
- }
535
- .pay {
536
- padding: 0 20px;
537
- height: 32px;
538
- line-height: 32px;
539
- background: linear-gradient(187.18deg, #353535 10.04%, #433f46 90.21%);
540
- border-radius: 16px;
541
- color: #e7caad;
542
- font-size: 15px;
543
- font-weight: 500;
544
- }
545
- }
546
- }
547
- .consumption-rules-popup {
548
- border-radius: 16px;
549
- width: 70%;
550
- max-height: 80%;
551
- padding: 24px;
552
- }
553
-
554
- .operation-box {
555
- width: 100vw;
556
- overflow: hidden;
557
- display: flex;
558
- flex-direction: column;
559
- height: calc(100vh - 87px);
560
- .operation-title {
561
- padding: 10px 15px;
562
- background: #fff;
563
- transition: box-shadow 0.3s;
564
-
565
- &.with-shadow {
566
- box-shadow: 0px 1px 10px 0px #99999933;
567
- }
568
- .text {
569
- color: #353535;
570
- font-size: 12px;
571
- opacity: 0.5;
572
- }
573
- .time-icon {
574
- display: block;
575
- font-size: 0;
576
- width: 12px;
577
- height: 12px;
578
- margin-left: 4px;
579
- }
580
- .search-time {
581
- display: flex;
582
- align-items: center;
583
- .title {
584
- color: #000;
585
- font-weight: 500;
586
- font-size: 17px;
587
- margin-right: 10px;
588
- }
589
- .time {
590
- height: 22px;
591
- padding-right: 5px;
592
- flex: 1;
593
- display: flex;
594
- align-items: center;
595
- }
596
- }
597
- .search {
598
- display: flex;
599
- align-items: center;
600
- height: 22px;
601
- padding-left: 5px;
602
- }
603
- }
604
- .spa-between {
605
- display: flex;
606
- justify-content: space-between;
607
- align-items: center;
608
- }
609
- .operation-list {
610
- flex: 1;
611
- width: 100%;
612
- .operation-scroll {
613
- height: calc(100vh - 130px);
614
- padding: 0 15px;
615
- box-sizing: border-box;
616
- }
617
- .box {
618
- &-detail {
619
- .title {
620
- line-height: 22px;
621
- font-weight: 700;
622
- font-size: 12px;
623
- color: #000;
624
- opacity: 0.5;
625
- margin-bottom: 10px;
626
- }
627
- .item {
628
- border-radius: 5px;
629
- background: rgba(255, 255, 255, 0.5);
630
- box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.05);
631
- border-radius: 5px;
632
- padding: 12px 10px;
633
- display: flex;
634
- margin-bottom: 10px;
635
- &-type {
636
- background: linear-gradient(113.95deg, #f4e2ce 1.2%, #debb9b 77.63%);
637
- width: 30px;
638
- height: 30px;
639
- border-radius: 15px;
640
- margin-right: 15px;
641
- line-height: 30px;
642
- font-size: 10px;
643
- font-weight: 500;
644
- color: #000;
645
- text-align: center;
646
- }
647
- &-detail {
648
- flex: 1;
649
- .item-info {
650
- padding: 0;
651
- font-size: 14px;
652
- font-weight: 700;
653
- color: #000;
654
- &-type {
655
- line-height: 15px;
656
- }
657
- &-title {
658
- font-weight: 400;
659
- opacity: 0.8;
660
- font-size: 11px;
661
- }
662
- &-amount {
663
- color: #9e7b5a;
664
- }
665
- }
666
- &-remark {
667
- opacity: 0.3;
668
- line-height: 15px;
669
- font-size: 10px;
670
- }
671
- }
672
- }
673
- }
674
- &-not-text {
675
- opacity: 0.4;
676
- color: #353535;
677
- font-size: 12px;
678
- line-height: 20px;
679
- margin: 0 auto 40px;
680
- text-align: center;
681
- }
682
- }
683
- }
684
- }
685
- .rights-card {
686
- margin: 10px 15px 0px;
687
- background: #fff8f3;
688
- border-radius: 5px;
689
- box-shadow: 0px 5px 18.9px 2px #0000000d;
690
- padding: 15px 20px;
691
- .title {
692
- height: 25px;
693
- line-height: 25px;
694
- background: rgba($color: #ff8320, $alpha: 0.3);
695
- border-radius: 20px 50px 50px 0px;
696
- color: #353535;
697
- font-size: 12px;
698
- display: inline-block;
699
- padding-left: 5px;
700
- padding-right: 12px;
701
- }
702
- .list {
703
- display: flex;
704
- flex-wrap: wrap;
705
- .item {
706
- width: 50%;
707
- margin-top: 15px;
708
- .item-count {
709
- font-size: 20px;
710
- color: #353535;
711
- font-weight: 700;
712
- line-height: 23px;
713
- }
714
- .item-title {
715
- line-height: 23px;
716
- color: #987356;
717
- font-size: 11px;
718
- display: flex;
719
- align-items: center;
720
- .item-title-button {
721
- padding: 0 27rpx;
722
- height: 54rpx;
723
- line-height: 54rpx;
724
- background: -webkit-linear-gradient(262.82deg, #353535 10.04%, #433f46 90.21%);
725
- background: linear-gradient(187.18deg, #353535 10.04%, #433f46 90.21%);
726
- border-radius: 27rpx;
727
- color: #e7caad;
728
- font-size: 24rpx;
729
- font-weight: 400;
730
- margin-left: 40rpx;
731
- display: flex;
732
- align-items: center;
733
- .button-icon {
734
- display: block;
735
- width: 24rpx;
736
- height: 24rpx;
737
- font-size: 0;
738
- margin-left: 4px;
739
- }
740
- }
741
- }
742
- }
743
- .star-item {
744
- width: 100%;
745
- }
746
- }
747
- }
748
- </style>
1
+ <template>
2
+ <div class="account-view">
3
+ <div class="scroll-content">
4
+ <page-header
5
+ color-mode="dark"
6
+ title="我的账户"
7
+ :class="{ 'with-background': scrolled > 0 }"
8
+ @close="onPageHeaderClose"
9
+ />
10
+ <div class="row jusify-right">
11
+ <div class="small-bean-button" @click="onSecondBalanceButtonClick">
12
+ <label>收支明细</label>
13
+ </div>
14
+ </div>
15
+ <div class="balance">
16
+ <div class="bean-box spa-between">
17
+ <div class="bean-img">
18
+ <img
19
+ class="bean-icon"
20
+ src="https://cdn.ddjf.com/static/images/bpms-workBench/gold-bean.png"
21
+ />
22
+ <div class="bean-tag tag">云豆</div>
23
+ </div>
24
+ <div class="rule" @click="rulesPopupOpen = true">规则说明</div>
25
+ </div>
26
+ <div class="bean-counts spa-between">
27
+ <div class="counts number">{{ balance.total || 0 }}</div>
28
+ <div class="pay" @click="gotoRecharge">云豆充值</div>
29
+ </div>
30
+ </div>
31
+ <Tip />
32
+ <div class="rights-card" v-if="balance.privileges.corporateStar">
33
+ <div class="title">企明星权益</div>
34
+ <div class="list">
35
+ <div
36
+ class="item star-item"
37
+ v-for="(item, index) in balance.privileges.corporateStar"
38
+ :key="index"
39
+ >
40
+ <div class="item-count">
41
+ <span>{{ item.count }}</span
42
+ ><span>{{ item.unit }}</span>
43
+ </div>
44
+ <div class="item-title">
45
+ <div>{{ item.title }}</div>
46
+ <div class="item-title-button" v-if="item.id">
47
+ <div @click="gotoTrade(item)">超值换购</div>
48
+ <img
49
+ class="button-icon"
50
+ src="https://cdn.ddjf.com/static/images/bpms-workBench/button-hg.svg"
51
+ />
52
+ </div>
53
+ </div>
54
+ </div>
55
+ </div>
56
+ </div>
57
+ <div class="rights-card" v-if="balance.privileges.aiapprove">
58
+ <div class="title">AI审批权益</div>
59
+ <div class="list">
60
+ <div class="item" v-for="(item, index) in balance.privileges.aiapprove" :key="index">
61
+ <div class="item-count">{{ item.count }}{{ item.unit }}</div>
62
+ <div class="item-title">{{ item.title }}</div>
63
+ </div>
64
+ </div>
65
+ </div>
66
+ </div>
67
+ </div>
68
+ <nut-popup
69
+ pop-class="consumption-rules-popup"
70
+ v-model:visible="rulesPopupOpen"
71
+ :close-on-click-overlay="false"
72
+ >
73
+ <consumption-rules @complete="rulesPopupOpen = false" />
74
+ </nut-popup>
75
+ <nut-popup
76
+ position="bottom"
77
+ :style="{ height: '40%' }"
78
+ round
79
+ :close-on-click-overlay="true"
80
+ v-model:visible="datePickerOpen"
81
+ >
82
+ <date-filter
83
+ v-if="datePickerOpen"
84
+ :from="filtering.dateFrom"
85
+ :to="filtering.dateTo"
86
+ @reset="onDateReset"
87
+ @complete="onDateFilterComplete"
88
+ />
89
+ </nut-popup>
90
+ <nut-popup
91
+ position="bottom"
92
+ :style="{ height: '75%' }"
93
+ round
94
+ :close-on-click-overlay="true"
95
+ v-model:visible="filterOpen"
96
+ >
97
+ <consumption-filter
98
+ :modelValue="[
99
+ filtering.账户类型,
100
+ filtering.收入还是支出,
101
+ filtering.交易类型,
102
+ filtering.权益类目,
103
+ ]"
104
+ @complete="onFilterComplete"
105
+ />
106
+ </nut-popup>
107
+ <app-drawer v-model="secondBalanceOpen" title="收支明细">
108
+ <div class="operation-box">
109
+ <div class="operation-title spa-between" :class="{ 'with-shadow': scrolled > 0 }">
110
+ <div class="search-time">
111
+ <div class="title">收支明细</div>
112
+ <div class="time" @click="openDateFilter" v-show="filtering.dateFrom">
113
+ <div class="text number">{{ dateRangeDisplay }}</div>
114
+ <img
115
+ style="margin-top: -2px"
116
+ class="time-icon"
117
+ src="https://cdn.ddjf.com/static/images/bpms-workBench/clound-bean-down.png"
118
+ />
119
+ </div>
120
+ </div>
121
+ <div class="search" @click="filterOpen = true">
122
+ <span class="text">筛选</span>
123
+ <img
124
+ class="time-icon"
125
+ src="https://cdn.ddjf.com/static/images/bpms-workBench/clound-bean-select-icon.png"
126
+ />
127
+ </div>
128
+ </div>
129
+ <div class="operation-list">
130
+ <scroll-view
131
+ class="operation-scroll"
132
+ :scroll-y="true"
133
+ @scroll="onScroll"
134
+ :lower-threshold="50"
135
+ @scrolltolower="onReachBottom"
136
+ >
137
+ <div class="box" v-if="consumptionGroups.length > 0">
138
+ <div class="box-detail" v-for="(item, index) in consumptionGroups" :key="index">
139
+ <div class="title number">{{ item.date }}</div>
140
+ <div class="item" v-for="(it, i) in item.consumptions" :key="i">
141
+ <div class="item-type">
142
+ {{ it.交易类型 }}
143
+ </div>
144
+ <div class="item-detail">
145
+ <div class="item-info spa-between">
146
+ <div>
147
+ <div class="item-info-type">
148
+ {{ it.账户类型 }}
149
+ </div>
150
+ <div class="item-info-title">{{ it.title }}</div>
151
+ </div>
152
+ <div class="item-info-amount number">
153
+ {{ it.收入还是支出 == '支出' ? '-' : '+' }}{{ it.amount }}
154
+ </div>
155
+ </div>
156
+ <div class="item-detail-remark">{{ it.description }}</div>
157
+ </div>
158
+ </div>
159
+ </div>
160
+ <div class="box-not-text" v-if="reachedLastPage">没有更多了</div>
161
+ </div>
162
+ <empty-view v-else></empty-view>
163
+ </scroll-view>
164
+ </div>
165
+ </div>
166
+ </app-drawer>
167
+ </template>
168
+
169
+ <script lang="ts" setup>
170
+ import Taro, { useDidShow } from '@tarojs/taro'
171
+ import { computed, onMounted, reactive, ref, watch } from 'vue'
172
+ import { endpoints, useHttp } from '../api'
173
+ import { 账户流水筛选项, Balance, ConsumptionGroup, 账户流水 } from '../types'
174
+ import ConsumptionFilter from './ConsumptionFilter.vue'
175
+ import DateFilter from './DateFilter.vue'
176
+ import ConsumptionRules from './ConsumptionRules.vue'
177
+ import { AppDrawer, PageHeader, WithPaging } from '../../shared'
178
+ import dayjs from 'dayjs'
179
+ import EmptyView from '../../shared/components/EmptyView.vue'
180
+ import Tip from './Tip.vue'
181
+ import groupBy from 'lodash/groupBy'
182
+
183
+ const refreshing = ref(false)
184
+
185
+ type AccountViewProps = {
186
+ app: string
187
+ }
188
+ const props = withDefaults(defineProps<AccountViewProps>(), {
189
+ app: '',
190
+ })
191
+
192
+ const emit = defineEmits(['recharge', 'trade'])
193
+
194
+ const filterOpen = ref<boolean>(false)
195
+
196
+ // 规则说明弹窗
197
+ const rulesPopupOpen = ref<boolean>(false)
198
+
199
+ const filtering = reactive<账户流水筛选项>({
200
+ 账户类型: '全部',
201
+ 收入还是支出: '全部',
202
+ 交易类型: '全部',
203
+ 权益类目: '',
204
+ dateFrom: '',
205
+ dateTo: '',
206
+ page: 1,
207
+ pageSize: 20,
208
+ })
209
+
210
+ const reachedLastPage = ref<boolean>(false)
211
+
212
+ /**
213
+ * 全新搜索
214
+ * 重置某些查询条件
215
+ */
216
+ function restartSearch() {
217
+ // 从第一页开始
218
+ filtering.page = 1
219
+ consumptionGroups.value = []
220
+ loadConsumptions()
221
+ }
222
+
223
+ // 时间筛选
224
+ const datePickerOpen = ref<boolean>(false)
225
+
226
+ function openDateFilter() {
227
+ datePickerOpen.value = true
228
+ }
229
+
230
+ const dateRangeDisplay = computed(() => {
231
+ let startTime = (filtering.dateFrom || '').replace(/-/g, '.').substring(2)
232
+ let endTime = (filtering.dateTo || '').replace(/-/g, '.').substring(2)
233
+ return startTime + ' - ' + endTime
234
+ })
235
+
236
+ const consumptionGroups = ref<ConsumptionGroup[]>([])
237
+
238
+ /**
239
+ * 余额数据
240
+ */
241
+ const balance = ref<Balance>({
242
+ total: 0,
243
+ privileges: {},
244
+ })
245
+
246
+ useDidShow(() => {
247
+ Taro.setNavigationBarTitle({
248
+ title: '我的账户',
249
+ })
250
+ loadBalance()
251
+ })
252
+
253
+ function groupDataByDate(data: 账户流水[]): ConsumptionGroup[] {
254
+ // 按照日期分组
255
+ const groups: ConsumptionGroup[] = consumptionGroups.value || []
256
+ // [
257
+ // date: '2023-10-1',
258
+ // consumptions: []
259
+ // ]
260
+ // 组装成按日期分组
261
+ data.forEach((d) => {
262
+ const date = dayjs(d.time).format('YYYY-MM-DD')
263
+ let group: ConsumptionGroup | undefined = groups.find((g) => g.date === date)
264
+ if (!group) {
265
+ group = {
266
+ date,
267
+ consumptions: [],
268
+ }
269
+ groups.push(group)
270
+ }
271
+ group.consumptions.push(d)
272
+ })
273
+ groups.sort((a, b) => (a.date > b.date ? -1 : 1))
274
+ return groups
275
+ }
276
+
277
+ /**
278
+ * 请求账户流水
279
+ * @param append 搜索结果累加 上拉分页时
280
+ */
281
+ async function loadConsumptions(append: boolean = false) {
282
+ if (!append) {
283
+ consumptionGroups.value = []
284
+ }
285
+ const $http = useHttp()
286
+ Taro.showLoading({
287
+ title: `加载中...`,
288
+ mask: true,
289
+ })
290
+ $http
291
+ .get<WithPaging<账户流水[]>>(endpoints.获取账户流水, {
292
+ app: props.app,
293
+ ...filtering,
294
+ })
295
+ .then((response) => {
296
+ consumptionGroups.value = groupDataByDate(response.data)
297
+ if (filtering.page >= response.totalPages) {
298
+ filtering.page = response.totalPages
299
+ reachedLastPage.value = true
300
+ } else {
301
+ reachedLastPage.value = false
302
+ }
303
+ Taro.hideLoading()
304
+ })
305
+ }
306
+
307
+ /**
308
+ * 获取账户余额明细
309
+ */
310
+ async function loadBalance() {
311
+ const $http = useHttp()
312
+ $http
313
+ .get<Balance>(endpoints.获取余额明细, {
314
+ app: props.app,
315
+ })
316
+ .then((data) => {
317
+ const { privileges, total } = data
318
+ const filterData = {
319
+ total,
320
+ privileges: groupBy(privileges, 'appCode'),
321
+ }
322
+ balance.value = filterData
323
+ })
324
+ }
325
+
326
+ const onFilterComplete = (value) => {
327
+ filtering.账户类型 = value[0]
328
+ filtering.收入还是支出 = value[1]
329
+ filtering.交易类型 = value[2]
330
+ filtering.权益类目 = value[3]
331
+ filterOpen.value = false
332
+ restartSearch()
333
+ }
334
+
335
+ const onDateFilterComplete = (value) => {
336
+ filtering.dateFrom = value.from
337
+ filtering.dateTo = value.to
338
+ datePickerOpen.value = false
339
+ restartSearch()
340
+ }
341
+
342
+ function gotoRecharge() {
343
+ emit('recharge')
344
+ }
345
+
346
+ function gotoTrade(item: any) {
347
+ emit('trade', item)
348
+ }
349
+
350
+ function loadNextPage() {
351
+ filtering.page++
352
+ loadConsumptions(true)
353
+ }
354
+
355
+ // 以下这一大段处理下拉刷新、上滑分页以及弹窗与页面滑动的逻辑
356
+ const scrolled = ref<number>(0)
357
+
358
+ /**
359
+ * 记录 scroll-view 滚动的位置
360
+ */
361
+ const onScroll = (e) => {
362
+ const { scrollTop } = e
363
+ scrolled.value = scrollTop
364
+ }
365
+
366
+ /**
367
+ * 下拉刷新 pull down refresh
368
+ */
369
+ const onPullDownRefresh = () => {
370
+ refreshing.value = true
371
+ // 重新请求余额数据
372
+ loadBalance()
373
+ setTimeout(() => {
374
+ refreshing.value = false
375
+ }, 500)
376
+ }
377
+
378
+ /**
379
+ * 滑动到底部请求下一页并合并查询结果
380
+ */
381
+ const onReachBottom = () => {
382
+ console.log('到底了')
383
+ if (reachedLastPage.value) {
384
+ return false
385
+ }
386
+ loadNextPage()
387
+ }
388
+
389
+ /**
390
+ * 浮窗弹出时不允许
391
+ * 1. 下拉刷新
392
+ * 2. 上滑分页
393
+ */
394
+ const isAnyPopupOpen = computed(() => {
395
+ return rulesPopupOpen.value || datePickerOpen.value || filterOpen.value || secondBalanceOpen.value
396
+ })
397
+
398
+ const secondBalanceOpen = ref<boolean>(false)
399
+
400
+ function onSecondBalanceButtonClick() {
401
+ secondBalanceOpen.value = true
402
+ loadConsumptions()
403
+ }
404
+
405
+ watch(secondBalanceOpen, () => {
406
+ Taro.setNavigationBarColor({
407
+ frontColor: secondBalanceOpen.value ? '#000000' : '#ffffff',
408
+ backgroundColor: '#ffffff',
409
+ animation: {
410
+ duration: 400,
411
+ timingFunc: 'easeIn',
412
+ },
413
+ })
414
+ })
415
+
416
+ function onDateReset() {
417
+ resetDateRange()
418
+ }
419
+
420
+ /**
421
+ * 充值筛选的起止时间
422
+ */
423
+ function resetDateRange() {
424
+ // 筛选的起止时间
425
+ filtering.dateTo = dayjs().format('YYYY-MM-DD')
426
+ filtering.dateFrom = dayjs().add(-3, 'M').format('YYYY-MM-DD')
427
+ }
428
+
429
+ function onPageHeaderClose() {
430
+ Taro.navigateBack({
431
+ delta: 1,
432
+ })
433
+ }
434
+
435
+ onMounted(() => {
436
+ resetDateRange()
437
+ })
438
+ </script>
439
+
440
+ <style lang="scss">
441
+ .account-view {
442
+ height: 100vh;
443
+ .scroll-content {
444
+ background-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNzUwIiBoZWlnaHQ9IjQwNiIgdmlld0JveD0iMCAwIDc1MCA0MDYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik0zODIuNSA0MDcuNUM1NzcuMzU3IDQwNy41IDc1My43ODggMzgxLjI0OCA4ODEuNTIxIDMzOC43OTFDOTQ1LjM4NiAzMTcuNTYzIDk5Ny4xMDQgMjkyLjI3NiAxMDMyLjg3IDI2NC4xNzFDMTA2OC42MiAyMzYuMDcxIDEwODguNSAyMDUuMDk3IDEwODguNSAxNzIuNUMxMDg4LjUgMTM5LjkwMyAxMDY4LjYyIDEwOC45MjkgMTAzMi44NyA4MC44Mjg5Qzk5Ny4xMDQgNTIuNzI0NSA5NDUuMzg2IDI3LjQzNjcgODgxLjUyMSA2LjIwODk4Qzc1My43ODggLTM2LjI0ODIgNTc3LjM1NyAtNjIuNSAzODIuNSAtNjIuNUMxODcuNjQzIC02Mi41IDExLjIxMjIgLTM2LjI0ODIgLTExNi41MjIgNi4yMDg5OEMtMTgwLjM4NiAyNy40MzY3IC0yMzIuMTA0IDUyLjcyNDUgLTI2Ny44NjcgODAuODI4OUMtMzAzLjYyNCAxMDguOTI5IC0zMjMuNSAxMzkuOTAzIC0zMjMuNSAxNzIuNUMtMzIzLjUgMjA1LjA5NyAtMzAzLjYyNCAyMzYuMDcxIC0yNjcuODY3IDI2NC4xNzFDLTIzMi4xMDQgMjkyLjI3NiAtMTgwLjM4NiAzMTcuNTYzIC0xMTYuNTIyIDMzOC43OTFDMTEuMjEyMiAzODEuMjQ4IDE4Ny42NDMgNDA3LjUgMzgyLjUgNDA3LjVaIiBmaWxsPSIjM0IzOTNDIiBzdHJva2U9ImJsYWNrIi8+Cjwvc3ZnPgo=');
445
+ background-position: center -200px;
446
+ background-repeat: no-repeat;
447
+ }
448
+ .page-header {
449
+ background-color: #3b393c;
450
+ position: sticky;
451
+ top: 0;
452
+ z-index: 1;
453
+ transition: background-color 0.3s;
454
+ &.with-background {
455
+ background-color: #3b393c;
456
+ }
457
+ }
458
+ .spa-between {
459
+ display: flex;
460
+ justify-content: space-between;
461
+ align-items: center;
462
+ }
463
+ .row {
464
+ display: flex;
465
+ flex-direction: row;
466
+ margin: 10px 15px;
467
+ }
468
+ .jusify-right {
469
+ justify-content: flex-end;
470
+ }
471
+ .small-bean-button {
472
+ height: 22px;
473
+ border-radius: 11px;
474
+ border: 1px solid #ffffff10;
475
+ background-color: #ffffff18;
476
+ display: inline-block;
477
+ padding: 0 10px;
478
+ line-height: 22px;
479
+ color: #e7caad;
480
+ font-size: 12px;
481
+ label {
482
+ background-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTEwIDZMMTYgMTJMMTAgMThWNloiIGZpbGw9IiNFN0NBQUQiIHN0cm9rZT0iI0U3Q0FBRCIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+Cjwvc3ZnPgo=');
483
+ padding-right: 18px;
484
+ background-repeat: no-repeat;
485
+ background-position: right center;
486
+ background-size: 16px;
487
+ }
488
+ }
489
+ .balance {
490
+ border-radius: 15px;
491
+ background: linear-gradient(105deg, #f4e2ce 1.88%, #debb9b 98.18%);
492
+ box-shadow: 0px -10px 9px -3px rgba(0, 0, 0, 0.33);
493
+ height: 112px;
494
+ padding: 20px;
495
+ margin: 0 15px;
496
+ box-sizing: border-box;
497
+ font-size: 10px;
498
+ .bean-img {
499
+ display: flex;
500
+ justify-content: flex-start;
501
+ align-items: center;
502
+ .bean-icon {
503
+ display: block;
504
+ font-size: 0;
505
+ width: 20px;
506
+ height: 20px;
507
+ margin-right: 4px;
508
+ }
509
+ .bean-tag {
510
+ color: #353535;
511
+ height: 15px;
512
+ line-height: 15px;
513
+ border-radius: 30px 50px 50px 0px;
514
+ background: rgba(#ff8320, 0.3);
515
+ padding-left: 5px;
516
+ padding-right: 10px;
517
+ }
518
+ .tag {
519
+ background: #ff8320;
520
+ color: #fff;
521
+ }
522
+ }
523
+ .rule {
524
+ color: #353535;
525
+ opacity: 0.5;
526
+ }
527
+ }
528
+ .bean-counts {
529
+ margin-top: 12px;
530
+ .counts {
531
+ color: #3d3835;
532
+ font-size: 32px;
533
+ font-weight: 700;
534
+ }
535
+ .pay {
536
+ padding: 0 20px;
537
+ height: 32px;
538
+ line-height: 32px;
539
+ background: linear-gradient(187.18deg, #353535 10.04%, #433f46 90.21%);
540
+ border-radius: 16px;
541
+ color: #e7caad;
542
+ font-size: 15px;
543
+ font-weight: 500;
544
+ }
545
+ }
546
+ }
547
+ .consumption-rules-popup {
548
+ border-radius: 16px;
549
+ width: 70%;
550
+ max-height: 80%;
551
+ padding: 24px;
552
+ }
553
+
554
+ .operation-box {
555
+ width: 100vw;
556
+ overflow: hidden;
557
+ display: flex;
558
+ flex-direction: column;
559
+ height: calc(100vh - 87px);
560
+ .operation-title {
561
+ padding: 10px 15px;
562
+ background: #fff;
563
+ transition: box-shadow 0.3s;
564
+
565
+ &.with-shadow {
566
+ box-shadow: 0px 1px 10px 0px #99999933;
567
+ }
568
+ .text {
569
+ color: #353535;
570
+ font-size: 12px;
571
+ opacity: 0.5;
572
+ }
573
+ .time-icon {
574
+ display: block;
575
+ font-size: 0;
576
+ width: 12px;
577
+ height: 12px;
578
+ margin-left: 4px;
579
+ }
580
+ .search-time {
581
+ display: flex;
582
+ align-items: center;
583
+ .title {
584
+ color: #000;
585
+ font-weight: 500;
586
+ font-size: 17px;
587
+ margin-right: 10px;
588
+ }
589
+ .time {
590
+ height: 22px;
591
+ padding-right: 5px;
592
+ flex: 1;
593
+ display: flex;
594
+ align-items: center;
595
+ }
596
+ }
597
+ .search {
598
+ display: flex;
599
+ align-items: center;
600
+ height: 22px;
601
+ padding-left: 5px;
602
+ }
603
+ }
604
+ .spa-between {
605
+ display: flex;
606
+ justify-content: space-between;
607
+ align-items: center;
608
+ }
609
+ .operation-list {
610
+ flex: 1;
611
+ width: 100%;
612
+ .operation-scroll {
613
+ height: calc(100vh - 130px);
614
+ padding: 0 15px;
615
+ box-sizing: border-box;
616
+ }
617
+ .box {
618
+ &-detail {
619
+ .title {
620
+ line-height: 22px;
621
+ font-weight: 700;
622
+ font-size: 12px;
623
+ color: #000;
624
+ opacity: 0.5;
625
+ margin-bottom: 10px;
626
+ }
627
+ .item {
628
+ border-radius: 5px;
629
+ background: rgba(255, 255, 255, 0.5);
630
+ box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.05);
631
+ border-radius: 5px;
632
+ padding: 12px 10px;
633
+ display: flex;
634
+ margin-bottom: 10px;
635
+ &-type {
636
+ background: linear-gradient(113.95deg, #f4e2ce 1.2%, #debb9b 77.63%);
637
+ width: 30px;
638
+ height: 30px;
639
+ border-radius: 15px;
640
+ margin-right: 15px;
641
+ line-height: 30px;
642
+ font-size: 10px;
643
+ font-weight: 500;
644
+ color: #000;
645
+ text-align: center;
646
+ }
647
+ &-detail {
648
+ flex: 1;
649
+ .item-info {
650
+ padding: 0;
651
+ font-size: 14px;
652
+ font-weight: 700;
653
+ color: #000;
654
+ &-type {
655
+ line-height: 15px;
656
+ }
657
+ &-title {
658
+ font-weight: 400;
659
+ opacity: 0.8;
660
+ font-size: 11px;
661
+ }
662
+ &-amount {
663
+ color: #9e7b5a;
664
+ }
665
+ }
666
+ &-remark {
667
+ opacity: 0.3;
668
+ line-height: 15px;
669
+ font-size: 10px;
670
+ }
671
+ }
672
+ }
673
+ }
674
+ &-not-text {
675
+ opacity: 0.4;
676
+ color: #353535;
677
+ font-size: 12px;
678
+ line-height: 20px;
679
+ margin: 0 auto 40px;
680
+ text-align: center;
681
+ }
682
+ }
683
+ }
684
+ }
685
+ .rights-card {
686
+ margin: 10px 15px 0px;
687
+ background: #fff8f3;
688
+ border-radius: 5px;
689
+ box-shadow: 0px 5px 18.9px 2px #0000000d;
690
+ padding: 15px 20px;
691
+ .title {
692
+ height: 25px;
693
+ line-height: 25px;
694
+ background: rgba($color: #ff8320, $alpha: 0.3);
695
+ border-radius: 20px 50px 50px 0px;
696
+ color: #353535;
697
+ font-size: 12px;
698
+ display: inline-block;
699
+ padding-left: 5px;
700
+ padding-right: 12px;
701
+ }
702
+ .list {
703
+ display: flex;
704
+ flex-wrap: wrap;
705
+ .item {
706
+ width: 50%;
707
+ margin-top: 15px;
708
+ .item-count {
709
+ font-size: 20px;
710
+ color: #353535;
711
+ font-weight: 700;
712
+ line-height: 23px;
713
+ }
714
+ .item-title {
715
+ line-height: 23px;
716
+ color: #987356;
717
+ font-size: 11px;
718
+ display: flex;
719
+ align-items: center;
720
+ .item-title-button {
721
+ padding: 0 27rpx;
722
+ height: 54rpx;
723
+ line-height: 54rpx;
724
+ background: -webkit-linear-gradient(262.82deg, #353535 10.04%, #433f46 90.21%);
725
+ background: linear-gradient(187.18deg, #353535 10.04%, #433f46 90.21%);
726
+ border-radius: 27rpx;
727
+ color: #e7caad;
728
+ font-size: 24rpx;
729
+ font-weight: 400;
730
+ margin-left: 40rpx;
731
+ display: flex;
732
+ align-items: center;
733
+ .button-icon {
734
+ display: block;
735
+ width: 24rpx;
736
+ height: 24rpx;
737
+ font-size: 0;
738
+ margin-left: 4px;
739
+ }
740
+ }
741
+ }
742
+ }
743
+ .star-item {
744
+ width: 100%;
745
+ }
746
+ }
747
+ }
748
+ </style>