From 3d2b60f4ffff98f6f2059b495c0d44126ac4bed3 Mon Sep 17 00:00:00 2001 From: 18278715334 <18278715334@163.com> Date: Fri, 12 Dec 2025 15:27:21 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E6=94=AF=E4=BB=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 268 +++++++++- package.json | 1 + src/api/index.js | 6 +- src/assets/styles/list-pages.css | 32 ++ src/pages/admin/recharge-records/index.vue | 79 ++- src/pages/finance/Wallet.vue | 504 ++++++++++++++++++- src/pages/finance/recharge-records/index.vue | 19 +- src/pages/products/detail.vue | 232 ++++++++- 8 files changed, 1110 insertions(+), 31 deletions(-) diff --git a/package-lock.json b/package-lock.json index ff8ad2b..c834040 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "lodash-es": "^4.17.21", "marked": "^16.1.1", "pinia": "^3.0.3", + "qrcode": "^1.5.4", "tinymce": "^8.0.2", "unplugin-auto-import": "^19.3.0", "unplugin-vue-components": "^28.8.0", @@ -2859,11 +2860,19 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -3115,6 +3124,15 @@ "node": ">=6" } }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001727", "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", @@ -3198,11 +3216,21 @@ "node": ">=18" } }, + "node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -3215,7 +3243,6 @@ "version": "1.1.4", "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, "license": "MIT" }, "node_modules/colorjs.io": { @@ -3348,6 +3375,15 @@ } } }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz", @@ -3416,6 +3452,12 @@ "node": ">=8" } }, + "node_modules/dijkstrajs": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", + "license": "MIT" + }, "node_modules/dom7": { "version": "3.0.0", "resolved": "https://registry.npmmirror.com/dom7/-/dom7-3.0.0.tgz", @@ -3582,6 +3624,12 @@ } } }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, "node_modules/enhanced-resolve": { "version": "5.18.2", "resolved": "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-5.18.2.tgz", @@ -4306,6 +4354,15 @@ "node": ">=6.9.0" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -4579,6 +4636,15 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", @@ -5678,6 +5744,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz", @@ -5708,7 +5783,6 @@ "version": "4.0.0", "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -5811,6 +5885,15 @@ "pathe": "^2.0.3" } }, + "node_modules/pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/postcss": { "version": "8.5.6", "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz", @@ -5950,6 +6033,23 @@ "node": ">=6" } }, + "node_modules/qrcode": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", + "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", + "license": "MIT", + "dependencies": { + "dijkstrajs": "^1.0.1", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/quansync": { "version": "0.2.10", "resolved": "https://registry.npmmirror.com/quansync/-/quansync-0.2.10.tgz", @@ -6004,6 +6104,21 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "license": "ISC" + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz", @@ -6423,6 +6538,12 @@ "node": ">=10" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", @@ -6558,6 +6679,32 @@ "integrity": "sha512-q+8UfWDg9Itrg0yWK7oe5p/XRCJpJF9OBtXfOPgSJl+u3Xd5KI328RUEvUqSMVM9CiQUEf1QdBzJMkYGErj9QA==", "license": "MIT" }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-final-newline": { "version": "4.0.0", "resolved": "https://registry.npmmirror.com/strip-final-newline/-/strip-final-newline-4.0.0.tgz", @@ -7347,6 +7494,12 @@ "node": ">= 8" } }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "license": "ISC" + }, "node_modules/wildcard": { "version": "1.1.2", "resolved": "https://registry.npmmirror.com/wildcard/-/wildcard-1.1.2.tgz", @@ -7363,6 +7516,20 @@ "node": ">=0.10.0" } }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wsl-utils": { "version": "0.1.0", "resolved": "https://registry.npmmirror.com/wsl-utils/-/wsl-utils-0.1.0.tgz", @@ -7389,6 +7556,12 @@ "node": ">=12" } }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "license": "ISC" + }, "node_modules/yallist": { "version": "5.0.0", "resolved": "https://registry.npmmirror.com/yallist/-/yallist-5.0.0.tgz", @@ -7398,6 +7571,93 @@ "node": ">=18" } }, + "node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/yargs/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 3f00f3c..060c65f 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "lodash-es": "^4.17.21", "marked": "^16.1.1", "pinia": "^3.0.3", + "qrcode": "^1.5.4", "tinymce": "^8.0.2", "unplugin-auto-import": "^19.3.0", "unplugin-vue-components": "^28.8.0", diff --git a/src/api/index.js b/src/api/index.js index 3cd9674..d5d23b8 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -1,11 +1,11 @@ import request from '@/utils/request' -import { articleApi } from './article.js' import { announcementApi } from './announcement.js' +import { articleApi } from './article.js' import { balanceAlertApi } from './balanceAlertApi.js' import { adminInvoiceApi, invoiceApi } from './invoice.js' // 直接导出发票API、文章API、公告API和余额预警API -export { adminInvoiceApi, articleApi, announcementApi, balanceAlertApi, invoiceApi } +export { adminInvoiceApi, announcementApi, articleApi, balanceAlertApi, invoiceApi } // 用户相关接口 - 严格按照后端路由定义 export const userApi = { @@ -109,6 +109,8 @@ export const financeApi = { transferRecharge: (data) => request.post('/finance/wallet/transfer-recharge', data), giftRecharge: (data) => request.post('/finance/wallet/gift-recharge', data), createAlipayRecharge: (data) => request.post('/finance/wallet/alipay-recharge', data), + createWechatRecharge: (data) => request.post('/finance/wallet/wechat-recharge', data), + getWechatOrderStatus: (params) => request.get('/finance/wallet/wechat-order-status', { params }), // 用户密钥相关 createUserSecrets: (data) => request.post('/finance/secrets', data), diff --git a/src/assets/styles/list-pages.css b/src/assets/styles/list-pages.css index eb5f85b..b28efec 100644 --- a/src/assets/styles/list-pages.css +++ b/src/assets/styles/list-pages.css @@ -55,6 +55,7 @@ display: flex; align-items: center; gap: 12px; + flex-wrap: wrap; } /* ===== 筛选区域 ===== */ @@ -366,10 +367,31 @@ padding: 24px 20px 20px; } + .list-page-header .flex.justify-between { + flex-direction: column; + gap: 16px; + } + .list-page-title { font-size: 24px; } + .list-page-actions { + width: 100%; + justify-content: flex-start; + gap: 8px; + } + + .list-page-actions .el-button { + flex: 1; + min-width: 0; + flex-basis: calc(50% - 4px); + } + + .list-page-actions .el-button:last-child:nth-child(odd) { + flex-basis: 100%; + } + .list-page-filters { padding: 20px; } @@ -502,6 +524,16 @@ font-size: 20px; } + .list-page-actions { + flex-direction: column; + gap: 8px; + } + + .list-page-actions .el-button { + flex-basis: 100%; + width: 100%; + } + .list-page-filters { padding: 16px 12px; } diff --git a/src/pages/admin/recharge-records/index.vue b/src/pages/admin/recharge-records/index.vue index 32c1409..3c45702 100644 --- a/src/pages/admin/recharge-records/index.vue +++ b/src/pages/admin/recharge-records/index.vue @@ -50,6 +50,7 @@ class="w-full" > + @@ -181,6 +182,28 @@ + + + + @@ -290,6 +434,7 @@ import { useUserStore } from '@/stores/user' import { Check } from '@element-plus/icons-vue' import { CreditCardIcon, CurrencyYenIcon } from '@heroicons/vue/24/outline' import { ElMessage, ElMessageBox } from 'element-plus' +import QRCode from 'qrcode' const userStore = useUserStore() const userInfo = userStore.userInfo @@ -307,7 +452,13 @@ const { // 响应式数据 const selectedMethod = ref('alipay') const alipayLoading = ref(false) +const wechatLoading = ref(false) const showBusinessConsultation = ref(false) +const showQrCodeDialog = ref(false) +const qrCodeCanvas = ref(null) +const currentWechatOrderNo = ref(null) +const isCheckingPayment = ref(false) +let wechatOrderPollTimer = null // 钱包信息 const walletInfo = ref({ @@ -337,6 +488,12 @@ const alipayForm = reactive({ amount: '', }) +// 微信充值表单 +const wechatFormRef = ref() +const wechatForm = reactive({ + amount: '', +}) + // 预设金额选择 const selectedPresetAmount = ref(null) @@ -371,6 +528,17 @@ const handleAmountInput = (value) => { } } +// 处理微信金额输入变化 +const handleWechatAmountInput = (value) => { + const formatted = formatAmountInput(value || '') + wechatForm.amount = formatted + + // 如果输入了自定义金额,更新选择状态 + if (formatted && selectedPresetAmount.value !== 'custom') { + selectedPresetAmount.value = 'custom' + } +} + const alipayRules = { amount: [ { required: true, message: '请输入充值金额', trigger: 'blur' }, @@ -410,6 +578,45 @@ const alipayRules = { ], } +const wechatRules = { + amount: [ + { required: true, message: '请输入充值金额', trigger: 'blur' }, + { + validator: (rule, value, callback) => { + if (!value) { + callback() + return + } + + // 检查是否为有效数字格式 + const amountRegex = /^\d+(\.\d{1,2})?$/ + if (!amountRegex.test(value)) { + callback(new Error('请输入正确的金额格式,最多支持两位小数')) + return + } + + // 检查金额范围 + const amount = parseFloat(value) + const minAmount = parseFloat(rechargeConfig.value.min_amount) + const maxAmount = parseFloat(rechargeConfig.value.max_amount) + + if (amount < minAmount) { + callback(new Error(`充值金额不能少于${minAmount}元`)) + return + } + + if (amount > maxAmount) { + callback(new Error(`单次充值金额不能超过${maxAmount}元`)) + return + } + + callback() + }, + trigger: 'blur', + }, + ], +} + // 初始化 onMounted(() => { loadWalletInfo() @@ -450,7 +657,9 @@ const loadRechargeConfig = async () => { if (rechargeConfig.value.alipay_recharge_bonus && rechargeConfig.value.alipay_recharge_bonus.length > 0) { const firstBonus = rechargeConfig.value.alipay_recharge_bonus[0] selectedPresetAmount.value = firstBonus.recharge_amount - alipayForm.amount = firstBonus.recharge_amount.toString() + const amountStr = firstBonus.recharge_amount.toString() + alipayForm.amount = amountStr + wechatForm.amount = amountStr } } } catch (error) { @@ -513,13 +722,16 @@ const copyToClipboard = async (text) => { // 选择预设金额 const selectPresetAmount = (amount) => { selectedPresetAmount.value = amount - alipayForm.amount = amount.toString() + const amountStr = amount.toString() + alipayForm.amount = amountStr + wechatForm.amount = amountStr } // 选择自定义金额 const selectCustomAmount = () => { selectedPresetAmount.value = 'custom' alipayForm.amount = '' // 清空金额输入框 + wechatForm.amount = '' // 清空微信金额输入框 } // 根据充值金额获取赠送金额 @@ -545,7 +757,9 @@ const getBonusAmount = (rechargeAmount) => { // 获取当前预设金额的赠送金额 const getCurrentBonusAmount = () => { if (selectedPresetAmount.value === 'custom') { - return getBonusAmount(alipayForm.amount) + // 根据当前选择的充值方式获取金额 + const currentAmount = selectedMethod.value === 'wechat' ? wechatForm.amount : alipayForm.amount + return getBonusAmount(currentAmount) } const bonus = rechargeConfig.value.alipay_recharge_bonus.find( @@ -565,7 +779,9 @@ const getCustomBonusText = () => { // 获取自定义金额的总到账金额 const getCustomTotalAmount = () => { if (selectedPresetAmount.value === 'custom') { - const amount = parseFloat(alipayForm.amount || 0) + // 根据当前选择的充值方式获取金额 + const currentAmount = selectedMethod.value === 'wechat' ? wechatForm.amount : alipayForm.amount + const amount = parseFloat(currentAmount || 0) const bonus = getBonusAmount(amount) return formatPrice(amount + bonus) } @@ -628,6 +844,204 @@ const handleAlipayRecharge = async () => { alipayLoading.value = false } } + +// 微信充值 +const handleWechatRecharge = async () => { + if (!wechatFormRef.value) return + + try { + await wechatFormRef.value.validate() + + // 显示确认框 + await ElMessageBox.confirm( + `确认充值 ¥${wechatForm.amount} 到您的钱包吗?`, + '确认充值', + { + confirmButtonText: '确认充值', + cancelButtonText: '取消', + type: 'warning', + customClass: 'custom-message-box', + dangerouslyUseHTMLString: false + } + ) + + wechatLoading.value = true + + // 调用后端创建微信充值订单 + const response = await callProtectedAPI(financeApi.createWechatRecharge, { + amount: wechatForm.amount, // 直接传递字符串类型 + subject: `钱包充值 ¥${wechatForm.amount}`, + platform: 'wx_h5', // PC/H5 场景,后端已兼容无 openid + }) + + if (!response) { + ElMessage.error('请先完成企业认证后再进行充值操作') + return + } + + // 处理微信支付响应 + // prepay_data 可能包含 code_url (扫码支付) 或 pay_url (H5支付) + if (response.data && response.data.prepay_data) { + const prepayData = response.data.prepay_data + + // 扫码支付:显示二维码 + if (prepayData.code_url) { + // 保存订单号用于轮询(从响应中获取订单号) + if (response.data.out_trade_no) { + currentWechatOrderNo.value = response.data.out_trade_no + await showQrCode(prepayData.code_url) + // 开始轮询订单状态 + startWechatOrderPolling() + } else { + ElMessage.error('获取订单号失败,请重新支付') + } + } + // H5支付:跳转到支付页面 + else if (prepayData.pay_url || response.data.pay_url) { + ElMessage.success('正在跳转到微信支付...') + window.location.href = prepayData.pay_url || response.data.pay_url + } + // 小程序或APP支付 + else if (prepayData.prepay_id || response.data.prepay_id) { + ElMessage.success('请使用微信扫码支付') + // 这里可以根据实际返回的数据进行处理 + } else { + console.warn('微信支付返回数据格式异常:', response.data) + ElMessage.warning('支付数据格式异常,请联系客服') + } + } else if (response.data && response.data.pay_url) { + // 兼容旧的返回格式(直接返回 pay_url) + ElMessage.success('正在跳转到微信支付...') + window.location.href = response.data.pay_url + } else { + console.warn('微信支付返回数据异常:', response.data) + ElMessage.warning('获取支付信息失败,请稍后重试') + } + } catch (error) { + // 如果是用户取消,不显示错误信息 + if (error === 'cancel' || error === 'close') { + return + } + + console.error('微信充值失败:', error) + if (canCallAPI.value) { + ElMessage.error('微信充值失败,请稍后重试') + } + } finally { + wechatLoading.value = false + } +} + +// 显示二维码 +const showQrCode = async (codeUrl) => { + try { + showQrCodeDialog.value = true + + // 等待DOM更新 + await nextTick() + + if (qrCodeCanvas.value) { + // 生成二维码 + await QRCode.toCanvas(qrCodeCanvas.value, codeUrl, { + width: 256, + margin: 2, + color: { + dark: '#000000', + light: '#FFFFFF' + } + }) + } + } catch (error) { + console.error('生成二维码失败:', error) + ElMessage.error('生成二维码失败,请稍后重试') + showQrCodeDialog.value = false + } +} + +// 关闭二维码弹窗 +const closeQrCodeDialog = () => { + stopWechatOrderPolling() + showQrCodeDialog.value = false + currentWechatOrderNo.value = null + isCheckingPayment.value = false +} + +// 开始轮询微信订单状态 +const startWechatOrderPolling = () => { + // 清除之前的定时器 + stopWechatOrderPolling() + + // 立即检查一次 + checkWechatOrderStatus() + + // 每3秒轮询一次 + wechatOrderPollTimer = setInterval(() => { + checkWechatOrderStatus() + }, 3000) +} + +// 停止轮询 +const stopWechatOrderPolling = () => { + if (wechatOrderPollTimer) { + clearInterval(wechatOrderPollTimer) + wechatOrderPollTimer = null + } +} + +// 检查微信订单状态 +const checkWechatOrderStatus = async () => { + if (!currentWechatOrderNo.value) { + return + } + + try { + isCheckingPayment.value = true + const response = await callProtectedAPI(financeApi.getWechatOrderStatus, { + out_trade_no: currentWechatOrderNo.value + }) + + if (!response || !response.data) { + isCheckingPayment.value = false + return + } + + const orderStatus = response.data.status + + // 订单状态:pending, success, failed, closed + if (orderStatus === 'success') { + // 支付成功 + stopWechatOrderPolling() + isCheckingPayment.value = false + closeQrCodeDialog() + ElMessage.success('充值成功!') + + // 刷新钱包余额 + await loadWalletInfo() + + // 重置表单 + wechatForm.amount = '' + selectedPresetAmount.value = null + } else if (orderStatus === 'failed' || orderStatus === 'closed') { + // 支付失败或关闭 + stopWechatOrderPolling() + isCheckingPayment.value = false + ElMessage.error('支付失败,请重新支付') + closeQrCodeDialog() + } else { + // pending 状态继续轮询 + isCheckingPayment.value = false + } + } catch (error) { + console.error('查询微信订单状态失败:', error) + isCheckingPayment.value = false + // 不显示错误,继续轮询 + } +} + +// 组件卸载时清理定时器 +onBeforeUnmount(() => { + stopWechatOrderPolling() +}) diff --git a/src/pages/finance/recharge-records/index.vue b/src/pages/finance/recharge-records/index.vue index b6c8599..9200a36 100644 --- a/src/pages/finance/recharge-records/index.vue +++ b/src/pages/finance/recharge-records/index.vue @@ -14,6 +14,7 @@ class="w-full" > + @@ -100,6 +101,10 @@ 支付宝订单: {{ row.alipay_order_id }} +
+ 微信订单: + {{ row.wechat_order_id }} +
转账订单: {{ row.transfer_order_id }} @@ -203,11 +208,19 @@ let searchTimer = null const loadRecords = async () => { loading.value = true try { + // 构建参数,过滤掉空值 const params = { page: currentPage.value, - page_size: pageSize.value, - ...filters + page_size: pageSize.value } + + // 只添加非空的筛选条件 + Object.keys(filters).forEach(key => { + const value = filters[key] + if (value !== '' && value !== null && value !== undefined) { + params[key] = value + } + }) const response = await financeApi.getUserRechargeRecords(params) records.value = response.data?.items || [] @@ -247,6 +260,7 @@ const formatTime = (date) => { const getRechargeTypeTagType = (type) => { const typeMap = { alipay: 'primary', + wechat: 'success', transfer: 'warning', gift: 'success' } @@ -257,6 +271,7 @@ const getRechargeTypeTagType = (type) => { const getRechargeTypeText = (type) => { const typeMap = { alipay: '支付宝充值', + wechat: '微信充值', transfer: '对公转账', gift: '赠送' } diff --git a/src/pages/products/detail.vue b/src/pages/products/detail.vue index 67fe782..68a3808 100644 --- a/src/pages/products/detail.vue +++ b/src/pages/products/detail.vue @@ -3,15 +3,21 @@
-
-
+
+

{{ product?.name || '产品详情' }}

{{ product?.description || '查看产品详细信息' }}

-
- 返回 +
+ + 返回 + @@ -60,7 +69,7 @@

基本信息

-
+
{{ product.code }} @@ -270,6 +279,7 @@