wupengfei
2025-05-28 6b333bea26b7599a3cf39cfa88333572ad66261e
fix: bug
12个文件已添加
9个文件已修改
1623 ■■■■■ 已修改文件
pnpm-lock.yaml 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Reward/RewardInfoCard.vue 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Reward/RewardInfoCardContentItem.vue 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Reward/RewardInfoCardList.vue 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Reward/RewardInfoCardPrice.vue 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/constants/customerConsume.ts 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/constants/index.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.ts 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/services/api/ParkBountyApply.ts 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/services/api/User.ts 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/services/api/index.ts 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/services/api/typings.d.ts 161 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/common/common.ts 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/common/file.ts 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/BalanceManage/BalanceManage.vue 188 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/BalanceManage/components/ConsumeRecordView.vue 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/BalanceManage/components/RechargeRecordView.vue 143 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/BalanceManage/components/RewardGrantRecordView.vue 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/BalanceManage/components/RewardRechargeDialog.vue 266 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/FileManage/FileManage.vue 161 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/Home/Home.vue 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pnpm-lock.yaml
@@ -21,13 +21,13 @@
    version: 1.0.3(@bole-12333/chat-kit@1.0.1)(@tiptap/core@2.1.13)(@tiptap/extension-document@2.1.13)(@tiptap/extension-image@2.1.13)(@tiptap/extension-mention@2.1.13)(@tiptap/extension-paragraph@2.1.13)(@tiptap/extension-placeholder@2.1.13)(@tiptap/extension-text@2.1.13)(@tiptap/pm@2.1.13)(@tiptap/suggestion@2.1.13)(dayjs@1.11.6)(vue@3.5.11)
  '@bole-core/components':
    specifier: latest
    version: 1.3.5(@bole-core/core@1.0.2)(@bole-core/sass-utils@0.0.1)(@element-plus/icons-vue@2.3.1)(@tanstack/vue-query@4.37.1)(@videojs-player/vue@1.0.0)(@vueuse/core@11.1.0)(@vueuse/shared@11.1.0)(dayjs@1.11.6)(element-plus@2.9.1)(lodash@4.17.21)(mitt@3.0.0)(senin-help@1.0.5)(senin-vue@1.0.4)(video.js@7.20.3)(vue-component-type-helpers@2.1.6)(vue@3.5.11)
    version: 1.3.9(@bole-core/core@1.0.5)(@bole-core/sass-utils@0.0.1)(@element-plus/icons-vue@2.3.1)(@tanstack/vue-query@4.37.1)(@videojs-player/vue@1.0.0)(@vueuse/core@11.1.0)(@vueuse/shared@11.1.0)(dayjs@1.11.6)(element-plus@2.9.1)(lodash@4.17.21)(mitt@3.0.0)(senin-help@1.0.5)(senin-vue@1.0.4)(video.js@7.20.3)(vue-component-type-helpers@2.1.6)(vue@3.5.11)
  '@bole-core/core':
    specifier: latest
    version: 1.0.2(@ywwlmm/openapi@0.0.3)(ali-oss@6.21.0)(axios@1.7.7)(dayjs@1.11.6)(element-plus@2.9.1)(file-saver@2.0.5)(jszip@3.10.1)(lodash@4.17.21)(senior-request@1.0.1)(vue-router@4.4.5)(vue@3.5.11)(xlsx-style@0.8.13)(xlsx@0.18.5)
    version: 1.0.5(@ywwlmm/openapi@0.0.3)(ali-oss@6.21.0)(axios@1.7.7)(dayjs@1.11.6)(element-plus@2.9.1)(file-saver@2.0.5)(jszip@3.10.1)(lodash@4.17.21)(senior-request@1.0.1)(vue-router@4.4.5)(vue@3.5.11)(xlsx-style@0.8.13)(xlsx@0.18.5)
  '@bole-core/pretty-layout':
    specifier: latest
    version: 1.2.0(@bole-core/core@1.0.2)(@bole-core/sass-utils@0.0.1)(@element-plus/icons-vue@2.3.1)(@vueuse/core@11.1.0)(element-plus@2.9.1)(fuse.js@6.6.2)(lottie-web@5.10.2)(path-browserify@1.0.1)(path-to-regexp@6.2.1)(pinia@2.2.4)(vue-router@4.4.5)(vue@3.5.11)
    version: 1.2.0(@bole-core/core@1.0.5)(@bole-core/sass-utils@0.0.1)(@element-plus/icons-vue@2.3.1)(@vueuse/core@11.1.0)(element-plus@2.9.1)(fuse.js@6.6.2)(lottie-web@5.10.2)(path-browserify@1.0.1)(path-to-regexp@6.2.1)(pinia@2.2.4)(vue-router@4.4.5)(vue@3.5.11)
  '@bole-core/sass-utils':
    specifier: latest
    version: 0.0.1(element-plus@2.9.1)
@@ -1889,8 +1889,8 @@
      vue: 3.5.11(typescript@4.8.4)
    dev: false
  /@bole-core/components@1.3.5(@bole-core/core@1.0.2)(@bole-core/sass-utils@0.0.1)(@element-plus/icons-vue@2.3.1)(@tanstack/vue-query@4.37.1)(@videojs-player/vue@1.0.0)(@vueuse/core@11.1.0)(@vueuse/shared@11.1.0)(dayjs@1.11.6)(element-plus@2.9.1)(lodash@4.17.21)(mitt@3.0.0)(senin-help@1.0.5)(senin-vue@1.0.4)(video.js@7.20.3)(vue-component-type-helpers@2.1.6)(vue@3.5.11):
    resolution: {integrity: sha512-HSl3vo6hYNJwQx46p4FSeU46dPfAHgdxOcXOyN9sFdspgvUOiSktTBUBV+fqMSx8aA5ESJNVBcGtiRsY2LkQlg==}
  /@bole-core/components@1.3.9(@bole-core/core@1.0.5)(@bole-core/sass-utils@0.0.1)(@element-plus/icons-vue@2.3.1)(@tanstack/vue-query@4.37.1)(@videojs-player/vue@1.0.0)(@vueuse/core@11.1.0)(@vueuse/shared@11.1.0)(dayjs@1.11.6)(element-plus@2.9.1)(lodash@4.17.21)(mitt@3.0.0)(senin-help@1.0.5)(senin-vue@1.0.4)(video.js@7.20.3)(vue-component-type-helpers@2.1.6)(vue@3.5.11):
    resolution: {integrity: sha512-9lCPb9fKp0fUnC6YzMaGxDXiqcgYLMg/TuLNFOuClW1uuggEQfNPQxwSdWS4C/SynXuLVPPRArQUy08AheVNRw==}
    peerDependencies:
      '@bole-core/core': '*'
      '@bole-core/sass-utils': '*'
@@ -1909,7 +1909,7 @@
      vue: 3.5.11
      vue-component-type-helpers: ^2.1.6
    dependencies:
      '@bole-core/core': 1.0.2(@ywwlmm/openapi@0.0.3)(ali-oss@6.21.0)(axios@1.7.7)(dayjs@1.11.6)(element-plus@2.9.1)(file-saver@2.0.5)(jszip@3.10.1)(lodash@4.17.21)(senior-request@1.0.1)(vue-router@4.4.5)(vue@3.5.11)(xlsx-style@0.8.13)(xlsx@0.18.5)
      '@bole-core/core': 1.0.5(@ywwlmm/openapi@0.0.3)(ali-oss@6.21.0)(axios@1.7.7)(dayjs@1.11.6)(element-plus@2.9.1)(file-saver@2.0.5)(jszip@3.10.1)(lodash@4.17.21)(senior-request@1.0.1)(vue-router@4.4.5)(vue@3.5.11)(xlsx-style@0.8.13)(xlsx@0.18.5)
      '@bole-core/sass-utils': 0.0.1(element-plus@2.9.1)
      '@element-plus/icons-vue': 2.3.1(vue@3.5.11)
      '@tanstack/vue-query': 4.37.1(vue@3.5.11)
@@ -1927,8 +1927,8 @@
      vue-component-type-helpers: 2.1.6
    dev: false
  /@bole-core/core@1.0.2(@ywwlmm/openapi@0.0.3)(ali-oss@6.21.0)(axios@1.7.7)(dayjs@1.11.6)(element-plus@2.9.1)(file-saver@2.0.5)(jszip@3.10.1)(lodash@4.17.21)(senior-request@1.0.1)(vue-router@4.4.5)(vue@3.5.11)(xlsx-style@0.8.13)(xlsx@0.18.5):
    resolution: {integrity: sha512-v5XqgmuaFe1l5tBe9CHytW8UhqI8rh2m8R2J7F2F46XZy5wh7vGfiHshuaooTbfK6ex9A06jcepIWwDOklLs+A==}
  /@bole-core/core@1.0.5(@ywwlmm/openapi@0.0.3)(ali-oss@6.21.0)(axios@1.7.7)(dayjs@1.11.6)(element-plus@2.9.1)(file-saver@2.0.5)(jszip@3.10.1)(lodash@4.17.21)(senior-request@1.0.1)(vue-router@4.4.5)(vue@3.5.11)(xlsx-style@0.8.13)(xlsx@0.18.5):
    resolution: {integrity: sha512-qrLiGGb6Ei6xDLegubxswH/CSvcE2zRWslBA069KeZJTmjddLGQ/V3wqcMcRTLUj8bCSERguUB/BXM+TtQ25bg==}
    peerDependencies:
      '@ywwlmm/openapi': '*'
      ali-oss: '*'
@@ -1959,7 +1959,7 @@
      xlsx-style: 0.8.13(patch_hash=6ixwdxwh5a3amidwu6vee22jpi)
    dev: false
  /@bole-core/pretty-layout@1.2.0(@bole-core/core@1.0.2)(@bole-core/sass-utils@0.0.1)(@element-plus/icons-vue@2.3.1)(@vueuse/core@11.1.0)(element-plus@2.9.1)(fuse.js@6.6.2)(lottie-web@5.10.2)(path-browserify@1.0.1)(path-to-regexp@6.2.1)(pinia@2.2.4)(vue-router@4.4.5)(vue@3.5.11):
  /@bole-core/pretty-layout@1.2.0(@bole-core/core@1.0.5)(@bole-core/sass-utils@0.0.1)(@element-plus/icons-vue@2.3.1)(@vueuse/core@11.1.0)(element-plus@2.9.1)(fuse.js@6.6.2)(lottie-web@5.10.2)(path-browserify@1.0.1)(path-to-regexp@6.2.1)(pinia@2.2.4)(vue-router@4.4.5)(vue@3.5.11):
    resolution: {integrity: sha512-LvneMdMfGj6dpFR7voQd2F+v02P4zYHGP3BAWKcrTj8BMeWXRt08Sz7qBrhq1F8lidjqi7TCttQbx2cEV1qBEA==}
    peerDependencies:
      '@bole-core/core': '*'
@@ -1975,7 +1975,7 @@
      vue: 3.5.11
      vue-router: '*'
    dependencies:
      '@bole-core/core': 1.0.2(@ywwlmm/openapi@0.0.3)(ali-oss@6.21.0)(axios@1.7.7)(dayjs@1.11.6)(element-plus@2.9.1)(file-saver@2.0.5)(jszip@3.10.1)(lodash@4.17.21)(senior-request@1.0.1)(vue-router@4.4.5)(vue@3.5.11)(xlsx-style@0.8.13)(xlsx@0.18.5)
      '@bole-core/core': 1.0.5(@ywwlmm/openapi@0.0.3)(ali-oss@6.21.0)(axios@1.7.7)(dayjs@1.11.6)(element-plus@2.9.1)(file-saver@2.0.5)(jszip@3.10.1)(lodash@4.17.21)(senior-request@1.0.1)(vue-router@4.4.5)(vue@3.5.11)(xlsx-style@0.8.13)(xlsx@0.18.5)
      '@bole-core/sass-utils': 0.0.1(element-plus@2.9.1)
      '@element-plus/icons-vue': 2.3.1(vue@3.5.11)
      '@vueuse/core': 11.1.0(vue@3.5.11)
@@ -3692,21 +3692,21 @@
      vue-demi: 0.13.11(vue@3.5.11)
    dev: false
  /@tencentcloud/call-engine-js@2.9.3:
    resolution: {integrity: sha512-UtE3K7yIS9ECuvSscm6GA6aCpuaN06A3gfzmzXmhBEVjNTkiP5eCug7hHSMIYmR0sa5ILask4+Tki2NLq+A8Cw==}
  /@tencentcloud/call-engine-js@3.0.1:
    resolution: {integrity: sha512-OafM512KDbk4b9dpVVymLvuQuoVNqp7ZQmnZVjDmC5v/bQxA1FSUaAinty+7AU1Fb1FxQwxuHKDPysQAU+9M0g==}
    dependencies:
      '@tencentcloud/chat': 3.5.3
      core-js: 3.38.1
      eventemitter3: 4.0.7
      rtc-detect: 0.0.5
      trtc-cloud-js-sdk: 2.8.7
      trtc-cloud-js-sdk: 2.9.0
      tuikit-logger: 0.0.4-beta.1
    dev: false
  /@tencentcloud/call-uikit-vue2.6@4.0.3(vue@3.5.11):
    resolution: {integrity: sha512-DgsqCK4biGp/SVgbTjxKP9rQrxzEsN6x4qSjonLjiiT7BVT9pLlx8Mwxzv+MchPE9CGSqu440Lm/98uRkVlhRQ==}
  /@tencentcloud/call-uikit-vue2.6@4.0.6(vue@3.5.11):
    resolution: {integrity: sha512-As0qoW6TncyZ0y10Ogm6KrziZGznM5DX5HBPdYfZDbDm6eIYxG13HD3K8BzUC/GRlny8Nxn4EfhSkn48StBruQ==}
    dependencies:
      '@tencentcloud/call-engine-js': 2.9.3
      '@tencentcloud/call-engine-js': 3.0.1
      '@tencentcloud/chat': 3.5.3
      '@tencentcloud/tui-core': 2.4.0
      '@vue/composition-api': 1.7.2(vue@3.5.11)
@@ -3714,10 +3714,10 @@
      - vue
    dev: false
  /@tencentcloud/call-uikit-vue2@4.0.3:
    resolution: {integrity: sha512-xfPoIW/XEl8EGo/mOYA4z8BDgz+dmH5K+BjKGdFzLNuNZ8n5YxFuwDqrJTCVAl9ydijeJ+DhO68F5jQgsdDMkw==}
  /@tencentcloud/call-uikit-vue2@4.0.6:
    resolution: {integrity: sha512-egV/rS0T77vj3+d3YpurJxynFBJ41hbBO8WPmwc5z9BD2qjM2HSKgbMR2jGF6XAxjherCK/qlaIZLQQiNIKmNQ==}
    dependencies:
      '@tencentcloud/call-engine-js': 2.9.3
      '@tencentcloud/call-engine-js': 3.0.1
      '@tencentcloud/chat': 3.5.3
      '@tencentcloud/tui-core': 2.4.0
    dev: false
@@ -3729,10 +3729,10 @@
      tuicall-engine-webrtc: 3.1.7
    dev: false
  /@tencentcloud/call-uikit-vue@4.0.3:
    resolution: {integrity: sha512-EeZ0Q3x8igY5Ny5ghSD0T6seBiE3eDf0NagThASdGnD8+k6/GwBgD5HEBH8DrLpkC65rAMxf0ylE8JZPYilPQw==}
  /@tencentcloud/call-uikit-vue@4.0.6:
    resolution: {integrity: sha512-eqcIv2QcsH1iRdpbBJu6sv73wVMzaeb4Bap5A2B+uVJrnrwEd1KwOzk8wV4gNIbtF08fK933R4OnkuCRMhoH4A==}
    dependencies:
      '@tencentcloud/call-engine-js': 2.9.3
      '@tencentcloud/call-engine-js': 3.0.1
      '@tencentcloud/chat': 3.5.3
      '@tencentcloud/tui-core': 2.4.0
    dev: false
@@ -3740,7 +3740,15 @@
  /@tencentcloud/chat-uikit-engine@2.4.2:
    resolution: {integrity: sha512-L5rr1mO98S1Uz5IndJ2QdGv++5zbSUTnGSz4x3rKx5lWXlcJzSo1RY9DxYcWTKQKos9bG2d4JIIeg4DlfGb+Pg==}
    dependencies:
      '@tencentcloud/chat': 3.5.3
      '@tencentcloud/chat': 3.5.5
      tim-profanity-filter-plugin: 1.1.0
      tim-upload-plugin: 1.4.2
    dev: false
  /@tencentcloud/chat-uikit-engine@2.4.4:
    resolution: {integrity: sha512-I26rCiRYcFMFN35Kf0QMJhphf0lBlJ7TXTAy255rJIiFzAXLBG1yV50cXbktGtBkjCjgOym8kvDUCmltPLPCRQ==}
    dependencies:
      '@tencentcloud/chat': 3.5.5
      tim-profanity-filter-plugin: 1.1.0
      tim-upload-plugin: 1.4.2
    dev: false
@@ -3758,10 +3766,10 @@
      '@tiptap/pm': ^2.0.0-beta.220
      '@tiptap/suggestion': ^2.0.0-beta.220
    dependencies:
      '@tencentcloud/call-uikit-vue': 4.0.3
      '@tencentcloud/call-uikit-vue2': 4.0.3
      '@tencentcloud/call-uikit-vue2.6': 4.0.3(vue@3.5.11)
      '@tencentcloud/chat-uikit-engine': 2.4.2
      '@tencentcloud/call-uikit-vue': 4.0.6
      '@tencentcloud/call-uikit-vue2': 4.0.6
      '@tencentcloud/call-uikit-vue2.6': 4.0.6(vue@3.5.11)
      '@tencentcloud/chat-uikit-engine': 2.4.4
      '@tencentcloud/tui-core': 2.4.0
      '@tencentcloud/tui-customer-service-plugin': 2.2.6(@tencentcloud/tui-core@2.4.0)(vue@3.5.11)
      '@tiptap/core': 2.1.13(@tiptap/pm@2.1.13)
@@ -3786,10 +3794,14 @@
    resolution: {integrity: sha512-1iC9CW+8sMcUtZXjB+97N65uyqRAV73InfKCzXtU/igxPqT8nfzfkrcjhi7JfywaKR3jGC1j8tDpsFNlLepFqQ==}
    dev: false
  /@tencentcloud/chat@3.5.5:
    resolution: {integrity: sha512-21kQw8PUns9D2nJMDv5KErnGusDAGSZ0xlFqoCO2VrpHaOXNp7diUs8yVccR/UHk+O/474zNbLe4n095UqLSVQ==}
    dev: false
  /@tencentcloud/tui-core@2.0.0:
    resolution: {integrity: sha512-KBwDFWFV+IGtAZ82uCjRi891nMQcAen0wZHdPUUTSNHaWGjYiwOAHD7yw4gC1c5DYqVYMa3tVA7bSBd/hKVZKg==}
    dependencies:
      '@tencentcloud/chat': 3.5.3
      '@tencentcloud/chat': 3.5.5
      tim-profanity-filter-plugin: 0.9.0
      tim-upload-plugin: 1.3.0
    dev: false
@@ -3797,7 +3809,7 @@
  /@tencentcloud/tui-core@2.4.0:
    resolution: {integrity: sha512-OFXHoi8CQlPtWvNRLApg0wonWbW79uoTgAqqffqWO1icx3xDYd2RZDHMzpBexOsE3CHpWokrfXrqiXkpOmYWew==}
    dependencies:
      '@tencentcloud/chat': 3.5.3
      '@tencentcloud/chat': 3.5.5
      tim-profanity-filter-plugin: 1.1.0
      tim-upload-plugin: 1.4.2
    dev: false
@@ -14854,6 +14866,12 @@
      trtc-sdk-v5: 5.8.7-beta.1
    dev: false
  /trtc-cloud-js-sdk@2.9.0:
    resolution: {integrity: sha512-m2gufzg1yqnyPUanLJJXgyPzenRSyH1stK16EDrDINQo1FCPeCmY0kDARF1NiLZJLKm9lUP5DQsrUUKn4LYkPg==}
    dependencies:
      trtc-sdk-v5: 5.9.1
    dev: false
  /trtc-js-sdk@4.15.19:
    resolution: {integrity: sha512-FFVvaji65d7+aq5o4hE+5lamvL1CEolNTPavaN7sRzh8Bb5UdDieBL9jV+lfxCnYpJ5lMi1YJt+EynDPX20Asw==}
    dependencies:
@@ -14862,6 +14880,12 @@
  /trtc-sdk-v5@5.8.7-beta.1:
    resolution: {integrity: sha512-kQdbrM4WBOmWm14QXcwPKYhlcitSX+7hkIN1bsLNhCekVxVohZ5VFEhjYVxIF9g3YYETXLzqIjKKJigzJEaLbA==}
    dependencies:
      webrtc-adapter: 8.2.3
    dev: false
  /trtc-sdk-v5@5.9.1:
    resolution: {integrity: sha512-Vzu1UIzIQtQ3zUQHruha1Zh13aXsDsi/edMdwWdpO8URmRraqftsgKxjR8x2GPHd6p4cDreR/wY50xrWu9ZPlw==}
    dependencies:
      webrtc-adapter: 8.2.3
    dev: false
@@ -14942,7 +14966,7 @@
  /tuicall-engine-webrtc@3.1.7:
    resolution: {integrity: sha512-MRye9/x/8Gy5aRAfEACLtGhK5RcBLAev7cgCGi7OLNt0Uj28mEJkt1tHs4p7xTQiQj6vcB+7OVTdsxuSq7EmOA==}
    dependencies:
      '@tencentcloud/chat': 3.5.3
      '@tencentcloud/chat': 3.5.5
      rtc-ai-denoiser: 1.1.7
      trtc-cloud-js-sdk: 2.8.7
    dev: false
src/components/Reward/RewardInfoCard.vue
New file
@@ -0,0 +1,46 @@
<template>
  <div class="reward-info-card-wrapper">
    <div class="reward-info-card-title">
      <slot name="title">{{ props.title }}</slot>
    </div>
    <div class="reward-info-card-content">
      <slot></slot>
    </div>
  </div>
</template>
<script setup lang="ts">
defineOptions({
  name: 'RewardInfoCard',
});
type Props = {
  title?: string;
};
const props = withDefaults(defineProps<Props>(), {});
</script>
<style lang="scss" scoped>
@use '@/style/common.scss' as *;
.reward-info-card-wrapper {
  display: flex;
  padding: 16px;
  border: 1px solid var(--el-border-color-light);
  border-radius: 4px;
  background-color: #ffffff;
  flex-direction: column;
  .reward-info-card-title {
    margin-bottom: 12px;
    font-size: 16px;
    color: getCssVar('text-color', 'primary');
  }
  .reward-info-card-content {
    flex: 1;
    min-height: 0;
  }
}
</style>
src/components/Reward/RewardInfoCardContentItem.vue
New file
@@ -0,0 +1,45 @@
<template>
  <div class="reward-info-card-content-item">
    <div class="reward-info-card-content-item-label">{{ label }}</div>
    <div class="reward-info-card-content-item-content"><slot></slot></div>
  </div>
</template>
<script setup lang="ts">
defineOptions({
  name: 'RewardInfoCardContentItem',
});
type Props = {
  label?: string;
};
const props = withDefaults(defineProps<Props>(), {});
</script>
<style lang="scss" scoped>
@use '@/style/common.scss' as *;
.reward-info-card-content-item {
  display: flex;
  align-items: center;
  margin-bottom: 12px;
  font-size: 14px;
  &:last-child {
    margin-bottom: 0;
  }
  .reward-info-card-content-item-label {
    width: 80px;
    color: getCssVar('text-color', 'regular');
  }
  .reward-info-card-content-item-content {
    flex: 1;
    min-width: 0;
    @include utils-ellipsis;
    color: getCssVar('text-color', 'primary');
  }
}
</style>
src/components/Reward/RewardInfoCardList.vue
New file
@@ -0,0 +1,22 @@
<template>
  <div class="reward-info-card-list">
    <slot></slot>
  </div>
</template>
<script setup lang="ts">
defineOptions({
  name: 'RewardInfoCardList',
});
</script>
<style lang="scss" scoped>
@use '@/style/common.scss' as *;
.reward-info-card-list {
  display: flex;
  gap: 20px;
  flex-wrap: wrap;
  padding: 20px 0;
}
</style>
src/components/Reward/RewardInfoCardPrice.vue
New file
@@ -0,0 +1,48 @@
<template>
  <div class="reward-info-card-price">
    <div class="reward-info-card-value">{{ displayValue }}</div>
    <div class="reward-info-card-unit">{{ unit }}</div>
  </div>
</template>
<script setup lang="ts">
import { toThousand } from '@/utils';
defineOptions({
  name: 'RewardInfoCardPrice',
});
type Props = {
  value?: number;
  useThousand?: boolean;
  unit?: string;
};
const props = withDefaults(defineProps<Props>(), {
  useThousand: true,
  unit: '元',
});
const displayValue = computed(() => (props.useThousand ? toThousand(props.value) : props.value));
</script>
<style lang="scss" scoped>
@use '@/style/common.scss' as *;
.reward-info-card-price {
  display: flex;
  align-items: flex-end;
  .reward-info-card-value {
    margin-right: 4px;
    font-size: 20px;
    font-weight: bold;
    color: getCssVar('text-color', 'primary');
  }
  .reward-info-card-unit {
    font-size: 14px;
    color: getCssVar('text-color', 'primary');
  }
}
</style>
src/constants/customerConsume.ts
New file
@@ -0,0 +1,20 @@
export enum ParkBountyTradeAccessEnum {
  /**江佑共保 */
  JYB = 1,
}
export const ParkBountyTradeAccessEnumText = {
  [ParkBountyTradeAccessEnum.JYB]: '江佑共保',
};
export enum FinanceTypeEnum {
  /**预充值金额 */
  Recharge = 10,
  /**平台奖励 */
  PlatReward = 20,
}
export const FinanceTypeEnumText = {
  [FinanceTypeEnum.Recharge]: '预充值金额',
  [FinanceTypeEnum.PlatReward]: '平台奖励',
};
src/constants/index.ts
@@ -11,3 +11,4 @@
export * from './app';
export * from './temp';
export * from './insureBatchBill';
export * from './customerConsume';
src/router/index.ts
@@ -2,6 +2,7 @@
import Layout from '@/layout/MainLayout/Layout.vue';
import ErrorLayout from '@/layout/ErrorLayout/ErrorLayout.vue';
import { AppType } from '@/constants';
// import { ElIcons } from '@/typings'
@@ -53,6 +54,7 @@
  children?: Route[];
};
//@ts-ignore
export const constantRoutes: Route[] = [
  {
    path: '/redirect',
@@ -175,6 +177,61 @@
      },
    ],
  },
  AppType === 'jx' && {
    path: '/BalanceManage',
    redirect: 'noRedirect',
    component: Layout,
    hidden: false,
    alwaysShow: true,
    meta: {
      rank: 10010,
      title: '余额管理',
      rootMenu: true,
      icon: 'home',
    },
    children: [
      {
        path: '/BalanceManage',
        name: 'BalanceManage',
        hidden: false,
        alwaysShow: true,
        component: () => import('@/views/BalanceManage/BalanceManage.vue'),
        meta: {
          rank: 10011,
          title: '余额管理',
          // rootMenu: true,
          icon: 'home',
        },
      },
    ],
  },
  // {
  //   path: '/FileManage',
  //   redirect: 'noRedirect',
  //   component: Layout,
  //   hidden: false,
  //   alwaysShow: true,
  //   meta: {
  //     rank: 10020,
  //     title: '文件管理',
  //     rootMenu: true,
  //     icon: 'home',
  //   },
  //   children: [
  //     {
  //       path: '/FileManage',
  //       name: 'FileManage',
  //       hidden: false,
  //       alwaysShow: true,
  //       component: () => import('@/views/FileManage/FileManage.vue'),
  //       meta: {
  //         rank: 10021,
  //         title: '文件管理',
  //         icon: 'home',
  //       },
  //     },
  //   ],
  // },
  {
    path: '/Login',
    name: 'Login',
@@ -213,7 +270,7 @@
      },
    ],
  },
];
].filter(Boolean);
export const asyncRoutes: Route[] = [
  {
src/services/api/ParkBountyApply.ts
New file
@@ -0,0 +1,54 @@
/* eslint-disable */
// @ts-ignore
import { request } from '@/utils/request';
/** 运营端-奖励进出账详情 POST /api/ParkBountyApply/GetParkBountyTradeDetailList */
export async function getParkBountyTradeDetailList(
  body: API.GetParkBountyTradeDetailByIdInput,
  options?: API.RequestConfig
) {
  return request<API.GetParkBountyTradeDetailOutputPageOutput>(
    '/api/ParkBountyApply/GetParkBountyTradeDetailList',
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      data: body,
      ...(options || {}),
    }
  );
}
/** 运营端-奖励金使用管理列表 POST /api/ParkBountyApply/GetParkBountyTradeList */
export async function getParkBountyTradeList(
  body: API.GetParkBountyTradeInput,
  options?: API.RequestConfig
) {
  return request<API.GetParkBountyTradeOutputPageOutput>(
    '/api/ParkBountyApply/GetParkBountyTradeList',
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      data: body,
      ...(options || {}),
    }
  );
}
/** 运营端—奖励金-出账 POST /api/ParkBountyApply/ParkBountyApplyTrade */
export async function parkBountyApplyTrade(
  body: API.CreateParkBountyTradeInput,
  options?: API.RequestConfig
) {
  return request<number>('/api/ParkBountyApply/ParkBountyApplyTrade', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    data: body,
    ...(options || {}),
  });
}
src/services/api/User.ts
@@ -2,6 +2,21 @@
// @ts-ignore
import { request } from '@/utils/request';
/** 批量同步用户余额 POST /api/User/BatchSyncUserAmount */
export async function batchSyncUserAmount(
  body: API.BatchSyncUserAmountInput,
  options?: API.RequestConfig
) {
  return request<any>('/api/User/BatchSyncUserAmount', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    data: body,
    ...(options || {}),
  });
}
/** 新增账号信息 POST /api/User/CreateAccount */
export async function createAccount(body: API.CreateAccountInput, options?: API.RequestConfig) {
  return request<string>('/api/User/CreateAccount', {
@@ -81,6 +96,14 @@
  });
}
/** 获取客户余额信息 GET /api/User/GetUserAmountShow */
export async function getUserAmountShow(options?: API.RequestConfig) {
  return request<API.UserAmountShowDto>('/api/User/GetUserAmountShow', {
    method: 'GET',
    ...(options || {}),
  });
}
/** 获取用户详情 GET /api/User/GetUserDetail */
export async function getUserDetail(
  // 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
@@ -135,6 +158,18 @@
  });
}
/** 同步用户 POST /api/User/SyncUser */
export async function syncUser(body: API.SyncUserDto, options?: API.RequestConfig) {
  return request<string>('/api/User/SyncUser', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    data: body,
    ...(options || {}),
  });
}
/** 更新账号信息 POST /api/User/UpdateAccount */
export async function updateAccount(body: API.UpdateAccountInput, options?: API.RequestConfig) {
  return request<number>('/api/User/UpdateAccount', {
src/services/api/index.ts
@@ -13,6 +13,7 @@
import * as InsuranceClaim from './InsuranceClaim';
import * as InsuranceOrder from './InsuranceOrder';
import * as InsureBatchBill from './InsureBatchBill';
import * as ParkBountyApply from './ParkBountyApply';
import * as Permissions from './Permissions';
import * as PhoneMessage from './PhoneMessage';
import * as Profile from './Profile';
@@ -31,6 +32,7 @@
  InsuranceClaim,
  InsuranceOrder,
  InsureBatchBill,
  ParkBountyApply,
  Permissions,
  PhoneMessage,
  Profile,
src/services/api/typings.d.ts
@@ -474,6 +474,10 @@
    checkStatus?: BatchBillCheckStatus;
    /** 生效时间 */
    effectTime?: string;
    /** 保单生效时间 */
    effectStartTime?: string;
    /** 保单失效时间 */
    effectEndTime?: string;
    /** 附件上传 */
    attachmentUrl?: string;
    /** 保单号 */
@@ -482,6 +486,10 @@
    enterpriseName?: string;
    /** 批改人员详情 */
    staffList?: InsureBatchBillDetailDto[];
  }
  interface BatchSyncUserAmountInput {
    items?: SyncUserAmountInput[];
  }
  interface ChangePasswordInput {
@@ -577,6 +585,16 @@
    remark?: string;
    /** 角色Id */
    id?: string;
  }
  interface CreateParkBountyTradeInput {
    enterpriseId?: string;
    /** 最近出账金额 */
    tradeAmount?: number;
    /** 出账凭证 */
    payFileUrl?: string;
    /** 消费类型 */
    payRemark?: string;
  }
  interface CurrentCultureDto {
@@ -728,6 +746,8 @@
    key?: string;
  }
  type FinanceTypeEnum = 10 | 20;
  interface GetEffectingStaffListInput {
    claimDateTime?: string;
    idNumber?: string;
@@ -846,6 +866,73 @@
    pageModel?: Pagination;
    objectData?: any;
    data?: GetInsuranceStaffPageTemplate[];
  }
  interface GetParkBountyTradeDetailByIdInput {
    pageModel?: Pagination;
    /** 企业名/信用代码 */
    searchKeyWord?: string;
    /** 出账起始日期 */
    transferTimeBegin?: string;
    /** 出账结束日期 */
    transferTimeEnd?: string;
    enterpriseId?: string;
  }
  interface GetParkBountyTradeDetailOutput {
    id?: string;
    /** 最近出账金额 */
    tradeAmount?: number;
    /** 出账凭证 */
    payFileUrl?: string;
    access?: ParkBountyTradeAccessEnum;
    /** 消费类型 */
    payRemark?: string;
    /** 出账时间 */
    tradeTime?: string;
    /** 奖励金余额 */
    remianAmount?: number;
    financeType?: FinanceTypeEnum;
  }
  interface GetParkBountyTradeDetailOutputPageOutput {
    pageModel?: Pagination;
    objectData?: any;
    data?: GetParkBountyTradeDetailOutput[];
  }
  interface GetParkBountyTradeInput {
    pageModel?: Pagination;
    /** 企业名/信用代码 */
    searchKeyWord?: string;
    /** 出账起始日期 */
    transferTimeBegin?: string;
    /** 出账结束日期 */
    transferTimeEnd?: string;
  }
  interface GetParkBountyTradeOutput {
    enterpriseId?: string;
    /** 账号 */
    userName?: string;
    /** 企业名称 */
    enterpriseName?: string;
    /** 电话 */
    contactPhone?: string;
    /** 社会统一信用代码 */
    societyCreditCode?: string;
    /** 最近出账金额 */
    tradeAmount?: number;
    /** 出账时间 */
    tradeTime?: string;
    /** 奖励金余额 */
    remianAmount?: number;
  }
  interface GetParkBountyTradeOutputPageOutput {
    pageModel?: Pagination;
    objectData?: any;
    data?: GetParkBountyTradeOutput[];
  }
  interface GetPermissionListResultDto {
@@ -1028,6 +1115,8 @@
  }
  interface ImportBatchStaffListInput {
    /** 原被保人姓名 */
    orginName?: string;
    orginIdNumber?: string;
    /** 电话号码 */
    phoneNumber?: string;
@@ -1041,6 +1130,7 @@
    workType?: string;
    birthDay?: string;
    remark?: string;
    changeType?: BatchChangeTypeEnum;
  }
  interface ImportBatchUpdateOrderDataInput {
@@ -1188,11 +1278,13 @@
    accidentType?: string;
    /** 事故发生时间 */
    accidentTime?: string;
    /** 伤残比例 */
    disabilityRatio?: number;
    /** 伤残比例 */
    disabilityRatioStr?: string;
    claimResultStr?: string;
    /** 下款金额 */
    downPaymentAmount?: number;
    /** 下款金额 */
    downPaymentAmountStr?: string;
    /** 结/撤案日期 */
    claimResultTime?: string;
    /** 最后更新日期 */
@@ -1573,6 +1665,8 @@
    descriptorName?: string;
  }
  type ParkBountyTradeAccessEnum = 1;
  interface PasswordLoginInput {
    /** 账号 */
    loginName: string;
@@ -1806,6 +1900,48 @@
    modules: ModuleTypeInfo[];
  }
  interface SyncUserAmountInput {
    /** 用户名称 */
    id?: string;
    /** 奖励金余额 */
    bountyAmount?: number;
    /** 充值余额 */
    rechargeAmount?: number;
  }
  interface SyncUserDto {
    /** 开通 */
    open?: boolean;
    /** 818用户Id */
    humanResourcesServiceUserId?: string;
    /** 名称 */
    name?: string;
    /** 登录用户名 */
    userName?: string;
    /** 用户手机号 */
    phoneNumber?: string;
    /** 企业名称 */
    enterpriseName?: string;
    /** 营业执照文件地址 */
    licenseUrl?: string;
    /** 统一社会信用代码 */
    societyCreditCode?: string;
    /** 银行总行名称 */
    bankName?: string;
    /** 银行支行名称 */
    bankBranchName?: string;
    /** 银行卡号 */
    bankCardNumber?: string;
    /** 奖励金余额 */
    bountyAmount?: number;
    /** 充值余额 */
    rechargeAmount?: number;
    /** 联系人 */
    contact?: string;
    /** 联系电话 */
    contactPhone?: string;
  }
  interface TenantDto {
    extraProperties?: Record<string, any>;
    id?: string;
@@ -2002,6 +2138,27 @@
    listFiles?: string[];
  }
  interface UserAmountShowDto {
    /** 企业名称 */
    enterpriseName?: string;
    /** 营业执照文件地址 */
    licenseUrl?: string;
    /** 统一社会信用代码 */
    societyCreditCode?: string;
    /** 银行总行名称 */
    bankName?: string;
    /** 银行支行名称 */
    bankBranchName?: string;
    /** 银行卡号 */
    bankCardNumber?: string;
    /** 奖励金余额 */
    bountyAmount?: number;
    /** 充值余额 */
    rechargeAmount?: number;
    /** 资金总额 */
    totalAmount?: number;
  }
  interface UserData {
    id?: string;
    tenantId?: string;
src/utils/common/common.ts
@@ -1,5 +1,5 @@
import dayjs from 'dayjs';
import { BoleRegExp } from '@bole-core/core';
import { BoleRegExp, Message } from '@bole-core/core';
import { round, floor, omitBy } from 'lodash';
export function format(date: string | Date, fmt = 'YYYY-MM-DD') {
@@ -106,3 +106,42 @@
export function filterNumbersFromString(str: string) {
  return str.replace(/\D/g, '');
}
export function copyTextToClipboard(text: string) {
  if (navigator.clipboard && window.isSecureContext) {
    // navigator clipboard 向剪贴板写文本
    navigator.clipboard
      .writeText(text)
      .then(() => {
        Message.successMessage('已复制');
      })
      .catch((error) => {
        console.error('Failed to copy text to clipboard:', error);
      });
  } else {
    // 创建text area
    let textArea = document.createElement('textarea');
    textArea.value = text;
    // 使text area不在viewport,同时设置不可见
    textArea.style.position = 'absolute';
    textArea.style.opacity = 0 as any;
    textArea.style.left = '-999999px';
    textArea.style.top = '-999999px';
    document.body.appendChild(textArea);
    textArea.select();
    let res = document.execCommand('copy');
    if (res) {
      Message.successMessage('已复制');
    }
    textArea.remove();
  }
}
export function addStarForString(str: string, start = 0, end = 0) {
  return str.substring(0, start) + '*'.repeat(end - start) + str.substring(end);
}
export function addStarForEmail(str: string) {
  const end = str.lastIndexOf('.');
  return addStarForString(str, 2, end);
}
src/utils/common/file.ts
@@ -25,6 +25,14 @@
  return path ? [convertApi2FormUrl(path)] : [];
}
export function convertApi2FormUrlBySeparator(path: string, separator = '|') {
  return path ? path.split(separator).map((x) => setOSSLink(x)) : [];
}
export function convertApi2FormUrlObjectBySeparator(path: string, separator = '|') {
  return path ? path.split(separator).map(convertApi2FormUrl) : [];
}
/**
 * 把upload的路径转换为上传到api的路径
 */
@@ -32,6 +40,13 @@
  return urls.filter((x) => x.path).map((x) => urlOmitDomain(x.path));
}
export function convertFormUrl2ApiBySeparator<T extends { path?: string }>(
  urls: T[],
  separator = '|'
) {
  return convertFormUrl2Api(urls).join(separator);
}
export async function imageUrlToFile(url: string, filename: string) {
  const response = await fetch(`${url}?${new Date().getTime()}`);
  const blob = await response.blob();
src/views/BalanceManage/BalanceManage.vue
New file
@@ -0,0 +1,188 @@
<template>
  <LoadingLayout>
    <AppContainer>
      <RewardInfoCardList>
        <RewardInfoCard class="reward-wrapper">
          <template #title>
            <div class="reward-wrapper-title">
              <div class="reward-wrapper-title-amount">
                <span>资金总额(元)</span>
                <span class="reward-wrapper-title-amount-value">{{
                  toThousand(detail?.totalAmount ?? 0)
                }}</span>
              </div>
            </div>
          </template>
          <div class="reward-wrapper-content">
            <div class="reward-wrapper-content-item">
              <div class="reward-wrapper-content-item-label">平台奖励余额(元)</div>
              <div class="reward-wrapper-content-item-value">
                {{ toThousand(detail?.bountyAmount ?? 0) }}
              </div>
            </div>
            <div class="reward-wrapper-content-item-line"></div>
            <div class="reward-wrapper-content-item">
              <div class="reward-wrapper-content-item-label">预充值金额(元)</div>
              <div class="reward-wrapper-content-item-value">
                {{ toThousand(detail?.rechargeAmount ?? 0) }}
              </div>
            </div>
          </div>
        </RewardInfoCard>
        <RewardInfoCard title="企业基本信息" style="flex: 1">
          <RewardInfoCardContentItem label="企业名称:">
            {{ detail?.enterpriseName ?? '' }}
          </RewardInfoCardContentItem>
          <RewardInfoCardContentItem label="信用代码:">
            {{ detail?.societyCreditCode ?? '' }}
          </RewardInfoCardContentItem>
          <RewardInfoCardContentItem label="营业执照:">
            <el-button
              v-if="!!detail?.licenseUrl"
              type="primary"
              link
              class="link-style-clear"
              @click="handlePreviewCertificate()"
              >查看</el-button
            >
          </RewardInfoCardContentItem>
        </RewardInfoCard>
        <RewardInfoCard title="账户信息" style="flex: 1">
          <RewardInfoCardContentItem label="开户总行:">
            {{ detail?.bankName ?? '' }}
          </RewardInfoCardContentItem>
          <RewardInfoCardContentItem label="开户支行:">
            {{ detail?.bankBranchName ?? '' }}
          </RewardInfoCardContentItem>
          <RewardInfoCardContentItem label="银行帐号:">
            {{ detail?.bankCardNumber ?? '' }}
          </RewardInfoCardContentItem>
        </RewardInfoCard>
      </RewardInfoCardList>
      <ProTabs v-model="state.tabType" hasBorder class="reward-tabs">
        <ProTabPane lazy label="消费记录" name="Consume">
          <ConsumeRecordView></ConsumeRecordView>
        </ProTabPane>
      </ProTabs>
    </AppContainer>
  </LoadingLayout>
</template>
<script setup lang="ts">
import { LoadingLayout, AppContainer, ProTabs, ProTabPane } from '@bole-core/components';
import * as userServices from '@/services/api/User';
import RewardInfoCardList from '@/components/Reward/RewardInfoCardList.vue';
import RewardInfoCard from '@/components/Reward/RewardInfoCard.vue';
import RewardInfoCardContentItem from '@/components/Reward/RewardInfoCardContentItem.vue';
import { setOSSLink, downloadFileByUrl, toThousand } from '@/utils';
import ConsumeRecordView from './components/ConsumeRecordView.vue';
import { useQuery } from '@tanstack/vue-query';
import { useUser } from '@/hooks';
defineOptions({
  name: 'BalanceManage',
});
const BaseState = {
  loading: true,
  tabType: 'Consume',
};
const { user } = useUser();
const state = reactive({ ...BaseState });
const { isLoading, data: detail } = useQuery({
  queryKey: ['userServices/getUserAmountShow'],
  queryFn: async () => {
    return await userServices.getUserAmountShow({
      showLoading: false,
    });
  },
  placeholderData: () => ({} as API.UserAmountShowDto),
});
function handlePreviewCertificate() {
  downloadFileByUrl(setOSSLink(detail.value?.licenseUrl ?? ''));
}
</script>
<style lang="scss" scoped>
@use '@/style/common.scss' as *;
.pretty-layout-main-container .app-main-wrapper > .app-main {
  background-color: #ffffff;
}
.reward-wrapper {
  :deep() {
    .reward-info-card-content {
      display: flex;
    }
  }
  .reward-wrapper-title {
    display: flex;
    justify-content: space-between;
    padding-bottom: 10px;
    border-bottom: 1px solid var(--el-border-color-light);
    .reward-wrapper-title-amount {
      display: flex;
      align-items: flex-end;
      min-width: 0;
      flex: 1;
    }
    .reward-wrapper-title-amount-value {
      font-size: 24px;
      font-weight: bold;
      color: getCssVar('text-color', 'primary');
    }
  }
  .reward-wrapper-content {
    display: flex;
    justify-content: center;
    padding: 10px;
    min-width: 0;
    min-height: 0;
    border: 1px solid var(--el-border-color-light);
    border-radius: 4px;
    flex: 1;
    .reward-wrapper-content-item-line {
      margin: 0 20px;
      width: 1px;
      height: 100%;
      background-color: var(--el-border-color-light);
    }
    .reward-wrapper-content-item {
      flex: 1;
      min-width: 0;
      .reward-wrapper-content-item-label {
        font-size: 14px;
        white-space: nowrap;
        color: getCssVar('text-color', 'regular');
        line-height: 20px;
      }
      .reward-wrapper-content-item-value {
        font-size: 16px;
        line-height: 22px;
        font-weight: bold;
        color: getCssVar('color', 'primary');
      }
    }
  }
}
.reward-tabs {
  :deep() {
    .pro-table-wrapper .pro-table-container {
      padding-top: 20px;
    }
  }
}
</style>
src/views/BalanceManage/components/ConsumeRecordView.vue
New file
@@ -0,0 +1,118 @@
<template>
  <LoadingLayout :loading="state.loading">
    <AppContainer>
      <ProTableV2 v-bind="proTableProps" :columns="column" :operationBtns="operationBtns">
        <template #operationBtn-checkBtn="{ data, row }">
          <PreviewBtnV2
            class="pro-table-operation-btn"
            :url="convertApi2FormUrlBySeparator(row.payFileUrl ?? '')"
            preview-btn-text="查看凭证"
          />
        </template>
      </ProTableV2>
    </AppContainer>
  </LoadingLayout>
</template>
<script setup lang="ts">
import {
  AppContainer,
  useTable,
  PreviewBtnV2,
  ProTableV2,
  defineOperationBtns,
} from '@bole-core/components';
import { OrderInputType } from '@bole-core/core';
import * as parkBountyApplyServices from '@/services/api/ParkBountyApply';
import { convertApi2FormUrlBySeparator } from '@/utils';
import { FinanceTypeEnumText } from '@/constants';
defineOptions({
  name: 'ConsumeRecordView',
});
const column = [
  {
    id: '1',
    enCode: 'tradeTime',
    name: '消费时间',
  },
  {
    id: '2',
    enCode: 'tradeAmount',
    name: '消费金额',
  },
  {
    id: '3',
    enCode: 'payRemark',
    name: '消费类型',
  },
  {
    id: '4',
    enCode: 'financeType',
    name: '资金类型',
  },
];
const operationBtns = defineOperationBtns([
  {
    data: {
      enCode: 'checkBtn',
      name: '查看凭证',
    },
  },
]);
const route = useRoute();
const router = useRouter();
const id = route.params.id as string;
const BaseState = {
  loading: true,
};
const state = reactive({ ...BaseState });
const {
  getDataSource: getList,
  proTableProps,
  paginationState,
  extraParamState,
  reset,
} = useTable(
  async ({ pageIndex, pageSize }, extraParamState) => {
    try {
      let params: API.GetParkBountyTradeDetailByIdInput = {
        pageModel: {
          rows: pageSize,
          page: pageIndex,
          orderInput: extraParamState.orderInput,
        },
        enterpriseId: id,
      };
      let res = await parkBountyApplyServices.getParkBountyTradeDetailList(params, {
        showLoading: !state.loading,
      });
      return res;
    } catch (error) {}
  },
  {
    defaultExtraParams: {
      keyWord: '',
      orderInput: [{ property: 'tradeTime', order: OrderInputType.Asc }],
    },
    columnsRenderProps: {
      tradeTime: { type: 'date', format: 'YYYY-MM-DD HH:mm:ss' },
      tradeAmount: { type: 'money' },
      financeType: { type: 'enum', valueEnum: FinanceTypeEnumText },
    },
  }
);
onMounted(async () => {
  await getList();
  state.loading = false;
});
</script>
<style lang="scss" scoped>
@use '@/style/common.scss' as *;
</style>
src/views/BalanceManage/components/RechargeRecordView.vue
New file
@@ -0,0 +1,143 @@
<template>
  <LoadingLayout :loading="state.loading">
    <AppContainer>
      <ProTableV2 v-bind="proTableProps" :columns="column" :operationBtns="operationBtns">
        <template #checkStatus="{ data, row }">
          <div style="display: flex; justify-content: center; align-items: center">
            {{ EnterpriseRechargeStatusEnumText[row.checkStatus] }}
            <el-tooltip
              placement="top"
              v-if="row.checkStatus === EnterpriseRechargeStatusEnum.CheckReject && row.checkRemark"
              :content="row.checkRemark"
            >
              <el-icon color="#ff0000"><WarningFilled /></el-icon>
            </el-tooltip>
          </div>
        </template>
        <template #operationBtn-checkBtn="{ data, row }">
          <PreviewBtnV2
            class="pro-table-operation-btn"
            :url="convertApi2FormUrlBySeparator(row.rechargeVoucherFileUrl ?? '')"
            preview-btn-text="查看凭证"
          />
        </template>
      </ProTableV2>
    </AppContainer>
  </LoadingLayout>
</template>
<script setup lang="ts">
import {
  AppContainer,
  useTable,
  PreviewBtnV2,
  ProTableV2,
  defineOperationBtns,
} from '@bole-core/components';
import { OrderInputType } from '@bole-core/core';
// import * as parkBountyApplyServices from '@/services/api/ParkBountyApply';
import { useUser } from '@/hooks';
// import {
//   EnterprisePrechargeInComeStatusEnum,
//   EnterprisePrechargeInComeStatusEnumText,
//   EnterpriseRechargeStatusEnum,
//   EnterpriseRechargeStatusEnumText,
// } from '@/constants';
import { convertApi2FormUrlBySeparator } from '@/utils';
defineOptions({
  name: 'RechargeRecordView',
});
const column = [
  {
    id: '1',
    enCode: 'creationTime',
    name: '充值时间',
  },
  {
    id: '2',
    enCode: 'prechargeAmount',
    name: '充值金额',
  },
  {
    id: '3',
    enCode: 'checkStatus',
    name: '审核状态',
  },
  {
    id: '4',
    enCode: 'inComeStatus',
    name: '入账状态',
  },
];
const operationBtns = defineOperationBtns([
  {
    data: {
      enCode: 'checkBtn',
      name: '查看凭证',
    },
    extraProps: {
      hide: (row) => row.inComeStatus !== EnterprisePrechargeInComeStatusEnum.HasInCome,
    },
  },
]);
const route = useRoute();
const router = useRouter();
const id = route.params.id as string;
const BaseState = {
  loading: true,
};
const state = reactive({ ...BaseState });
const { userDetail } = useUser();
const {
  getDataSource: getList,
  proTableProps,
  paginationState,
  extraParamState,
} = useTable(
  async ({ pageIndex, pageSize }, extraParamState) => {
    try {
      let params: API.PageInput = {
        pageModel: {
          rows: pageSize,
          page: pageIndex,
          orderInput: extraParamState.orderInput,
        },
      };
      let res = await parkBountyApplyServices.getEnterprisePrechargeList(params, {
        showLoading: !state.loading,
      });
      return res;
    } catch (error) {}
  },
  {
    defaultExtraParams: {
      orderInput: [{ property: 'id', order: OrderInputType.Desc }],
    },
    columnsRenderProps: {
      creationTime: { type: 'date', format: 'YYYY-MM-DD' },
      prechargeAmount: { type: 'money' },
      checkStatus: { type: 'enum', valueEnum: EnterpriseRechargeStatusEnumText },
      inComeStatus: { type: 'enum', valueEnum: EnterprisePrechargeInComeStatusEnumText },
    },
  }
);
onMounted(async () => {
  await getList();
  state.loading = false;
});
defineExpose({
  getList,
});
</script>
<style lang="scss" scoped>
@use '@/style/common.scss' as *;
</style>
src/views/BalanceManage/components/RewardGrantRecordView.vue
New file
@@ -0,0 +1,102 @@
<template>
  <LoadingLayout :loading="state.loading">
    <AppContainer>
      <ProTableV2 v-bind="proTableProps" :columns="column" :showOperationColumn="false">
      </ProTableV2>
    </AppContainer>
  </LoadingLayout>
</template>
<script setup lang="ts">
import { AppContainer, useTable, ProTableV2 } from '@bole-core/components';
import { OrderInputType } from '@bole-core/core';
// import * as parkBountyApplyServices from '@/services/api/ParkBountyApply';
import { useUser } from '@/hooks';
// import { TransferToStatusEnumText } from '@/constants';
defineOptions({
  name: 'RewardGrantRecordView',
});
const column = [
  {
    id: '1',
    enCode: 'transferToTime',
    name: '发放时间',
  },
  {
    id: '2',
    enCode: 'transferToAmount',
    name: '发放金额',
  },
  {
    id: '3',
    enCode: 'batchNo',
    name: '发放批次',
  },
  {
    id: '4',
    enCode: 'applyMonth',
    name: '发放月份',
  },
  {
    id: '4',
    enCode: 'transferToStatus',
    name: '发放状态',
  },
];
const route = useRoute();
const router = useRouter();
const id = route.params.id as string;
const BaseState = {
  loading: true,
};
const state = reactive({ ...BaseState });
const { userDetail } = useUser();
const {
  getDataSource: getList,
  proTableProps,
  paginationState,
  extraParamState,
} = useTable(
  async ({ pageIndex, pageSize }, extraParamState) => {
    try {
      let params: API.QueryParkCustomerBountyApplyInput = {
        pageModel: {
          rows: pageSize,
          page: pageIndex,
          orderInput: extraParamState.orderInput,
        },
        id: userDetail.value?.userId ?? '',
      };
      let res = await parkBountyApplyServices.getParkCustomerBountySettleList(params, {
        showLoading: !state.loading,
      });
      return res;
    } catch (error) {}
  },
  {
    defaultExtraParams: {
      orderInput: [{ property: 'settleTime', order: OrderInputType.Desc }],
    },
    columnsRenderProps: {
      transferToStatus: { type: 'enum', valueEnum: TransferToStatusEnumText },
      transferToTime: { type: 'date', format: 'YYYY-MM-DD' },
      applyMonth: { type: 'date', format: 'YYYY年MM月' },
      transferToAmount: { type: 'money' },
    },
  }
);
onMounted(async () => {
  await getList();
  state.loading = false;
});
</script>
<style lang="scss" scoped>
@use '@/style/common.scss' as *;
</style>
src/views/BalanceManage/components/RewardRechargeDialog.vue
New file
@@ -0,0 +1,266 @@
<template>
  <ProDialog
    title="充值"
    v-model="innerVisible"
    @close="onDialogClose"
    destroy-on-close
    draggable
    top="10vh"
    :width="900"
  >
    <div class="reward-recharge-wrapper">
      <div class="reward-recharge-wrapper-title">
        请您通过网银转账或线下汇款的方式打款至下方指定账号
      </div>
      <div class="reward-recharge-wrapper-content">
        <ProForm label-width="auto" is-read>
          <ProFormItemV2 label="进账单位:">
            <ProFormText :model-value="innerForm?.incomeEnterpriseName ?? ''"></ProFormText>
          </ProFormItemV2>
          <ProFormItemV2 label="开户名称:">
            <ProFormText :model-value="innerForm?.incomeEnterpriseName ?? ''"></ProFormText>
          </ProFormItemV2>
          <ProFormItemV2 label="开户银行:">
            <ProFormText :model-value="innerForm?.bankBranchName ?? ''"></ProFormText>
          </ProFormItemV2>
          <ProFormItemV2 label="银行账号:">
            <ProFormText :model-value="innerForm?.incomeBankNum">
              <template #readContent>
                <span>{{ StringUtils.insertSpaces(innerForm?.incomeBankNum) }}</span>
                <el-button
                  type="primary"
                  link
                  class="el-button-link-clear"
                  style="margin-left: 16px"
                  @click="copyTextToClipboard(innerForm?.incomeBankNum)"
                  >复制账号</el-button
                >
              </template>
            </ProFormText>
          </ProFormItemV2>
        </ProForm>
      </div>
    </div>
    <ProForm :model="innerForm" label-width="120px" ref="dialogForm">
      <FormCellV2 title="转账/汇款信息确认">
        <ProFormCol>
          <ProFormColItem :span="12">
            <ProFormItemV2
              label="汇款账户名称:"
              prop="outEnterpriseName"
              :check-rules="[{ message: '请输入您汇款的账户名称' }]"
            >
              <ProFormText
                v-model.trim="innerForm.outEnterpriseName"
                :maxlength="30"
                placeholder="请输入您汇款的账户名称"
              />
            </ProFormItemV2>
          </ProFormColItem>
        </ProFormCol>
        <ProFormCol>
          <ProFormColItem :span="12">
            <ProFormItemV2
              label="汇款银行账号:"
              prop="outBankNum"
              :check-rules="[
                {
                  trigger: 'blur',
                  validator: (rule, value, callback) => {
                    const _value = value.replace(/\s+/g, '');
                    if (!_value) {
                      callback(new Error('请输入您汇款的对公帐号!'));
                    } else if (!BoleRegExp.RegBankCard.test(_value)) {
                      callback(new Error('请输入正确的银行卡号!'));
                    } else {
                      callback();
                    }
                  },
                },
              ]"
            >
              <ProFormText
                v-model.trim="innerForm.outBankNum"
                :maxlength="30"
                placeholder="请输入您汇款的对公帐号"
                :formatter="(value: string) => value.replace(/\s+/g, '')"
              />
            </ProFormItemV2>
          </ProFormColItem>
        </ProFormCol>
        <ProFormItemV2
          label="汇款金额:"
          prop="prechargeAmount"
          :check-rules="[{ type: 'number', message: '请输入您的汇款金额' }]"
        >
          <ProFormInputNumber
            v-model.trim="innerForm.prechargeAmount"
            placeholder="请输入您的汇款金额"
            :controls="false"
            :min="0"
            :max="99999999999999"
            :precision="2"
            unit="元"
            style="width: 200px"
          />
        </ProFormItemV2>
        <ProFormCol>
          <ProFormColItem :span="12">
            <ProFormItemV2
              label="开户银行:"
              prop="outBankName"
              :check-rules="[{ message: '请输入您的开户银行' }]"
            >
              <ProFormText
                v-model.trim="innerForm.outBankName"
                :maxlength="30"
                placeholder="请输入您的开户银行"
              />
            </ProFormItemV2>
          </ProFormColItem>
        </ProFormCol>
        <ProFormCol>
          <ProFormColItem :span="12">
            <ProFormItemV2 label="开户支行:" prop="outBankResumeName">
              <ProFormText
                v-model.trim="innerForm.outBankResumeName"
                :maxlength="30"
                placeholder="请输入您的开户支行"
              />
            </ProFormItemV2>
          </ProFormColItem>
        </ProFormCol>
        <ProFormItemV2
          label="上传汇款回单:"
          prop="outReceiptFileUrl"
          :check-rules="[{ type: 'upload', message: '请上传汇款回单' }]"
        >
          <ProFormUpload
            v-model:file-url="innerForm.outReceiptFileUrl"
            :limitFileSize="10"
            accept="jpg/jpeg,png,pdf"
          >
          </ProFormUpload>
        </ProFormItemV2>
      </FormCellV2>
    </ProForm>
    <template #footer>
      <span class="dialog-footer">
        <el-button type="default" @click="emit('onCancel')">返回</el-button>
        <el-button type="primary" @click="handleConfirm">确认提交</el-button>
      </span>
    </template>
  </ProDialog>
</template>
<script setup lang="ts">
import { FormInstance } from 'element-plus';
import {
  ProDialog,
  ProForm,
  ProFormItemV2,
  ProFormText,
  ProFormUpload,
  ProFormInputNumber,
  UploadUserFile,
  ProFormCol,
  ProFormColItem,
} from '@bole-core/components';
import { StringUtils, copyTextToClipboard } from '@/utils';
import { BoleRegExp } from '@bole-core/core';
defineOptions({
  name: 'RewardRechargeDialog',
});
type Props = {
  modelValue: boolean;
  form: {
    title?: string;
    incomeBankNum: string;
    incomeEnterpriseName: string;
    bankBranchName: string;
    outBankNum: string;
    outBankName: string;
    outBankResumeName: string;
    outEnterpriseName: string;
    prechargeAmount: number;
    outReceiptFileUrl: UploadUserFile[];
  };
};
const props = withDefaults(defineProps<Props>(), {
  modelValue: false,
});
const emit = defineEmits<{
  (e: 'update:modelValue', value: boolean): void;
  (e: 'update:form', value: Props['form']): void;
  (e: 'onConfirm'): void;
  (e: 'onCancel'): void;
}>();
const dialogForm = ref<FormInstance>();
const innerVisible = computed({
  get() {
    return props.modelValue;
  },
  set(val) {
    emit('update:modelValue', val);
  },
});
const innerForm = computed({
  get() {
    return props.form;
  },
  set(val) {
    emit('update:form', val);
  },
});
function onDialogClose() {
  if (!dialogForm.value) return;
  dialogForm.value.resetFields();
}
function handleConfirm() {
  if (!dialogForm.value) return;
  dialogForm.value.validate((valid) => {
    if (valid) {
      emit('onConfirm');
    } else {
      return;
    }
  });
}
</script>
<style lang="scss" scoped>
.reward-recharge-wrapper {
  margin-bottom: 24px;
  padding: 20px;
  border: 1px solid #e8e8e8;
  border-radius: 4px;
  .reward-recharge-wrapper-title {
    margin-bottom: 24px;
    font-size: 14px;
    color: getCssVar('text-color', 'primary');
    line-height: 19px;
  }
  .reward-recharge-wrapper-content {
    padding: 20px 40px;
    border: 1px dashed #ffd64f;
    border-radius: 4px;
    background: #fffcf2;
    .el-form {
      :deep() {
        .el-form-item__label {
          padding-right: 8px;
        }
      }
    }
  }
}
</style>
src/views/FileManage/FileManage.vue
New file
@@ -0,0 +1,161 @@
<template>
  <LoadingLayout :loading="state.loading">
    <AppContainer>
      <ProTableQueryFilterBar @on-reset="reset">
        <template #query>
          <QueryFilterItem>
            <FieldDatePicker
              v-model="extraParamState.creationDate"
              type="daterange"
              range-separator="~"
              start-placeholder="起始时间"
              end-placeholder="结束时间"
              clearable
              @change="getList()"
              tooltipContent="操作时间"
            ></FieldDatePicker>
          </QueryFilterItem>
        </template>
      </ProTableQueryFilterBar>
      <ProTableV2 v-bind="proTableProps" :columns="columns" :operationBtns="operationBtns">
      </ProTableV2>
    </AppContainer>
  </LoadingLayout>
</template>
<script setup lang="ts">
import {
  ProTableQueryFilterBar,
  ProTableV2,
  LoadingLayout,
  AppContainer,
  QueryFilterItem,
  useTable,
  FieldDatePicker,
  defineOperationBtns,
  defineColumns,
} from '@bole-core/components';
import * as insuranceOrderServices from '@/services/api/InsuranceOrder';
import { OrderInputType, downloadFileByUrl } from '@bole-core/core';
import { format, setOSSLink } from '@/utils';
import { ModelValueType } from 'element-plus';
import { InsurancePolicyStatusEnumText, InsurancePolicyStatusEnum } from '@/constants';
defineOptions({
  name: 'FileManage',
});
const columns = defineColumns([
  {
    id: '1',
    enCode: 'q',
    name: '标题',
  },
  {
    id: '2',
    enCode: 'q',
    name: '时间',
  },
  {
    id: '3',
    enCode: 'q',
    name: '操作',
  },
  {
    id: '4',
    enCode: 'q',
    name: '状态',
  },
]);
const operationBtns = defineOperationBtns([
  {
    data: {
      enCode: 'downloadFileBtn',
      name: '下载文件',
    },
    emits: {
      onClick: (role) => handleDownloadFile(role),
    },
  },
  {
    data: {
      enCode: 'downloadOriginalFileBtn',
      name: '下载原文件',
    },
    emits: {
      onClick: (role) => handleDownloadOriginalFile(role),
    },
  },
  {
    data: {
      enCode: 'downloadErrorDataBtn',
      name: '下载错误数据',
    },
    emits: {
      onClick: (role) => handleDownloadErrorData(role),
    },
  },
]);
const BaseState = {
  loading: true,
};
const state = reactive({ ...BaseState });
onMounted(async () => {
  await getList();
  state.loading = false;
});
const {
  getDataSource: getList,
  proTableProps,
  paginationState,
  extraParamState,
  reset,
} = useTable(
  async ({ pageIndex, pageSize }, extraParamState) => {
    try {
      let params: API.GetInsurancePageInput = {
        pageModel: {
          rows: pageSize,
          page: pageIndex,
          orderInput: extraParamState.orderInput,
        },
        importStartDateTime: format(extraParamState.creationDate?.[0] ?? '', 'YYYY-MM-DD 00:00:00'),
        importEndDateTime: format(extraParamState.creationDate?.[1] ?? '', 'YYYY-MM-DD 23:59:59'),
        insurancePeriod: extraParamState.insurancePeriod,
        status: extraParamState.status,
      };
      let res = await insuranceOrderServices.getInsurancePage(params, {
        showLoading: !state.loading,
      });
      return res;
    } catch (error) {}
  },
  {
    defaultExtraParams: {
      orderInput: [{ property: 'id', order: OrderInputType.Desc }],
      creationDate: [] as unknown as ModelValueType,
      status: '' as any as InsurancePolicyStatusEnum,
      insurancePeriod: '',
    },
    columnsRenderProps: {
      status: { type: 'enum', valueEnum: InsurancePolicyStatusEnumText },
      effectEndTime: { type: 'date', format: 'YYYY-MM-DD' },
    },
  }
);
function handleDownloadFile(row: API.GetInsurancePageOutput) {
  downloadFileByUrl(setOSSLink(row.insureBillUrl));
}
function handleDownloadOriginalFile(row: API.GetInsurancePageOutput) {
  downloadFileByUrl(setOSSLink(row.insureBillUrl));
}
function handleDownloadErrorData(row: API.GetInsurancePageOutput) {
  downloadFileByUrl(setOSSLink(row.insureBillUrl));
}
</script>
src/views/Home/Home.vue
@@ -47,11 +47,16 @@
          <el-button @click="handleDownload()" type="primary" style="margin-right: 10px" link
            >模板下载</el-button
          >
          <el-button @click="handleUpload()" type="primary" style="margin-right: 10px"
            >导入</el-button
            >导入投保人员</el-button
          >
          <!-- <el-button
            v-if="AppType === 'jx'"
            @click="handleUpload()"
            type="primary"
            style="margin-right: 10px"
            >大批量数据导入</el-button
          > -->
          <el-button @click="getInsurancePageExport()" type="primary" link>导出保单列表</el-button>
          <el-button @click="getInsuranceStaffPageExport()" type="primary" link
            >导出在保人员</el-button
@@ -103,6 +108,7 @@
  insuranceTypeText,
  InsurancePolicyStatusEnumText,
  InsurancePolicyStatusEnum,
  AppType,
} from '@/constants';
import dayjs from 'dayjs';
import _ from 'lodash';