增强和平小店账号管理
This commit is contained in:
Generated
+331
@@ -9,7 +9,9 @@
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@tanstack/vue-virtual": "^3.11.2",
|
||||
"@types/qrcode": "^1.5.6",
|
||||
"pinia": "^2.3.0",
|
||||
"qrcode": "^1.5.4",
|
||||
"vue": "^3.5.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -935,6 +937,24 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "26.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.2.0.tgz",
|
||||
"integrity": "sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~8.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/qrcode": {
|
||||
"version": "1.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.6.tgz",
|
||||
"integrity": "sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitejs/plugin-vue": {
|
||||
"version": "5.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz",
|
||||
@@ -1127,6 +1147,30 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"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.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
@@ -1144,6 +1188,44 @@
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"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/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.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||
@@ -1157,6 +1239,27 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"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/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/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/entities": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
|
||||
@@ -1235,6 +1338,19 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"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/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
@@ -1250,6 +1366,15 @@
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.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/he": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
|
||||
@@ -1260,6 +1385,27 @@
|
||||
"he": "bin/he"
|
||||
}
|
||||
},
|
||||
"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/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/magic-string": {
|
||||
"version": "0.30.21",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
||||
@@ -1310,6 +1456,42 @@
|
||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||
}
|
||||
},
|
||||
"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/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/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/path-browserify": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
|
||||
@@ -1317,6 +1499,15 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/path-exists": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
@@ -1358,6 +1549,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"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.15",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
|
||||
@@ -1386,6 +1586,38 @@
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"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/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/rollup": {
|
||||
"version": "4.62.2",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz",
|
||||
@@ -1431,6 +1663,12 @@
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"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/source-map-js": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
@@ -1440,6 +1678,32 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"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/tinyglobby": {
|
||||
"version": "0.2.17",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
|
||||
@@ -1471,6 +1735,12 @@
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz",
|
||||
"integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "6.4.3",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz",
|
||||
@@ -1616,6 +1886,67 @@
|
||||
"peerDependencies": {
|
||||
"typescript": ">=5.0.0"
|
||||
}
|
||||
},
|
||||
"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/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/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/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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,11 @@
|
||||
"type-check": "vue-tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^3.5.13",
|
||||
"@tanstack/vue-virtual": "^3.11.2",
|
||||
"@types/qrcode": "^1.5.6",
|
||||
"pinia": "^2.3.0",
|
||||
"@tanstack/vue-virtual": "^3.11.2"
|
||||
"qrcode": "^1.5.4",
|
||||
"vue": "^3.5.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
|
||||
@@ -1 +1 @@
|
||||
6ead8f3487bf170694573d8c0420d83c
|
||||
27d72c03cb2e439b5e40cec2bc0c9f2b
|
||||
@@ -9,6 +9,7 @@ import { useActivity } from '@/composables/useActivity'
|
||||
import AppShell from '@/components/layout/AppShell.vue'
|
||||
import ToastContainer from '@/components/ToastContainer.vue'
|
||||
import { GetTime, GetVersion } from '@wails/go/app/App'
|
||||
import { WindowGetSize, WindowSetSize } from '@wails/runtime/runtime'
|
||||
|
||||
const { initTheme } = useTheme()
|
||||
const { loadProducts, restoreSelection: restoreProductSelection } = useProducts()
|
||||
@@ -25,6 +26,27 @@ const serverTime = ref('--')
|
||||
let timeTimer: number | null = null
|
||||
let logTimer: number | null = null
|
||||
let resultTimer: number | null = null
|
||||
let windowSizeTimer: number | null = null
|
||||
|
||||
const restoreWindowSize = async () => {
|
||||
try {
|
||||
const saved = JSON.parse(localStorage.getItem('windowSize') || '{}')
|
||||
const width = Number(saved.width)
|
||||
const height = Number(saved.height)
|
||||
if (Number.isFinite(width) && Number.isFinite(height) && width >= 1100 && height >= 700) {
|
||||
WindowSetSize(Math.round(width), Math.round(height))
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const persistWindowSize = async () => {
|
||||
try {
|
||||
const size = await WindowGetSize()
|
||||
if (size && size.w >= 1100 && size.h >= 700) {
|
||||
localStorage.setItem('windowSize', JSON.stringify({ width: size.w, height: size.h }))
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const updateTime = async () => {
|
||||
try {
|
||||
@@ -49,6 +71,8 @@ const stopPolling = () => {
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await restoreWindowSize()
|
||||
|
||||
// 初始化主题
|
||||
initTheme()
|
||||
|
||||
@@ -77,10 +101,13 @@ onMounted(async () => {
|
||||
updateTime()
|
||||
startPolling()
|
||||
fetchServerLogs()
|
||||
await persistWindowSize()
|
||||
windowSizeTimer = window.setInterval(persistWindowSize, 1500)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
stopPolling()
|
||||
if (windowSizeTimer) clearInterval(windowSizeTimer)
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import QRCode from 'qrcode'
|
||||
import { useAccounts } from '@/composables/useAccounts'
|
||||
import { useProducts } from '@/composables/useProducts'
|
||||
import type { Product } from '@/composables/useProducts'
|
||||
@@ -13,6 +14,11 @@ const {
|
||||
toggleAll,
|
||||
getScore,
|
||||
getXiaodianAssets,
|
||||
getXiaodianRole,
|
||||
deleteAccount,
|
||||
xiaodianQRCode,
|
||||
bindXiaodianRole,
|
||||
closeXiaodianQRCode,
|
||||
getAccountProduct,
|
||||
setAccountProduct
|
||||
} = useAccounts()
|
||||
@@ -21,6 +27,22 @@ const { products, selectedProductName } = useProducts()
|
||||
const { currentActivity } = useActivity()
|
||||
const isXiaodian = computed(() => currentActivity.value?.id === 'xiaodian')
|
||||
const productFilterMap = ref<Record<string, string>>({})
|
||||
const qrImage = ref('')
|
||||
|
||||
watch(xiaodianQRCode, async (value) => {
|
||||
qrImage.value = ''
|
||||
if (value?.url) {
|
||||
try {
|
||||
qrImage.value = await QRCode.toDataURL(value.url, { width: 240, margin: 2 })
|
||||
} catch {}
|
||||
}
|
||||
})
|
||||
|
||||
const roleChannel = (type?: string) => type === 'wx' ? '微信' : type === 'qq' ? '手Q' : (type || '')
|
||||
|
||||
const handleBind = async () => {
|
||||
await bindXiaodianRole()
|
||||
}
|
||||
|
||||
const allSelected = computed(() =>
|
||||
accounts.value.length > 0 && selectedIndices.value.size === accounts.value.length
|
||||
@@ -102,39 +124,44 @@ const getResultMessage = (name: string) => {
|
||||
<span class="panel-title">账号作战表</span>
|
||||
<span class="panel-sub">{{ accounts.length }} 个账号,已选 {{ selectedIndices.size }} 个</span>
|
||||
</div>
|
||||
<label class="stat-pill">
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="allSelected"
|
||||
@change="toggleAll"
|
||||
>
|
||||
全选
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="select-cell"></th>
|
||||
<th class="index-cell">序号</th>
|
||||
<th class="select-cell">
|
||||
<label class="select-all">
|
||||
<span>全选</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="allSelected"
|
||||
@change="toggleAll"
|
||||
>
|
||||
</label>
|
||||
</th>
|
||||
<th>账号</th>
|
||||
<th>凭据</th>
|
||||
<th>商品</th>
|
||||
<th>{{ isXiaodian ? '点券' : '积分' }}</th>
|
||||
<th v-if="isXiaodian">扭蛋碎片</th>
|
||||
<th v-if="isXiaodian">角色信息</th>
|
||||
<th class="status-col">状态</th>
|
||||
<th>最后结果</th>
|
||||
<th class="account-action-col">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-if="accounts.length === 0">
|
||||
<td :colspan="isXiaodian ? 8 : 7" class="empty-state">暂无账号,请加载账号</td>
|
||||
<td :colspan="isXiaodian ? 11 : 9" class="empty-state">暂无账号,请加载账号</td>
|
||||
</tr>
|
||||
<template v-else>
|
||||
<tr
|
||||
v-for="(account, index) in accounts"
|
||||
:key="account.name"
|
||||
>
|
||||
<td class="index-cell">{{ index + 1 }}</td>
|
||||
<td class="select-cell">
|
||||
<label class="account-check" @click="toggleAccount(index)">
|
||||
<div class="check-box" :class="{ checked: selectedIndices.has(index) }">
|
||||
@@ -200,6 +227,18 @@ const getResultMessage = (name: string) => {
|
||||
<td v-if="isXiaodian">
|
||||
{{ getXiaodianAssets(account.name)?.success ? getXiaodianAssets(account.name)?.fragments : '-' }}
|
||||
</td>
|
||||
<td v-if="isXiaodian" class="role-cell">
|
||||
<template v-if="getXiaodianRole(account.name)?.success">
|
||||
<div class="role-name">{{ getXiaodianRole(account.name)?.roleName || '-' }}</div>
|
||||
<div class="account-sub">
|
||||
{{ roleChannel(getXiaodianRole(account.name)?.type) }}
|
||||
<span v-if="getXiaodianRole(account.name)?.areaId"> · 大区{{ getXiaodianRole(account.name)?.areaId }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<span v-else class="account-sub" :title="getXiaodianRole(account.name)?.error || ''">
|
||||
{{ getXiaodianRole(account.name)?.error || '-' }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="status-td">
|
||||
<span
|
||||
class="status-badge"
|
||||
@@ -213,12 +252,43 @@ const getResultMessage = (name: string) => {
|
||||
{{ getResultMessage(account.name) }}
|
||||
</span>
|
||||
</td>
|
||||
<td class="account-action-cell">
|
||||
<button
|
||||
class="btn btn-compact btn-account-delete"
|
||||
title="删除账号及其 CK"
|
||||
@click.stop="deleteAccount(account.name)"
|
||||
>
|
||||
删除账号
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="xiaodianQRCode" class="qr-backdrop" @click.self="closeXiaodianQRCode">
|
||||
<section class="qr-modal" role="dialog" aria-modal="true" aria-label="和平小店绑定二维码">
|
||||
<div class="qr-modal-head">
|
||||
<div>
|
||||
<div class="panel-title">绑定和平小店角色</div>
|
||||
<div class="panel-sub">账号:{{ xiaodianQRCode.accountName }}</div>
|
||||
</div>
|
||||
<button class="btn btn-ghost icon-btn" title="关闭" aria-label="关闭" @click="closeXiaodianQRCode">×</button>
|
||||
</div>
|
||||
<div class="qr-modal-body">
|
||||
<img v-if="qrImage" :src="qrImage" alt="和平小店绑定二维码" class="qr-image">
|
||||
<div v-else class="qr-loading">二维码生成中...</div>
|
||||
<p class="qr-hint">请使用微信扫码选择并绑定游戏角色,完成后点击“绑定”。</p>
|
||||
<a v-if="xiaodianQRCode.gameRoleBindUrl" :href="xiaodianQRCode.gameRoleBindUrl" target="_blank" rel="noreferrer" class="qr-link">打开绑定页面</a>
|
||||
</div>
|
||||
<div class="qr-modal-foot">
|
||||
<button class="btn" @click="closeXiaodianQRCode">取消</button>
|
||||
<button class="btn btn-primary" @click="handleBind">绑定</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
@@ -257,15 +327,6 @@ const getResultMessage = (name: string) => {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.panel-head label.stat-pill {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.panel-head label.stat-pill input {
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.table-wrap {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
@@ -317,15 +378,131 @@ tbody tr:hover {
|
||||
width: 36px;
|
||||
}
|
||||
|
||||
.select-all {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
cursor: pointer;
|
||||
text-transform: none;
|
||||
letter-spacing: 0;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.select-all input {
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.index-cell {
|
||||
width: 48px;
|
||||
text-align: center;
|
||||
color: var(--muted);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.status-col {
|
||||
width: 96px;
|
||||
}
|
||||
|
||||
.account-action-col,
|
||||
.account-action-cell {
|
||||
width: 92px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.btn-account-delete {
|
||||
color: var(--danger);
|
||||
border-color: var(--danger);
|
||||
background: var(--danger-soft);
|
||||
}
|
||||
|
||||
.account-name {
|
||||
font-weight: 500;
|
||||
color: var(--text-strong);
|
||||
}
|
||||
|
||||
.role-cell {
|
||||
min-width: 130px;
|
||||
}
|
||||
|
||||
.role-name {
|
||||
color: var(--text-strong);
|
||||
font-weight: 500;
|
||||
max-width: 150px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.qr-backdrop {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 20;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 20px;
|
||||
background: rgba(0, 0, 0, .58);
|
||||
}
|
||||
|
||||
.qr-modal {
|
||||
width: min(380px, 100%);
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--line-strong);
|
||||
border-radius: var(--radius);
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.qr-modal-head,
|
||||
.qr-modal-foot {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.qr-modal-head {
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.qr-modal-foot {
|
||||
justify-content: flex-end;
|
||||
border-top: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.qr-modal-body {
|
||||
padding: 20px 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.qr-image {
|
||||
display: block;
|
||||
width: 240px;
|
||||
height: 240px;
|
||||
margin: 0 auto 12px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.qr-loading {
|
||||
height: 240px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.qr-hint {
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.qr-link {
|
||||
display: inline-block;
|
||||
margin-top: 8px;
|
||||
color: var(--accent);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.account-sub {
|
||||
font-size: 11px;
|
||||
color: var(--muted);
|
||||
|
||||
@@ -300,6 +300,7 @@ const handleOrganize = async () => {
|
||||
<table class="managed-account-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="managed-index">序号</th>
|
||||
<th class="managed-check"></th>
|
||||
<th>账号</th>
|
||||
<th>CK</th>
|
||||
@@ -308,10 +309,11 @@ const handleOrganize = async () => {
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-if="accounts.length === 0">
|
||||
<td colspan="4" class="managed-empty">暂无账号</td>
|
||||
<td colspan="5" class="managed-empty">暂无账号</td>
|
||||
</tr>
|
||||
<template v-else>
|
||||
<tr v-for="account in accounts" :key="account.name">
|
||||
<tr v-for="(account, index) in accounts" :key="account.name">
|
||||
<td class="managed-index">{{ index + 1 }}</td>
|
||||
<td class="managed-check">
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -618,6 +620,13 @@ const handleOrganize = async () => {
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
.managed-index {
|
||||
width: 48px;
|
||||
text-align: center !important;
|
||||
color: var(--muted);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.managed-name {
|
||||
overflow: hidden;
|
||||
color: var(--text-strong);
|
||||
|
||||
@@ -7,7 +7,15 @@ import { useProducts } from '@/composables/useProducts'
|
||||
import { useToast } from '@/composables/useToast'
|
||||
|
||||
const { scheduleActive, seckillRunning, startNow, stopSeckill, startFixed, startSchedule, cancelSchedule, restoreSchedule, subscribeSeckillState, unsubscribeSchedulerEvents } = useScheduler()
|
||||
const { reloadAccounts, queryAllScore, queryAllXiaodianAssets } = useAccounts()
|
||||
const {
|
||||
accounts,
|
||||
selectedIndices,
|
||||
reloadAccounts,
|
||||
queryAllScore,
|
||||
queryAllXiaodianAssets,
|
||||
queryAllXiaodianRoles,
|
||||
getXiaodianBindQRCode
|
||||
} = useAccounts()
|
||||
const { currentActivity } = useActivity()
|
||||
const isXiaodian = computed(() => currentActivity.value?.id === 'xiaodian')
|
||||
const { products, selectedProductId, loadProducts, selectProduct } = useProducts()
|
||||
@@ -50,6 +58,20 @@ const queryAssets = () => {
|
||||
queryAllScore()
|
||||
}
|
||||
|
||||
const queryRoles = () => {
|
||||
if (isXiaodian.value) queryAllXiaodianRoles()
|
||||
}
|
||||
|
||||
const openBindQRCode = async () => {
|
||||
if (selectedIndices.value.size !== 1) {
|
||||
showToast('获取绑定二维码', '请勾选一个账号后再操作', 'warn')
|
||||
return
|
||||
}
|
||||
const index = [...selectedIndices.value][0]
|
||||
const account = accounts.value[index]
|
||||
if (account) await getXiaodianBindQRCode(account.name)
|
||||
}
|
||||
|
||||
const saveFixedConfig = () => {
|
||||
localStorage.setItem('xiaodianFixedKeyword', fixedKeyword.value.trim())
|
||||
localStorage.setItem('xiaodianWatchKeyword', fixedKeyword.value.trim())
|
||||
@@ -177,6 +199,8 @@ onUnmounted(() => {
|
||||
</button>
|
||||
<button class="btn" @click="reloadAccounts">加载账号</button>
|
||||
<button class="btn" @click="queryAssets">{{ isXiaodian ? '查询点券/碎片' : '查询积分' }}</button>
|
||||
<button v-if="isXiaodian" class="btn" @click="queryRoles">查询角色</button>
|
||||
<button v-if="isXiaodian" class="btn btn-primary" @click="openBindQRCode">获取绑定二维码</button>
|
||||
<button v-if="isXiaodian" class="btn btn-fixed" @click="startFixedBuy">固定抢购</button>
|
||||
</div>
|
||||
<div class="schedule-item target-item">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
import { useTheme } from '@/composables/useTheme'
|
||||
import { useScheduler } from '@/composables/useScheduler'
|
||||
import StatusGrid from '@/components/console/StatusGrid.vue'
|
||||
@@ -19,10 +19,70 @@ const { runState, countdownSeconds, formatRemain } = useScheduler()
|
||||
|
||||
const settingsModalRef = ref<InstanceType<typeof SettingsModal> | null>(null)
|
||||
const currentPage = ref<'console' | 'ck' | 'exchange'>('console')
|
||||
const workspaceRef = ref<HTMLElement | null>(null)
|
||||
const resizingWorkspace = ref(false)
|
||||
const compactViewport = ref(false)
|
||||
const storedWorkspaceRatio = Number(localStorage.getItem('accountPanelRatio'))
|
||||
const accountPanelRatio = ref(
|
||||
Number.isFinite(storedWorkspaceRatio) && storedWorkspaceRatio >= 0.55 && storedWorkspaceRatio <= 0.84
|
||||
? storedWorkspaceRatio
|
||||
: 0.76
|
||||
)
|
||||
|
||||
const workspaceStyle = computed(() => ({
|
||||
gridTemplateColumns: compactViewport.value
|
||||
? '1fr'
|
||||
: `minmax(0, ${accountPanelRatio.value}fr) 8px minmax(260px, ${1 - accountPanelRatio.value}fr)`
|
||||
}))
|
||||
|
||||
const openSettings = () => {
|
||||
settingsModalRef.value?.open()
|
||||
}
|
||||
|
||||
const updateWorkspaceRatio = (clientX: number) => {
|
||||
const workspace = workspaceRef.value
|
||||
if (!workspace) return
|
||||
const rect = workspace.getBoundingClientRect()
|
||||
const next = (clientX - rect.left) / rect.width
|
||||
accountPanelRatio.value = Math.max(0.55, Math.min(0.84, next))
|
||||
}
|
||||
|
||||
const startWorkspaceResize = (event: PointerEvent) => {
|
||||
if (window.matchMedia('(max-width: 1180px)').matches) return
|
||||
resizingWorkspace.value = true
|
||||
updateWorkspaceRatio(event.clientX)
|
||||
document.body.classList.add('resizing-columns')
|
||||
window.addEventListener('pointermove', handleWorkspacePointerMove)
|
||||
window.addEventListener('pointerup', stopWorkspaceResize, { once: true })
|
||||
}
|
||||
|
||||
const handleWorkspacePointerMove = (event: PointerEvent) => {
|
||||
if (resizingWorkspace.value) updateWorkspaceRatio(event.clientX)
|
||||
}
|
||||
|
||||
const stopWorkspaceResize = () => {
|
||||
if (!resizingWorkspace.value) return
|
||||
resizingWorkspace.value = false
|
||||
localStorage.setItem('accountPanelRatio', String(accountPanelRatio.value))
|
||||
document.body.classList.remove('resizing-columns')
|
||||
window.removeEventListener('pointermove', handleWorkspacePointerMove)
|
||||
}
|
||||
|
||||
const updateCompactViewport = () => {
|
||||
compactViewport.value = window.matchMedia('(max-width: 1180px)').matches
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
updateCompactViewport()
|
||||
window.addEventListener('resize', updateCompactViewport)
|
||||
window.addEventListener('pointerup', stopWorkspaceResize)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
stopWorkspaceResize()
|
||||
window.removeEventListener('resize', updateCompactViewport)
|
||||
window.removeEventListener('pointerup', stopWorkspaceResize)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -87,8 +147,14 @@ const openSettings = () => {
|
||||
<ControlStrip />
|
||||
|
||||
<!-- 工作区 -->
|
||||
<section class="workspace">
|
||||
<section ref="workspaceRef" class="workspace" :class="{ resizing: resizingWorkspace }" :style="workspaceStyle">
|
||||
<AccountTable />
|
||||
<div
|
||||
class="workspace-resizer"
|
||||
role="separator"
|
||||
aria-label="调整账号表和日志宽度"
|
||||
@pointerdown="startWorkspaceResize"
|
||||
></div>
|
||||
<aside class="side-stack">
|
||||
<LogPanel />
|
||||
</aside>
|
||||
@@ -220,7 +286,7 @@ const openSettings = () => {
|
||||
|
||||
.workspace {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) 380px;
|
||||
grid-template-columns: minmax(0, 1fr) 8px minmax(260px, 1fr);
|
||||
gap: 14px;
|
||||
padding: 14px 20px 20px;
|
||||
align-items: stretch;
|
||||
@@ -229,6 +295,25 @@ const openSettings = () => {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.workspace-resizer {
|
||||
width: 8px;
|
||||
min-height: 100%;
|
||||
border-radius: 4px;
|
||||
cursor: col-resize;
|
||||
transition: background-color .12s ease;
|
||||
}
|
||||
|
||||
.workspace-resizer:hover,
|
||||
.workspace.resizing .workspace-resizer {
|
||||
background: var(--accent-border);
|
||||
}
|
||||
|
||||
:global(body.resizing-columns),
|
||||
:global(body.resizing-columns *) {
|
||||
cursor: col-resize !important;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.side-stack {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -254,5 +339,9 @@ const openSettings = () => {
|
||||
padding: 14px;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.workspace-resizer {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -3,6 +3,10 @@ import { useToast } from './useToast'
|
||||
import {
|
||||
GetAccounts,
|
||||
QueryXiaodianAssets,
|
||||
QueryXiaodianRoles,
|
||||
GetXiaodianBindQRCode,
|
||||
BindXiaodianRole,
|
||||
DeleteAccounts,
|
||||
QueryScore,
|
||||
ReloadAccounts,
|
||||
VerifyAllExchange,
|
||||
@@ -33,6 +37,25 @@ export interface XiaodianAssets {
|
||||
error?: string
|
||||
}
|
||||
const xiaodianAssetsMap = ref<Record<string, XiaodianAssets>>({})
|
||||
export interface XiaodianRole {
|
||||
success: boolean
|
||||
roleId?: string
|
||||
roleName?: string
|
||||
openId?: string
|
||||
type?: string
|
||||
platId?: number
|
||||
areaId?: number
|
||||
error?: string
|
||||
}
|
||||
export interface XiaodianQRCode {
|
||||
success: boolean
|
||||
accountName?: string
|
||||
url?: string
|
||||
gameRoleBindUrl?: string
|
||||
error?: string
|
||||
}
|
||||
const xiaodianRolesMap = ref<Record<string, XiaodianRole>>({})
|
||||
const xiaodianQRCode = ref<XiaodianQRCode | null>(null)
|
||||
|
||||
export function useAccounts() {
|
||||
const { showToast } = useToast()
|
||||
@@ -54,6 +77,32 @@ export function useAccounts() {
|
||||
showToast('账号已刷新', `共 ${data.count} 个账号`, 'success')
|
||||
}
|
||||
|
||||
const deleteAccount = async (accountName: string) => {
|
||||
if (!accountName) return false
|
||||
if (!window.confirm(`确定删除账号「${accountName}」及其 CK 吗?`)) return false
|
||||
try {
|
||||
const result = await DeleteAccounts([accountName])
|
||||
if (!result.success) {
|
||||
showToast('删除失败', result.error || '账号文件未更新', 'error')
|
||||
return false
|
||||
}
|
||||
selectedIndices.value = new Set()
|
||||
saveSelection()
|
||||
delete statusMap.value[accountName]
|
||||
delete accountProductMap.value[accountName]
|
||||
delete xiaodianAssetsMap.value[accountName]
|
||||
delete xiaodianRolesMap.value[accountName]
|
||||
localStorage.setItem('statusMap', JSON.stringify(statusMap.value))
|
||||
localStorage.setItem('accountProducts', JSON.stringify(accountProductMap.value))
|
||||
await loadAccounts()
|
||||
showToast('删除成功', `已删除 ${accountName} 及其 CK`, 'success')
|
||||
return true
|
||||
} catch (e: any) {
|
||||
showToast('删除失败', e?.message || '', 'error')
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const toggleAccount = (index: number) => {
|
||||
if (selectedIndices.value.has(index)) {
|
||||
selectedIndices.value.delete(index)
|
||||
@@ -117,17 +166,91 @@ export function useAccounts() {
|
||||
}
|
||||
|
||||
const queryAllXiaodianAssets = async () => {
|
||||
const accountNames = selectedAccountNames()
|
||||
if (accountNames.length === 0) {
|
||||
showToast('查询点券和碎片', '请先勾选要查询的账号', 'warn')
|
||||
return
|
||||
}
|
||||
showToast('查询点券和碎片', '正在查询账号资产...', 'info')
|
||||
try {
|
||||
const data = await QueryXiaodianAssets()
|
||||
xiaodianAssetsMap.value = data?.results || {}
|
||||
const successCount = Object.values(xiaodianAssetsMap.value).filter(item => item.success).length
|
||||
showToast('资产查询完成', `成功 ${successCount}/${accounts.value.length} 个账号`, successCount > 0 ? 'success' : 'warn')
|
||||
const data = await QueryXiaodianAssets(accountNames)
|
||||
const queriedResults = data?.results || {}
|
||||
xiaodianAssetsMap.value = {
|
||||
...xiaodianAssetsMap.value,
|
||||
...queriedResults
|
||||
}
|
||||
const successCount = Object.values(queriedResults).filter(item => item.success).length
|
||||
showToast('资产查询完成', `成功 ${successCount}/${accountNames.length} 个账号`, successCount > 0 ? 'success' : 'warn')
|
||||
} catch (e: any) {
|
||||
showToast('资产查询失败', e?.message || '', 'error')
|
||||
}
|
||||
}
|
||||
|
||||
const queryAllXiaodianRoles = async () => {
|
||||
const accountNames = selectedAccountNames()
|
||||
if (accountNames.length === 0) {
|
||||
showToast('查询角色信息', '请先勾选要查询的账号', 'warn')
|
||||
return
|
||||
}
|
||||
showToast('查询角色信息', '正在查询小店绑定角色...', 'info')
|
||||
try {
|
||||
const data = await QueryXiaodianRoles(accountNames)
|
||||
const queriedResults = data?.results || {}
|
||||
xiaodianRolesMap.value = {
|
||||
...xiaodianRolesMap.value,
|
||||
...queriedResults
|
||||
}
|
||||
const successCount = Object.values(queriedResults).filter(item => item.success).length
|
||||
showToast('角色查询完成', `成功 ${successCount}/${accountNames.length} 个账号`, successCount > 0 ? 'success' : 'warn')
|
||||
} catch (e: any) {
|
||||
showToast('角色查询失败', e?.message || '', 'error')
|
||||
}
|
||||
}
|
||||
|
||||
const getXiaodianRole = (name: string): XiaodianRole | undefined => xiaodianRolesMap.value[name]
|
||||
|
||||
const getXiaodianBindQRCode = async (accountName: string) => {
|
||||
if (!accountName) {
|
||||
showToast('获取二维码', '请先选择一个账号', 'warn')
|
||||
return false
|
||||
}
|
||||
try {
|
||||
const result = await GetXiaodianBindQRCode(accountName)
|
||||
if (!result?.success || !result.url) {
|
||||
xiaodianQRCode.value = null
|
||||
showToast('获取二维码失败', result?.error || '接口未返回二维码链接', 'error')
|
||||
return false
|
||||
}
|
||||
xiaodianQRCode.value = result
|
||||
return true
|
||||
} catch (e: any) {
|
||||
showToast('获取二维码失败', e?.message || '', 'error')
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const bindXiaodianRole = async () => {
|
||||
const accountName = xiaodianQRCode.value?.accountName || ''
|
||||
if (!accountName) return false
|
||||
try {
|
||||
const result = await BindXiaodianRole(accountName)
|
||||
xiaodianRolesMap.value = { ...xiaodianRolesMap.value, [accountName]: result }
|
||||
if (!result?.success || !result.roleName) {
|
||||
showToast('绑定失败', result?.error || '尚未检测到绑定角色,请先扫码', 'warn')
|
||||
return false
|
||||
}
|
||||
showToast('绑定成功', `${accountName}:${result.roleName}`, 'success')
|
||||
return true
|
||||
} catch (e: any) {
|
||||
showToast('绑定失败', e?.message || '', 'error')
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const closeXiaodianQRCode = () => {
|
||||
xiaodianQRCode.value = null
|
||||
}
|
||||
|
||||
const getScore = (name: string): string => {
|
||||
return scoreMap.value[name] || localStorage.getItem('accScore_' + name) || '-'
|
||||
}
|
||||
@@ -225,15 +348,23 @@ export function useAccounts() {
|
||||
statusMap,
|
||||
scoreMap,
|
||||
xiaodianAssetsMap,
|
||||
xiaodianRolesMap,
|
||||
xiaodianQRCode,
|
||||
accountProductMap,
|
||||
loadAccounts,
|
||||
reloadAccounts,
|
||||
deleteAccount,
|
||||
toggleAccount,
|
||||
toggleAll,
|
||||
clearSelection,
|
||||
restoreSelection,
|
||||
queryAllScore,
|
||||
queryAllXiaodianAssets,
|
||||
queryAllXiaodianRoles,
|
||||
getXiaodianRole,
|
||||
getXiaodianBindQRCode,
|
||||
bindXiaodianRole,
|
||||
closeXiaodianQRCode,
|
||||
getScore,
|
||||
getXiaodianAssets,
|
||||
resetStatus,
|
||||
|
||||
@@ -105,6 +105,7 @@ var xiaodian = &Activity{
|
||||
StoreHomeURL: "https://apps.game.qq.com/daoju/v3/recommend_xn_live_cjm/common",
|
||||
RoleParamURL: "https://www.douyu.com/japi/carnival/nc/txEmbed/getIframeUrl",
|
||||
RoleURL: "https://apps.game.qq.com/daoju/igw/live",
|
||||
BindInfoURL: "https://www.douyu.com/japi/carnivalApi/v2/tencent/bindInfo",
|
||||
IsXiaodian: true,
|
||||
}
|
||||
|
||||
|
||||
@@ -37,13 +37,22 @@ func (a *App) QueryScore(accountName string) types.ScoreResult {
|
||||
return types.ScoreResult{Score: strconv.Itoa(score)}
|
||||
}
|
||||
|
||||
// QueryXiaodianAssets 一次查询所有账号的和平小店点券和扭蛋碎片。
|
||||
func (a *App) QueryXiaodianAssets() types.XiaodianAssetsResult {
|
||||
// QueryXiaodianAssets 查询勾选账号的和平小店点券和扭蛋碎片。
|
||||
func (a *App) QueryXiaodianAssets(accountNames []string) types.XiaodianAssetsResult {
|
||||
results := make(map[string]types.XiaodianAssets)
|
||||
if !a.getClient().Activity.IsXiaodian {
|
||||
return types.XiaodianAssetsResult{Results: results}
|
||||
}
|
||||
selected := make(map[string]struct{}, len(accountNames))
|
||||
for _, name := range accountNames {
|
||||
if name = strings.TrimSpace(name); name != "" {
|
||||
selected[name] = struct{}{}
|
||||
}
|
||||
}
|
||||
for _, account := range a.scheduler.AccountSnapshot() {
|
||||
if _, ok := selected[account.Name]; !ok {
|
||||
continue
|
||||
}
|
||||
if strings.TrimSpace(account.Cookie) == "" {
|
||||
results[account.Name] = types.XiaodianAssets{Error: "Cookie 为空"}
|
||||
continue
|
||||
@@ -58,6 +67,75 @@ func (a *App) QueryXiaodianAssets() types.XiaodianAssetsResult {
|
||||
return types.XiaodianAssetsResult{Results: results}
|
||||
}
|
||||
|
||||
// QueryXiaodianRoles 查询勾选账号当前绑定的和平小店角色。
|
||||
func (a *App) QueryXiaodianRoles(accountNames []string) types.XiaodianRoleResult {
|
||||
results := make(map[string]types.XiaodianRole)
|
||||
if !a.getClient().Activity.IsXiaodian {
|
||||
return types.XiaodianRoleResult{Results: results}
|
||||
}
|
||||
selected := make(map[string]struct{}, len(accountNames))
|
||||
for _, name := range accountNames {
|
||||
if name = strings.TrimSpace(name); name != "" {
|
||||
selected[name] = struct{}{}
|
||||
}
|
||||
}
|
||||
for _, account := range a.scheduler.AccountSnapshot() {
|
||||
if _, ok := selected[account.Name]; !ok {
|
||||
continue
|
||||
}
|
||||
if strings.TrimSpace(account.Cookie) == "" {
|
||||
results[account.Name] = types.XiaodianRole{Error: "Cookie 为空"}
|
||||
continue
|
||||
}
|
||||
role, err := a.getClient().XiaodianRole(account.Cookie)
|
||||
if err != nil {
|
||||
results[account.Name] = types.XiaodianRole{Error: "查询失败: " + err.Error()}
|
||||
continue
|
||||
}
|
||||
results[account.Name] = role
|
||||
}
|
||||
return types.XiaodianRoleResult{Results: results}
|
||||
}
|
||||
|
||||
// GetXiaodianBindQRCode 为指定账号生成和平小店角色绑定二维码。
|
||||
func (a *App) GetXiaodianBindQRCode(accountName string) types.XiaodianBindQRCode {
|
||||
accountName = strings.TrimSpace(accountName)
|
||||
if !a.getClient().Activity.IsXiaodian {
|
||||
return types.XiaodianBindQRCode{Error: "当前活动不是和平小店"}
|
||||
}
|
||||
cookie, ok := a.scheduler.AccountCookieByName(accountName)
|
||||
if !ok || strings.TrimSpace(cookie) == "" {
|
||||
return types.XiaodianBindQRCode{Error: "账号不存在或 Cookie 为空"}
|
||||
}
|
||||
urlValue, bindURL, err := a.getClient().XiaodianBindQRCode(cookie)
|
||||
if err != nil {
|
||||
return types.XiaodianBindQRCode{Error: "获取绑定二维码失败: " + err.Error()}
|
||||
}
|
||||
return types.XiaodianBindQRCode{
|
||||
Success: true,
|
||||
AccountName: accountName,
|
||||
URL: urlValue,
|
||||
GameRoleBindURL: bindURL,
|
||||
}
|
||||
}
|
||||
|
||||
// BindXiaodianRole 确认扫码后的绑定,并返回最新角色信息。
|
||||
func (a *App) BindXiaodianRole(accountName string) types.XiaodianRole {
|
||||
accountName = strings.TrimSpace(accountName)
|
||||
if !a.getClient().Activity.IsXiaodian {
|
||||
return types.XiaodianRole{Error: "当前活动不是和平小店"}
|
||||
}
|
||||
cookie, ok := a.scheduler.AccountCookieByName(accountName)
|
||||
if !ok || strings.TrimSpace(cookie) == "" {
|
||||
return types.XiaodianRole{Error: "账号不存在或 Cookie 为空"}
|
||||
}
|
||||
role, err := a.getClient().XiaodianBindRole(cookie)
|
||||
if err != nil {
|
||||
return types.XiaodianRole{Error: "绑定失败: " + err.Error()}
|
||||
}
|
||||
return role
|
||||
}
|
||||
|
||||
// VerifyExchange 查询兑换记录,用于复验抢购结果。
|
||||
func (a *App) VerifyExchange(accountName string) types.ExchangeRecordsResult {
|
||||
if accountName == "" {
|
||||
|
||||
+146
-5
@@ -289,13 +289,16 @@ func (s *xiaodianFlexibleString) UnmarshalJSON(data []byte) error {
|
||||
}
|
||||
|
||||
type xiaodianRoleContext struct {
|
||||
OpenID xiaodianFlexibleString `json:"gameOpenId"`
|
||||
RoleID xiaodianFlexibleString `json:"roleId"`
|
||||
Type xiaodianFlexibleString `json:"type"`
|
||||
Area xiaodianFlexibleString `json:"area"`
|
||||
PlatID xiaodianFlexibleString `json:"platId"`
|
||||
OpenID xiaodianFlexibleString `json:"gameOpenId"`
|
||||
RoleID xiaodianFlexibleString `json:"roleId"`
|
||||
RoleName xiaodianFlexibleString `json:"roleName"`
|
||||
Type xiaodianFlexibleString `json:"type"`
|
||||
Area xiaodianFlexibleString `json:"area"`
|
||||
PlatID xiaodianFlexibleString `json:"platId"`
|
||||
}
|
||||
|
||||
const xiaodianBindRoleParamURL = "https://apiv2.douyucdn.cn/japi/carnivalApi/tencent/roleParam"
|
||||
|
||||
type xiaodianOrderList struct {
|
||||
Data struct {
|
||||
Count xiaodianFlexibleString `json:"cnt"`
|
||||
@@ -655,6 +658,144 @@ func (c *DouyuClient) XiaodianAssets(cookie string) (types.XiaodianAssets, error
|
||||
return types.XiaodianAssets{Success: true, Coupons: coupons, Fragments: fragments}, nil
|
||||
}
|
||||
|
||||
// XiaodianRole 查询一个账号当前绑定的和平小店角色。
|
||||
func (c *DouyuClient) XiaodianRole(cookie string) (types.XiaodianRole, error) {
|
||||
if !c.Activity.IsXiaodian {
|
||||
return types.XiaodianRole{}, errors.New("当前活动不是和平小店")
|
||||
}
|
||||
if strings.TrimSpace(cookie) == "" {
|
||||
return types.XiaodianRole{}, errors.New("Cookie 为空")
|
||||
}
|
||||
embedQuery, err := c.xiaodianEmbedQuery(cookie)
|
||||
if err != nil {
|
||||
return types.XiaodianRole{}, err
|
||||
}
|
||||
role, err := c.xiaodianRole(embedQuery)
|
||||
if err != nil {
|
||||
return types.XiaodianRole{}, err
|
||||
}
|
||||
if strings.TrimSpace(string(role.RoleID)) == "" {
|
||||
return types.XiaodianRole{}, errors.New("未获取到和平小店绑定角色")
|
||||
}
|
||||
areaID := parseXiaodianInt(string(role.Area))
|
||||
if areaID <= 0 {
|
||||
areaID = 1
|
||||
}
|
||||
platID := parseXiaodianInt(string(role.PlatID))
|
||||
if platID <= 0 {
|
||||
platID = 1
|
||||
}
|
||||
result := types.XiaodianRole{
|
||||
Success: true,
|
||||
RoleID: string(role.RoleID),
|
||||
RoleName: string(role.RoleName),
|
||||
OpenID: string(role.OpenID),
|
||||
Type: string(role.Type),
|
||||
PlatID: platID,
|
||||
AreaID: areaID,
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// XiaodianBindRole 确认扫码后的和平小店角色,并返回最新角色信息。
|
||||
func (c *DouyuClient) XiaodianBindRole(cookie string) (types.XiaodianRole, error) {
|
||||
if !c.Activity.IsXiaodian {
|
||||
return types.XiaodianRole{}, errors.New("当前活动不是和平小店")
|
||||
}
|
||||
uid := cookieValue(cookie, "acf_uid")
|
||||
stk := cookieValue(cookie, "acf_stk")
|
||||
ltkid := cookieValue(cookie, "acf_ltkid")
|
||||
if uid == "" || stk == "" || ltkid == "" {
|
||||
return types.XiaodianRole{}, errors.New("斗鱼 Cookie 缺少 acf_uid、acf_stk 或 acf_ltkid")
|
||||
}
|
||||
endpoint, err := url.Parse(c.Activity.BindInfoURL)
|
||||
if err != nil {
|
||||
return types.XiaodianRole{}, err
|
||||
}
|
||||
query := endpoint.Query()
|
||||
query.Set("actAlias", c.Activity.ActAlias)
|
||||
query.Set("token", uid+"_1_"+stk+"_0_"+ltkid)
|
||||
endpoint.RawQuery = query.Encode()
|
||||
req, err := http.NewRequest(http.MethodGet, endpoint.String(), nil)
|
||||
if err != nil {
|
||||
return types.XiaodianRole{}, err
|
||||
}
|
||||
c.setBrowserHeaders(req, cookie)
|
||||
var bind struct {
|
||||
types.APIResponse
|
||||
Data struct {
|
||||
BindRole xiaodianFlexibleString `json:"bindRole"`
|
||||
} `json:"data"`
|
||||
}
|
||||
if _, err := c.doJSON(req, &bind, "确认小店绑定"); err != nil {
|
||||
return types.XiaodianRole{}, err
|
||||
}
|
||||
if bind.Error != 0 || parseXiaodianInt(string(bind.Data.BindRole)) == 0 {
|
||||
if bind.Msg != "" {
|
||||
return types.XiaodianRole{}, errors.New(bind.Msg)
|
||||
}
|
||||
return types.XiaodianRole{}, errors.New("尚未检测到小店绑定角色,请先扫码绑定")
|
||||
}
|
||||
return c.XiaodianRole(cookie)
|
||||
}
|
||||
|
||||
// XiaodianBindQRCode 生成一个账号专用的小店角色绑定二维码链接。
|
||||
func (c *DouyuClient) XiaodianBindQRCode(cookie string) (string, string, error) {
|
||||
if !c.Activity.IsXiaodian {
|
||||
return "", "", errors.New("当前活动不是和平小店")
|
||||
}
|
||||
uid := cookieValue(cookie, "acf_uid")
|
||||
stk := cookieValue(cookie, "acf_stk")
|
||||
ltkid := cookieValue(cookie, "acf_ltkid")
|
||||
if uid == "" || stk == "" || ltkid == "" {
|
||||
return "", "", errors.New("斗鱼 Cookie 缺少 acf_uid、acf_stk 或 acf_ltkid")
|
||||
}
|
||||
form := url.Values{
|
||||
"token": {uid + "_1_" + stk + "_0_" + ltkid},
|
||||
"uid": {uid},
|
||||
"client": {"mobile"},
|
||||
"actAlias": {c.Activity.ActAlias},
|
||||
}
|
||||
endpoint, err := url.Parse(xiaodianBindRoleParamURL)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
query := endpoint.Query()
|
||||
query.Set("client_sys", "android")
|
||||
endpoint.RawQuery = query.Encode()
|
||||
req, err := http.NewRequest(http.MethodPost, endpoint.String(), strings.NewReader(form.Encode()))
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.Header.Set("Accept", "application/json, text/plain, */*")
|
||||
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")
|
||||
req.Header.Set("User-Agent", "android/7.9.2 (android 9; ; M973Q)")
|
||||
req.Header.Set("User-Device", "MDExZTUxMzEyYmYxMmY5MmQ3YmY1M2M4MDA3MDcxMTF8djcuOS4y")
|
||||
req.Header.Set("aid", "android1")
|
||||
req.Header.Set("Cookie", cookie)
|
||||
var result struct {
|
||||
types.APIResponse
|
||||
Data struct {
|
||||
URL string `json:"url"`
|
||||
GameRoleBindURL string `json:"gameRoleBindUrl"`
|
||||
} `json:"data"`
|
||||
}
|
||||
if _, err := c.doJSON(req, &result, "生成小店绑定二维码"); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
if result.Error != 0 {
|
||||
if result.Msg != "" {
|
||||
return "", "", errors.New(result.Msg)
|
||||
}
|
||||
return "", "", errors.New("生成小店绑定二维码失败")
|
||||
}
|
||||
if result.Data.URL == "" {
|
||||
return "", "", errors.New("生成小店绑定二维码失败:响应缺少二维码链接")
|
||||
}
|
||||
return result.Data.URL, result.Data.GameRoleBindURL, nil
|
||||
}
|
||||
|
||||
// XiaodianExchangeList 查询和平小店真实订单记录。
|
||||
func (c *DouyuClient) XiaodianExchangeList(cookie string) ([]types.XiaodianExchangeRecord, error) {
|
||||
if !c.Activity.IsXiaodian {
|
||||
|
||||
@@ -147,7 +147,7 @@ func TestProductsAutomaticallyResolvesXiaodianRoleAndPaginates(t *testing.T) {
|
||||
if got := r.URL.Query().Get("_act_id"); got != "46195" {
|
||||
t.Errorf("_act_id = %q", got)
|
||||
}
|
||||
_, _ = w.Write([]byte(`var info = {"gameOpenId":"openid","roleId":1192083858,"type":"qq","area":1};`))
|
||||
_, _ = w.Write([]byte(`var info = {"gameOpenId":"openid","roleId":1192083858,"roleName":"测试角色","type":"qq","area":1};`))
|
||||
case "/role/":
|
||||
switch r.URL.Query().Get("_jsvar") {
|
||||
case "banlanceInfo":
|
||||
@@ -232,6 +232,10 @@ func TestProductsAutomaticallyResolvesXiaodianRoleAndPaginates(t *testing.T) {
|
||||
if assets.Coupons != 8800 || assets.Fragments != 36 {
|
||||
t.Fatalf("资产 = %#v", assets)
|
||||
}
|
||||
role, err := cli.XiaodianRole("acf_uid=uid; acf_stk=stk; acf_ltkid=ltkid")
|
||||
if err != nil || role.RoleName != "测试角色" || role.AreaID != 2 {
|
||||
t.Fatalf("角色 = %#v, err=%v", role, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestXiaodianExchangeProductUsesLiveBuyPayTypes(t *testing.T) {
|
||||
|
||||
@@ -24,6 +24,32 @@ type XiaodianProductListOptions struct {
|
||||
PageSize int
|
||||
}
|
||||
|
||||
// XiaodianRole 是和平小店当前绑定的游戏角色信息。
|
||||
type XiaodianRole struct {
|
||||
Success bool `json:"success"`
|
||||
RoleID string `json:"roleId,omitempty"`
|
||||
RoleName string `json:"roleName,omitempty"`
|
||||
OpenID string `json:"openId,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
PlatID int `json:"platId,omitempty"`
|
||||
AreaID int `json:"areaId,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// XiaodianRoleResult 是批量查询和平小店角色的结果。
|
||||
type XiaodianRoleResult struct {
|
||||
Results map[string]XiaodianRole `json:"results"`
|
||||
}
|
||||
|
||||
// XiaodianBindQRCode 是和平小店绑定二维码结果。
|
||||
type XiaodianBindQRCode struct {
|
||||
Success bool `json:"success"`
|
||||
AccountName string `json:"accountName,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
GameRoleBindURL string `json:"gameRoleBindUrl,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// ActivityInfo 活动概要,供前端下拉选择使用。
|
||||
type ActivityInfo struct {
|
||||
ID string `json:"id"`
|
||||
|
||||
Reference in New Issue
Block a user