first commit
Some checks failed
Check / lint (18.x, macos-latest) (push) Has been cancelled
Check / lint (18.x, ubuntu-latest) (push) Has been cancelled
Check / lint (18.x, windows-latest) (push) Has been cancelled
Check / lint (20.x, macos-latest) (push) Has been cancelled
Check / lint (20.x, ubuntu-latest) (push) Has been cancelled
Check / lint (20.x, windows-latest) (push) Has been cancelled
Check / lint (22.x, macos-latest) (push) Has been cancelled
Check / lint (22.x, ubuntu-latest) (push) Has been cancelled
Check / lint (22.x, windows-latest) (push) Has been cancelled
Check / typecheck (18.x, macos-latest) (push) Has been cancelled
Check / typecheck (18.x, ubuntu-latest) (push) Has been cancelled
Check / typecheck (18.x, windows-latest) (push) Has been cancelled
Check / typecheck (20.x, macos-latest) (push) Has been cancelled
Check / typecheck (20.x, ubuntu-latest) (push) Has been cancelled
Check / typecheck (20.x, windows-latest) (push) Has been cancelled
Check / typecheck (22.x, macos-latest) (push) Has been cancelled
Check / typecheck (22.x, ubuntu-latest) (push) Has been cancelled
Check / typecheck (22.x, windows-latest) (push) Has been cancelled
Check / build (build, 18.x, macos-latest) (push) Has been cancelled
Check / build (build, 18.x, ubuntu-latest) (push) Has been cancelled
Check / build (build, 18.x, windows-latest) (push) Has been cancelled
Check / build (build, 20.x, macos-latest) (push) Has been cancelled
Check / build (build, 20.x, ubuntu-latest) (push) Has been cancelled
Check / build (build, 20.x, windows-latest) (push) Has been cancelled
Check / build (build, 22.x, macos-latest) (push) Has been cancelled
Check / build (build, 22.x, ubuntu-latest) (push) Has been cancelled
Check / build (build, 22.x, windows-latest) (push) Has been cancelled
Check / build (build:app, 18.x, macos-latest) (push) Has been cancelled
Check / build (build:app, 18.x, ubuntu-latest) (push) Has been cancelled
Check / build (build:app, 18.x, windows-latest) (push) Has been cancelled
Check / build (build:app, 20.x, macos-latest) (push) Has been cancelled
Check / build (build:app, 20.x, ubuntu-latest) (push) Has been cancelled
Check / build (build:app, 20.x, windows-latest) (push) Has been cancelled
Check / build (build:app, 22.x, macos-latest) (push) Has been cancelled
Check / build (build:app, 22.x, ubuntu-latest) (push) Has been cancelled
Check / build (build:app, 22.x, windows-latest) (push) Has been cancelled
Check / build (build:mp-weixin, 18.x, macos-latest) (push) Has been cancelled
Check / build (build:mp-weixin, 18.x, ubuntu-latest) (push) Has been cancelled
Check / build (build:mp-weixin, 18.x, windows-latest) (push) Has been cancelled
Check / build (build:mp-weixin, 20.x, macos-latest) (push) Has been cancelled
Check / build (build:mp-weixin, 20.x, ubuntu-latest) (push) Has been cancelled
Check / build (build:mp-weixin, 20.x, windows-latest) (push) Has been cancelled
Check / build (build:mp-weixin, 22.x, macos-latest) (push) Has been cancelled
Check / build (build:mp-weixin, 22.x, ubuntu-latest) (push) Has been cancelled
Check / build (build:mp-weixin, 22.x, windows-latest) (push) Has been cancelled
37
.cursorrules
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
// Uniapp Vue 3 best practices
|
||||||
|
|
||||||
|
const vue3CompositionApiBestPractices = [
|
||||||
|
"Use setup() function for component logic",
|
||||||
|
"Utilize ref and reactive for reactive state",
|
||||||
|
"Implement computed properties with computed()",
|
||||||
|
"Use watch and watchEffect for side effects",
|
||||||
|
"Implement lifecycle hooks with onMounted, onUpdated, etc.",
|
||||||
|
"Utilize provide/inject for dependency injection",
|
||||||
|
];
|
||||||
|
|
||||||
|
// Folder structure
|
||||||
|
|
||||||
|
const folderStructure = `
|
||||||
|
src/
|
||||||
|
components/
|
||||||
|
composables/
|
||||||
|
views/
|
||||||
|
static/
|
||||||
|
ui/
|
||||||
|
App.vue
|
||||||
|
main.ts
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Additional instructions
|
||||||
|
|
||||||
|
const additionalInstructions = `
|
||||||
|
1. Follow the uniapp vue3 version
|
||||||
|
2. Pay attention to the compatibility of mobile APP
|
||||||
|
3. Implement proper props and emits definitions
|
||||||
|
4. Utilize Vue 3's Teleport component when needed
|
||||||
|
5. Use Suspense for async components
|
||||||
|
6. Implement proper error handling
|
||||||
|
7. Follow Vue 3 style guide and naming conventions
|
||||||
|
8. Use Vite for fast development and building
|
||||||
|
`;
|
||||||
|
|
||||||
9
.editorconfig
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
5
.env
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# 基础环境变量配置
|
||||||
|
VITE_APP_BASE_URL=https://www.tianyuandb.com
|
||||||
|
VITE_APP_SHARE_URL=https://www.tianyuandb.com
|
||||||
|
# 默认环境配置
|
||||||
|
VITE_APP_DEBUG=false
|
||||||
5
.env.development
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# 开发环境配置
|
||||||
|
VITE_APP_BASE_URL=https://www.tianyuandb.com
|
||||||
|
|
||||||
|
# 是否启用调试模式
|
||||||
|
VITE_APP_DEBUG=true
|
||||||
5
.env.production
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# 生产环境配置
|
||||||
|
VITE_APP_BASE_URL=https://www.tianyuandb.com
|
||||||
|
|
||||||
|
# 关闭调试模式
|
||||||
|
VITE_APP_DEBUG=false
|
||||||
BIN
.github/images/preview.png
vendored
Normal file
|
After Width: | Height: | Size: 11 KiB |
84
.github/workflows/check.yml
vendored
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
name: Check
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node_version: [18.x, 20.x, 22.x]
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
|
fail-fast: false
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
timeout-minutes: 10
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- run: |
|
||||||
|
git config --global core.autocrlf false
|
||||||
|
git config --global core.eol lf
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node_version }}
|
||||||
|
cache: pnpm
|
||||||
|
- run: pnpm i
|
||||||
|
- run: pnpm run lint
|
||||||
|
|
||||||
|
typecheck:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node_version: [18.x, 20.x, 22.x]
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
|
fail-fast: false
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
timeout-minutes: 10
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- run: |
|
||||||
|
git config --global core.autocrlf false
|
||||||
|
git config --global core.eol lf
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node_version }}
|
||||||
|
cache: pnpm
|
||||||
|
- run: pnpm i
|
||||||
|
- run: pnpm run type-check
|
||||||
|
|
||||||
|
build:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node_version: [18.x, 20.x, 22.x]
|
||||||
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
|
build_cmd: [build, 'build:mp-weixin', 'build:app']
|
||||||
|
fail-fast: false
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
timeout-minutes: 10
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- run: |
|
||||||
|
git config --global core.autocrlf false
|
||||||
|
git config --global core.eol lf
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node_version }}
|
||||||
|
cache: pnpm
|
||||||
|
- run: pnpm i
|
||||||
|
- run: pnpm run ${{ matrix.build_cmd }}
|
||||||
23
.gitignore
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
.DS_Store
|
||||||
|
dist
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.idea
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
./src/components.d.ts
|
||||||
|
./src/auto-imports.d.ts
|
||||||
15
.hbuilderx/launch.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"version" : "1.0",
|
||||||
|
"configurations" : [
|
||||||
|
{
|
||||||
|
"playground" : "standard",
|
||||||
|
"type" : "uni-app:app-ios"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"app-plus" : {
|
||||||
|
"launchtype" : "local"
|
||||||
|
},
|
||||||
|
"type" : "uniCloud"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
3
.npmrc
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
strict-peer-dependencies=false
|
||||||
|
auto-install-peers=true
|
||||||
|
shamefully-hoist=true
|
||||||
11
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"recommendations": [
|
||||||
|
"antfu.vite",
|
||||||
|
"antfu.iconify",
|
||||||
|
"antfu.unocss",
|
||||||
|
"vue.volar",
|
||||||
|
"dbaeumer.vscode-eslint",
|
||||||
|
"editorConfig.editorConfig",
|
||||||
|
"uni-helper.uni-helper-vscode"
|
||||||
|
]
|
||||||
|
}
|
||||||
16
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Debug h5",
|
||||||
|
"type": "chrome",
|
||||||
|
"runtimeArgs": [
|
||||||
|
"--remote-debugging-port=9222"
|
||||||
|
],
|
||||||
|
"request": "launch",
|
||||||
|
"url": "http://localhost:5173",
|
||||||
|
"webRoot": "${workspaceFolder}",
|
||||||
|
"preLaunchTask": "uni:h5"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
55
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
{
|
||||||
|
// Disable the default formatter, use eslint instead
|
||||||
|
"prettier.enable": false,
|
||||||
|
"editor.formatOnSave": false,
|
||||||
|
|
||||||
|
// Auto fix
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.organizeImports": "never"
|
||||||
|
},
|
||||||
|
|
||||||
|
// Silent the stylistic rules in you IDE, but still auto fix them
|
||||||
|
"eslint.rules.customizations": [
|
||||||
|
{ "rule": "style/*", "severity": "off", "fixable": true },
|
||||||
|
{ "rule": "format/*", "severity": "off", "fixable": true },
|
||||||
|
{ "rule": "*-indent", "severity": "off", "fixable": true },
|
||||||
|
{ "rule": "*-spacing", "severity": "off", "fixable": true },
|
||||||
|
{ "rule": "*-spaces", "severity": "off", "fixable": true },
|
||||||
|
{ "rule": "*-order", "severity": "off", "fixable": true },
|
||||||
|
{ "rule": "*-dangle", "severity": "off", "fixable": true },
|
||||||
|
{ "rule": "*-newline", "severity": "off", "fixable": true },
|
||||||
|
{ "rule": "*quotes", "severity": "off", "fixable": true },
|
||||||
|
{ "rule": "*semi", "severity": "off", "fixable": true }
|
||||||
|
],
|
||||||
|
|
||||||
|
// Enable eslint for all supported languages
|
||||||
|
"eslint.validate": [
|
||||||
|
"javascript",
|
||||||
|
"javascriptreact",
|
||||||
|
"typescript",
|
||||||
|
"typescriptreact",
|
||||||
|
"vue",
|
||||||
|
"html",
|
||||||
|
"markdown",
|
||||||
|
"json",
|
||||||
|
"jsonc",
|
||||||
|
"yaml",
|
||||||
|
"toml",
|
||||||
|
"xml",
|
||||||
|
"gql",
|
||||||
|
"graphql",
|
||||||
|
"astro",
|
||||||
|
"svelte",
|
||||||
|
"css",
|
||||||
|
"less",
|
||||||
|
"scss",
|
||||||
|
"pcss",
|
||||||
|
"postcss"
|
||||||
|
],
|
||||||
|
|
||||||
|
// Enable file nesting
|
||||||
|
"explorer.fileNesting.enabled": true,
|
||||||
|
"explorer.fileNesting.patterns": {
|
||||||
|
"vite.config.*": "pages.config.*, manifest.config.*, uno.config.*, volar.config.*, *.env, .env.*"
|
||||||
|
}
|
||||||
|
}
|
||||||
16
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "uni:h5",
|
||||||
|
"type": "npm",
|
||||||
|
"script": "dev --devtools",
|
||||||
|
"isBackground": true,
|
||||||
|
"problemMatcher": "$vite",
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023-PRESENT KeJun
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
31
README.md
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<p align="center">
|
||||||
|
<img src="https://github.com/uni-helper/vitesse-uni-app/raw/main/.github/images/preview.png" width="300"/>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 align="center">
|
||||||
|
Vitesse for uni-app
|
||||||
|
</h2>
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://vitesse-uni-app.netlify.app/">📱 在线预览</a>
|
||||||
|
<a href="https://vitesse-docs.netlify.app/">📖 阅读文档</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
## 特性
|
||||||
|
|
||||||
|
- ⚡️ [Vue 3](https://github.com/vuejs/core), [Vite](https://github.com/vitejs/vite), [pnpm](https://pnpm.io/), [esbuild](https://github.com/evanw/esbuild) - 就是快!
|
||||||
|
|
||||||
|
- 🗂 [基于文件的路由](./src/pages)
|
||||||
|
|
||||||
|
- 📦 [组件自动化加载](./src/components)
|
||||||
|
|
||||||
|
- 📑 [布局系统](./src/layouts)
|
||||||
|
|
||||||
|
- 🎨 [UnoCSS](https://github.com/unocss/unocss) - 高性能且极具灵活性的即时原子化 CSS 引擎
|
||||||
|
|
||||||
|
- 😃 [各种图标集为你所用](https://github.com/antfu/unocss/tree/main/packages/preset-icons)
|
||||||
|
|
||||||
|
- 🔥 使用 [新的 `<script setup>` 语法](https://github.com/vuejs/rfcs/pull/227)
|
||||||
|
|
||||||
|
- 📥 [API 自动加载](https://github.com/antfu/unplugin-auto-import) - 直接使用 Composition API 无需引入
|
||||||
|
|
||||||
|
- 🦾 [TypeScript](https://www.typescriptlang.org/) & [ESLint](https://eslint.org/) - 保证代码质量
|
||||||
BIN
deploy/android/qnc.keystore
Normal file
BIN
deploy/ios/development/QNC_Development_Profile.mobileprovision
Normal file
BIN
deploy/ios/development/QNC_Development_Profile_2.mobileprovision
Normal file
BIN
deploy/ios/development/ios_development.cer
Normal file
BIN
deploy/ios/development/ios_distribution.p12
Normal file
33
deploy/ios/development/ios_distribution.pem
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFyzCCBLOgAwIBAgIQJWS27ihJedi6JX9bSk4CoTANBgkqhkiG9w0BAQsFADB1
|
||||||
|
MUQwQgYDVQQDDDtBcHBsZSBXb3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9ucyBD
|
||||||
|
ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTELMAkGA1UECwwCRzMxEzARBgNVBAoMCkFw
|
||||||
|
cGxlIEluYy4xCzAJBgNVBAYTAlVTMB4XDTI0MTEyMDA2MjAyNVoXDTI1MTEyMDA2
|
||||||
|
MjAyNFowgaYxGjAYBgoJkiaJk/IsZAEBDAo3NlE3TThXRjZLMTAwLgYDVQQDDCdp
|
||||||
|
UGhvbmUgRGV2ZWxvcGVyOiBmdXNpIGxpdSAoM01WNzRUOEtUNSkxEzARBgNVBAsM
|
||||||
|
ClI0RFY3VkY2SDcxNDAyBgNVBAoMK0hhaW5hbiBYdWV5dXNpIE5ldHdvcmsgVGVj
|
||||||
|
aG5vbG9neSBDby4sIEx0ZC4xCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEF
|
||||||
|
AAOCAQ8AMIIBCgKCAQEA3km9CiYZty9RJhn7X0hWZYi9rmfDFsSQjtDCxCQV9W7O
|
||||||
|
miLHSZIU3o+xTyr+X+9egXL2DIgMVpn8BDxNgC8BY6AMvhlBy8yHCbK2EJPrutqX
|
||||||
|
78CYY9G6Cre9R4OxOiUBsW34O0bObqagBC1POknQpL3Cla/7S6Kn7cjcIb4Dllco
|
||||||
|
r/n25Mr8rFVZEHgiOgMHju0hn5QzR/wVcA61vRfEZManHsB+MmKI323mq7pyV6D5
|
||||||
|
aLcG2m65OHi4HIz/eEm7as5NG4xIyYIQsfEgwAAZYsrpwzLRIg9uh7QsBmYr/NRZ
|
||||||
|
SJlK9gBPTnrPQ517RRnIA2As//ZoIG8bi59VSmUPQQIDAQABo4ICIzCCAh8wDAYD
|
||||||
|
VR0TAQH/BAIwADAfBgNVHSMEGDAWgBQJ/sAVkPmvZAqSErkmKGMMl+ynsjBwBggr
|
||||||
|
BgEFBQcBAQRkMGIwLQYIKwYBBQUHMAKGIWh0dHA6Ly9jZXJ0cy5hcHBsZS5jb20v
|
||||||
|
d3dkcmczLmRlcjAxBggrBgEFBQcwAYYlaHR0cDovL29jc3AuYXBwbGUuY29tL29j
|
||||||
|
c3AwMy13d2RyZzMwMzCCAR4GA1UdIASCARUwggERMIIBDQYJKoZIhvdjZAUBMIH/
|
||||||
|
MIHDBggrBgEFBQcCAjCBtgyBs1JlbGlhbmNlIG9uIHRoaXMgY2VydGlmaWNhdGUg
|
||||||
|
YnkgYW55IHBhcnR5IGFzc3VtZXMgYWNjZXB0YW5jZSBvZiB0aGUgdGhlbiBhcHBs
|
||||||
|
aWNhYmxlIHN0YW5kYXJkIHRlcm1zIGFuZCBjb25kaXRpb25zIG9mIHVzZSwgY2Vy
|
||||||
|
dGlmaWNhdGUgcG9saWN5IGFuZCBjZXJ0aWZpY2F0aW9uIHByYWN0aWNlIHN0YXRl
|
||||||
|
bWVudHMuMDcGCCsGAQUFBwIBFitodHRwczovL3d3dy5hcHBsZS5jb20vY2VydGlm
|
||||||
|
aWNhdGVhdXRob3JpdHkvMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMDMB0GA1UdDgQW
|
||||||
|
BBTX2EgxLl+irOnOYtlgQwBwxgvpWTAOBgNVHQ8BAf8EBAMCB4AwEwYKKoZIhvdj
|
||||||
|
ZAYBAgEB/wQCBQAwDQYJKoZIhvcNAQELBQADggEBACb1PyLRXf66cTo5D7Grn5Ll
|
||||||
|
BveYJO7Vn7b3rZgelm22O5fCuYTlpckwtFES1r4FG5GF+oTyd7RoSajbpBT8shI+
|
||||||
|
HPVcnJ++9aP1y/5EgpmKQno8IwqdXxGW+TKNsh4/PdAbVB5qUpiCxqRowAVHTHZI
|
||||||
|
fTiJT9kvvazA0wBeVQla59j1zC/UaZYoziiCDSLsfl7Kkd7UcwBxcgPTo2S+E6dU
|
||||||
|
ihA6H36Ef/7QLJwInDzN+zjIEsof7k/VNaet/Ub6CGOTKg8fAb3wakGVT547idE7
|
||||||
|
TjjCym78euH6lc+tNAnomqzntB1Pa3NkiW8PHP5mSL3RFaKhzbJu7FE8kCw/T/E=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
28
deploy/ios/development/private.key
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDeSb0KJhm3L1Em
|
||||||
|
GftfSFZliL2uZ8MWxJCO0MLEJBX1bs6aIsdJkhTej7FPKv5f716BcvYMiAxWmfwE
|
||||||
|
PE2ALwFjoAy+GUHLzIcJsrYQk+u62pfvwJhj0boKt71Hg7E6JQGxbfg7Rs5upqAE
|
||||||
|
LU86SdCkvcKVr/tLoqftyNwhvgOWVyiv+fbkyvysVVkQeCI6AweO7SGflDNH/BVw
|
||||||
|
DrW9F8RkxqcewH4yYojfbearunJXoPlotwbabrk4eLgcjP94Sbtqzk0bjEjJghCx
|
||||||
|
8SDAABliyunDMtEiD26HtCwGZiv81FlImUr2AE9Oes9DnXtFGcgDYCz/9mggbxuL
|
||||||
|
n1VKZQ9BAgMBAAECggEAK0qKHf1gmbwF3llGCv8AEIMqidpcdMUwcBf5LpDmk+eS
|
||||||
|
JhOq40mnddbfTc9OPinUqnpUsAkY+g7ANaT25QWCjEAB603o8gNxszJSYfUk/j2P
|
||||||
|
RJqqMxqH1ImHO/PKYswNQdxscULvdj5OQLIAUZZJo6PvTw64brkNYnJxaMLkWPVg
|
||||||
|
gNqmFMwNlpCUlYdMbARrBf+aLWuaXHcb9PmkwkAjrEVS87AErJFRmyiOX+LZcaYp
|
||||||
|
YxUCp0DVmcyWeGrm0Jj3ai1PSxC9PjthMVWL1G84BQR5pb9ITpZJHypO0lY2sOM0
|
||||||
|
5NjQo0C/iTqKjGbItbHjuHo/QZNzzVEd7gLMIqHY6QKBgQD8BxKFz+piwtA2fAGV
|
||||||
|
xEuXA2oglYTn6baLS26PsJcfcgfgre1jTbmr8AQTph3U7kqwW+Q1QF6BTiaDUqZd
|
||||||
|
y7HzC9dVLgqPbyshLWd9V+3xG+9Q+MvKjX4cWzT687bGBLixidy2svyse70BWDZx
|
||||||
|
fC/5yIbI9dNSFmMXGrxg1Ia9HQKBgQDhyqrUhrWSIKyI0b17X/Kbh3pLs6XRaLto
|
||||||
|
Q8hMZnsrlX1GH+T6kb5KaGe3txfo7UVqoYeUWhTGOnxmsNywRAyV5Ob0Ebq4a7oK
|
||||||
|
K2Dtzx+yZVrYralxqxcMU1vr322j51zBWNT1f2ig0/ae78xijvIuYG/gDokRwGsp
|
||||||
|
6k7o6z5VdQKBgQDIjfg3c9TY8p4uPVAllidw7M6vT0yMIHebMVOyFJyt8VhDeyTF
|
||||||
|
OTzfjggweqiSoW3eGcHofmi2DTKonkS1rzrUZj/dEN5mddikdjU8kZRPoiM3FEVX
|
||||||
|
0nDgr9PhP7/apDc1ULfKy3Q27eDiCQiKFoE3DFP152lakWYZJLVzeKwgIQKBgQCY
|
||||||
|
0/vTBCcL9I+zv/t38ZadzRJrAPrghfordY2yoL8H3hRADcCIFXPum0JwqD5Hy6nr
|
||||||
|
pc/GCz76eYXQYC/QIw89ugTU+NcgktIGphviewpiTpYyCUGijYJ5NkcGnWFxyl0P
|
||||||
|
BZiHXqisCyxnobn+j/C+kUnIJy6B1HL0mQK2RtwXLQKBgQDDcfTi/UusJvrrHp6O
|
||||||
|
DZlnP7lMWvOrKHUH2alDq6+YK3ieJ+e6btv3TxX2Y6CtbeKutjax3HQ/MTMwkeT8
|
||||||
|
4mItZ+oHZcnYUKjYmDDKqxyUZLqdzuylNKd57gXl/BkfZBLk+MbgH9+zAb1VsnMz
|
||||||
|
/8t+U5kmczy90XBU3OOigpWj+g==
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
22
deploy/ios/development/request.csr
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
-----BEGIN CERTIFICATE REQUEST-----
|
||||||
|
MIIDnTCCAoUCAQAwgfcxCzAJBgNVBAYTAkNOMREwDwYDVQQIDAjCusKjw4TDjzER
|
||||||
|
MA8GA1UEBwwIwrrCo8K/w5oxQTA/BgNVBAoMOMK6wqPDhMOPw4rCocORwqfDk8Ou
|
||||||
|
w4vCvMONw7jDgsOnwr/DhsK8wrzDk8OQw4/DnsK5wqvDi8K+MUEwPwYDVQQLDDjC
|
||||||
|
usKjw4TDj8OKwqHDkcKnw5PDrsOLwrzDjcO4w4LDp8K/w4bCvMK8w5PDkMOPw57C
|
||||||
|
ucKrw4vCvjEbMBkGA1UEAwwSY29tLmFsbGlub25lLmNoZWNrMR8wHQYJKoZIhvcN
|
||||||
|
AQkBFhBhZG1pbkBpaWVlaWkuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
|
||||||
|
CgKCAQEA3km9CiYZty9RJhn7X0hWZYi9rmfDFsSQjtDCxCQV9W7OmiLHSZIU3o+x
|
||||||
|
Tyr+X+9egXL2DIgMVpn8BDxNgC8BY6AMvhlBy8yHCbK2EJPrutqX78CYY9G6Cre9
|
||||||
|
R4OxOiUBsW34O0bObqagBC1POknQpL3Cla/7S6Kn7cjcIb4Dllcor/n25Mr8rFVZ
|
||||||
|
EHgiOgMHju0hn5QzR/wVcA61vRfEZManHsB+MmKI323mq7pyV6D5aLcG2m65OHi4
|
||||||
|
HIz/eEm7as5NG4xIyYIQsfEgwAAZYsrpwzLRIg9uh7QsBmYr/NRZSJlK9gBPTnrP
|
||||||
|
Q517RRnIA2As//ZoIG8bi59VSmUPQQIDAQABoGAwFQYJKoZIhvcNAQkHMQgMBjMy
|
||||||
|
MDMyMDBHBgkqhkiG9w0BCQIxOgw4wrrCo8OEw4/DisKhw5HCp8OTw67Di8K8w43D
|
||||||
|
uMOCw6fCv8OGwrzCvMOTw5DDj8OewrnCq8OLwr4wDQYJKoZIhvcNAQELBQADggEB
|
||||||
|
AI1CQFPrHE3xVWpnxynxK4DjM7eRjjCkPVUd3/IqNfiLnRNatDRzdU4K/Y5bLSVk
|
||||||
|
A4eEk479YeBw1Zyw2jlAvK0Ga0QLAE4/SWAvjCbSCqJqSe1/SR9skxaZO3DZECPG
|
||||||
|
EAgdiNsmYYlg7Oae3OT77OAFn6GkhjpRFBADO4F4Yb5yj75njNNava7gHvMn9NMx
|
||||||
|
12/IvIK1quS2V7XjcLaMIVkIfnYqGevbOnN3XGgwjGu2hCJz4Ob0iAI+kcNYdBrG
|
||||||
|
7rdwfSW7AbCSFiHmRL4n5rjJN6rs4VEHMnOPoB05E2inpIzLxZPa0wJ2k0UR5zZL
|
||||||
|
KKAkSMxpPrUvJyCnoRhX8BM=
|
||||||
|
-----END CERTIFICATE REQUEST-----
|
||||||
BIN
deploy/ios/production/QNC_Production_Profile.mobileprovision
Normal file
BIN
deploy/ios/production/ios_distribution.cer
Normal file
34
deploy/ios/production/ios_distribution.pem
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIF8TCCBNmgAwIBAgIQLaG9i7JFETJnqW6Z8lt9hTANBgkqhkiG9w0BAQsFADB1
|
||||||
|
MUQwQgYDVQQDDDtBcHBsZSBXb3JsZHdpZGUgRGV2ZWxvcGVyIFJlbGF0aW9ucyBD
|
||||||
|
ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTELMAkGA1UECwwCRzMxEzARBgNVBAoMCkFw
|
||||||
|
cGxlIEluYy4xCzAJBgNVBAYTAlVTMB4XDTI0MTEyMTE4NTczMVoXDTI1MTEyMTE4
|
||||||
|
NTczMFowgcwxGjAYBgoJkiaJk/IsZAEBDApSNERWN1ZGNkg3MVYwVAYDVQQDDE1p
|
||||||
|
UGhvbmUgRGlzdHJpYnV0aW9uOiBIYWluYW4gWHVleXVzaSBOZXR3b3JrIFRlY2hu
|
||||||
|
b2xvZ3kgQ28uLCBMdGQuIChSNERWN1ZGNkg3KTETMBEGA1UECwwKUjREVjdWRjZI
|
||||||
|
NzE0MDIGA1UECgwrSGFpbmFuIFh1ZXl1c2kgTmV0d29yayBUZWNobm9sb2d5IENv
|
||||||
|
LiwgTHRkLjELMAkGA1UEBhMCQ04wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
|
||||||
|
AoIBAQDeSb0KJhm3L1EmGftfSFZliL2uZ8MWxJCO0MLEJBX1bs6aIsdJkhTej7FP
|
||||||
|
Kv5f716BcvYMiAxWmfwEPE2ALwFjoAy+GUHLzIcJsrYQk+u62pfvwJhj0boKt71H
|
||||||
|
g7E6JQGxbfg7Rs5upqAELU86SdCkvcKVr/tLoqftyNwhvgOWVyiv+fbkyvysVVkQ
|
||||||
|
eCI6AweO7SGflDNH/BVwDrW9F8RkxqcewH4yYojfbearunJXoPlotwbabrk4eLgc
|
||||||
|
jP94Sbtqzk0bjEjJghCx8SDAABliyunDMtEiD26HtCwGZiv81FlImUr2AE9Oes9D
|
||||||
|
nXtFGcgDYCz/9mggbxuLn1VKZQ9BAgMBAAGjggIjMIICHzAMBgNVHRMBAf8EAjAA
|
||||||
|
MB8GA1UdIwQYMBaAFAn+wBWQ+a9kCpISuSYoYwyX7KeyMHAGCCsGAQUFBwEBBGQw
|
||||||
|
YjAtBggrBgEFBQcwAoYhaHR0cDovL2NlcnRzLmFwcGxlLmNvbS93d2RyZzMuZGVy
|
||||||
|
MDEGCCsGAQUFBzABhiVodHRwOi8vb2NzcC5hcHBsZS5jb20vb2NzcDAzLXd3ZHJn
|
||||||
|
MzAyMIIBHgYDVR0gBIIBFTCCAREwggENBgkqhkiG92NkBQEwgf8wgcMGCCsGAQUF
|
||||||
|
BwICMIG2DIGzUmVsaWFuY2Ugb24gdGhpcyBjZXJ0aWZpY2F0ZSBieSBhbnkgcGFy
|
||||||
|
dHkgYXNzdW1lcyBhY2NlcHRhbmNlIG9mIHRoZSB0aGVuIGFwcGxpY2FibGUgc3Rh
|
||||||
|
bmRhcmQgdGVybXMgYW5kIGNvbmRpdGlvbnMgb2YgdXNlLCBjZXJ0aWZpY2F0ZSBw
|
||||||
|
b2xpY3kgYW5kIGNlcnRpZmljYXRpb24gcHJhY3RpY2Ugc3RhdGVtZW50cy4wNwYI
|
||||||
|
KwYBBQUHAgEWK2h0dHBzOi8vd3d3LmFwcGxlLmNvbS9jZXJ0aWZpY2F0ZWF1dGhv
|
||||||
|
cml0eS8wFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwMwHQYDVR0OBBYEFNfYSDEuX6Ks
|
||||||
|
6c5i2WBDAHDGC+lZMA4GA1UdDwEB/wQEAwIHgDATBgoqhkiG92NkBgEEAQH/BAIF
|
||||||
|
ADANBgkqhkiG9w0BAQsFAAOCAQEANk6dzI9SJXj35krhx6WhXcQJoE8fZ6gNq9hW
|
||||||
|
SvpLjffZyn9143zKGZ93izi10H1EwAbBHrZhiIW5Tl7PB6e7ZmUWPxuL/Syex9Js
|
||||||
|
In9DnZp5XEHtbFZVQnffQqmIh0ZwmM40wgCMaOxRqtw/Vvlovb/K0sWmDXTbN1z0
|
||||||
|
lFqALTqFbsfMDj6uE2Xl0RUn2Qc6XxqV7yh/qI9IYkMicrAmoOAhKTxX98ny7ioR
|
||||||
|
et7F0aDF+CKG4ZceQt4QeYm9EnPHYt3DWBu3wIiwJJ+YCaclPPFK3+TgB64yNT7M
|
||||||
|
0p4n7okj3vS5I7SnCy8wcYndluByp7G0pSAzLLP2eLO2I4oDyA==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
BIN
deploy/ios/production/output.p12
Normal file
28
deploy/ios/production/private.key
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDeSb0KJhm3L1Em
|
||||||
|
GftfSFZliL2uZ8MWxJCO0MLEJBX1bs6aIsdJkhTej7FPKv5f716BcvYMiAxWmfwE
|
||||||
|
PE2ALwFjoAy+GUHLzIcJsrYQk+u62pfvwJhj0boKt71Hg7E6JQGxbfg7Rs5upqAE
|
||||||
|
LU86SdCkvcKVr/tLoqftyNwhvgOWVyiv+fbkyvysVVkQeCI6AweO7SGflDNH/BVw
|
||||||
|
DrW9F8RkxqcewH4yYojfbearunJXoPlotwbabrk4eLgcjP94Sbtqzk0bjEjJghCx
|
||||||
|
8SDAABliyunDMtEiD26HtCwGZiv81FlImUr2AE9Oes9DnXtFGcgDYCz/9mggbxuL
|
||||||
|
n1VKZQ9BAgMBAAECggEAK0qKHf1gmbwF3llGCv8AEIMqidpcdMUwcBf5LpDmk+eS
|
||||||
|
JhOq40mnddbfTc9OPinUqnpUsAkY+g7ANaT25QWCjEAB603o8gNxszJSYfUk/j2P
|
||||||
|
RJqqMxqH1ImHO/PKYswNQdxscULvdj5OQLIAUZZJo6PvTw64brkNYnJxaMLkWPVg
|
||||||
|
gNqmFMwNlpCUlYdMbARrBf+aLWuaXHcb9PmkwkAjrEVS87AErJFRmyiOX+LZcaYp
|
||||||
|
YxUCp0DVmcyWeGrm0Jj3ai1PSxC9PjthMVWL1G84BQR5pb9ITpZJHypO0lY2sOM0
|
||||||
|
5NjQo0C/iTqKjGbItbHjuHo/QZNzzVEd7gLMIqHY6QKBgQD8BxKFz+piwtA2fAGV
|
||||||
|
xEuXA2oglYTn6baLS26PsJcfcgfgre1jTbmr8AQTph3U7kqwW+Q1QF6BTiaDUqZd
|
||||||
|
y7HzC9dVLgqPbyshLWd9V+3xG+9Q+MvKjX4cWzT687bGBLixidy2svyse70BWDZx
|
||||||
|
fC/5yIbI9dNSFmMXGrxg1Ia9HQKBgQDhyqrUhrWSIKyI0b17X/Kbh3pLs6XRaLto
|
||||||
|
Q8hMZnsrlX1GH+T6kb5KaGe3txfo7UVqoYeUWhTGOnxmsNywRAyV5Ob0Ebq4a7oK
|
||||||
|
K2Dtzx+yZVrYralxqxcMU1vr322j51zBWNT1f2ig0/ae78xijvIuYG/gDokRwGsp
|
||||||
|
6k7o6z5VdQKBgQDIjfg3c9TY8p4uPVAllidw7M6vT0yMIHebMVOyFJyt8VhDeyTF
|
||||||
|
OTzfjggweqiSoW3eGcHofmi2DTKonkS1rzrUZj/dEN5mddikdjU8kZRPoiM3FEVX
|
||||||
|
0nDgr9PhP7/apDc1ULfKy3Q27eDiCQiKFoE3DFP152lakWYZJLVzeKwgIQKBgQCY
|
||||||
|
0/vTBCcL9I+zv/t38ZadzRJrAPrghfordY2yoL8H3hRADcCIFXPum0JwqD5Hy6nr
|
||||||
|
pc/GCz76eYXQYC/QIw89ugTU+NcgktIGphviewpiTpYyCUGijYJ5NkcGnWFxyl0P
|
||||||
|
BZiHXqisCyxnobn+j/C+kUnIJy6B1HL0mQK2RtwXLQKBgQDDcfTi/UusJvrrHp6O
|
||||||
|
DZlnP7lMWvOrKHUH2alDq6+YK3ieJ+e6btv3TxX2Y6CtbeKutjax3HQ/MTMwkeT8
|
||||||
|
4mItZ+oHZcnYUKjYmDDKqxyUZLqdzuylNKd57gXl/BkfZBLk+MbgH9+zAb1VsnMz
|
||||||
|
/8t+U5kmczy90XBU3OOigpWj+g==
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
22
deploy/ios/production/request.csr
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
-----BEGIN CERTIFICATE REQUEST-----
|
||||||
|
MIIDnTCCAoUCAQAwgfcxCzAJBgNVBAYTAkNOMREwDwYDVQQIDAjCusKjw4TDjzER
|
||||||
|
MA8GA1UEBwwIwrrCo8K/w5oxQTA/BgNVBAoMOMK6wqPDhMOPw4rCocORwqfDk8Ou
|
||||||
|
w4vCvMONw7jDgsOnwr/DhsK8wrzDk8OQw4/DnsK5wqvDi8K+MUEwPwYDVQQLDDjC
|
||||||
|
usKjw4TDj8OKwqHDkcKnw5PDrsOLwrzDjcO4w4LDp8K/w4bCvMK8w5PDkMOPw57C
|
||||||
|
ucKrw4vCvjEbMBkGA1UEAwwSY29tLmFsbGlub25lLmNoZWNrMR8wHQYJKoZIhvcN
|
||||||
|
AQkBFhBhZG1pbkBpaWVlaWkuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
|
||||||
|
CgKCAQEA3km9CiYZty9RJhn7X0hWZYi9rmfDFsSQjtDCxCQV9W7OmiLHSZIU3o+x
|
||||||
|
Tyr+X+9egXL2DIgMVpn8BDxNgC8BY6AMvhlBy8yHCbK2EJPrutqX78CYY9G6Cre9
|
||||||
|
R4OxOiUBsW34O0bObqagBC1POknQpL3Cla/7S6Kn7cjcIb4Dllcor/n25Mr8rFVZ
|
||||||
|
EHgiOgMHju0hn5QzR/wVcA61vRfEZManHsB+MmKI323mq7pyV6D5aLcG2m65OHi4
|
||||||
|
HIz/eEm7as5NG4xIyYIQsfEgwAAZYsrpwzLRIg9uh7QsBmYr/NRZSJlK9gBPTnrP
|
||||||
|
Q517RRnIA2As//ZoIG8bi59VSmUPQQIDAQABoGAwFQYJKoZIhvcNAQkHMQgMBjMy
|
||||||
|
MDMyMDBHBgkqhkiG9w0BCQIxOgw4wrrCo8OEw4/DisKhw5HCp8OTw67Di8K8w43D
|
||||||
|
uMOCw6fCv8OGwrzCvMOTw5DDj8OewrnCq8OLwr4wDQYJKoZIhvcNAQELBQADggEB
|
||||||
|
AI1CQFPrHE3xVWpnxynxK4DjM7eRjjCkPVUd3/IqNfiLnRNatDRzdU4K/Y5bLSVk
|
||||||
|
A4eEk479YeBw1Zyw2jlAvK0Ga0QLAE4/SWAvjCbSCqJqSe1/SR9skxaZO3DZECPG
|
||||||
|
EAgdiNsmYYlg7Oae3OT77OAFn6GkhjpRFBADO4F4Yb5yj75njNNava7gHvMn9NMx
|
||||||
|
12/IvIK1quS2V7XjcLaMIVkIfnYqGevbOnN3XGgwjGu2hCJz4Ob0iAI+kcNYdBrG
|
||||||
|
7rdwfSW7AbCSFiHmRL4n5rjJN6rs4VEHMnOPoB05E2inpIzLxZPa0wJ2k0UR5zZL
|
||||||
|
KKAkSMxpPrUvJyCnoRhX8BM=
|
||||||
|
-----END CERTIFICATE REQUEST-----
|
||||||
5
doc/个人风险评估.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
2. 司法涉诉
|
||||||
|
4. 个人不良
|
||||||
|
股东人企关联
|
||||||
|
6. 借贷意向
|
||||||
|
7. 特殊名单
|
||||||
5
doc/婚恋报告.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
2. 司法涉诉
|
||||||
|
3. 个人不良
|
||||||
|
4. 人企关联
|
||||||
|
5. 借贷意向
|
||||||
|
6. 单人婚姻状态
|
||||||
4
doc/家政服务.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
2. 司法涉诉
|
||||||
|
3. 特殊名单
|
||||||
|
4. 个人不良
|
||||||
|
5. 借贷意向
|
||||||
4
doc/租赁服务.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
2. 司法涉诉
|
||||||
|
4. 个人不良
|
||||||
|
5. 借贷意向
|
||||||
|
6. 特殊名单
|
||||||
7
doc/认识背景调查报告.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
2. 司法涉诉
|
||||||
|
4. 个人不良
|
||||||
|
5. 股东人企关联
|
||||||
|
6. 借贷意向
|
||||||
|
7. 特殊名单
|
||||||
|
8. 借贷行为
|
||||||
|
9. 单人婚姻
|
||||||
7
doc/贷前背调.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
2. 司法涉诉
|
||||||
|
4. 个人不良
|
||||||
|
5. 借贷行为
|
||||||
|
6. 借贷意向
|
||||||
|
7. 特殊名单
|
||||||
|
9. 婚姻状态
|
||||||
|
股东人企关联
|
||||||
53
docs/环境变量配置说明.md
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
# 环境变量配置说明
|
||||||
|
|
||||||
|
## 环境变量文件
|
||||||
|
|
||||||
|
本项目使用 Vite 的环境变量功能,支持以下环境变量文件:
|
||||||
|
|
||||||
|
- `.env`:所有环境都会加载的默认变量
|
||||||
|
- `.env.development`:开发环境变量(`npm run dev` 时加载)
|
||||||
|
- `.env.production`:生产环境变量(`npm run build` 时加载)
|
||||||
|
|
||||||
|
## 已配置的环境变量
|
||||||
|
|
||||||
|
| 变量名 | 说明 | 示例值 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| VITE_APP_BASE_URL | API 基础URL | https://www.tianyuandb.com |
|
||||||
|
| VITE_APP_DEBUG | 调试模式 | true/false |
|
||||||
|
|
||||||
|
## 在项目中使用环境变量
|
||||||
|
|
||||||
|
### 在 Vue 组件中使用
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<script setup>
|
||||||
|
// 使用 import.meta.env 访问环境变量
|
||||||
|
const baseUrl = import.meta.env.VITE_APP_BASE_URL
|
||||||
|
const isDebug = import.meta.env.VITE_APP_DEBUG === 'true'
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 在 JS/TS 文件中使用
|
||||||
|
|
||||||
|
```js
|
||||||
|
// 使用 import.meta.env 访问环境变量
|
||||||
|
const baseUrl = import.meta.env.VITE_APP_BASE_URL
|
||||||
|
const isDebug = import.meta.env.VITE_APP_DEBUG === 'true'
|
||||||
|
|
||||||
|
// 获取当前环境模式
|
||||||
|
const mode = import.meta.env.MODE
|
||||||
|
```
|
||||||
|
|
||||||
|
## 获取当前环境
|
||||||
|
|
||||||
|
```js
|
||||||
|
// 获取当前环境模式(development、production等)
|
||||||
|
const mode = import.meta.env.MODE
|
||||||
|
```
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. 所有环境变量必须以 `VITE_` 开头才能在客户端代码中访问
|
||||||
|
2. 环境变量默认为字符串类型,需要自行转换为其他类型(如布尔值)
|
||||||
|
3. 修改环境变量后需要重启开发服务器才能生效
|
||||||
|
4. 不要在环境变量文件中存储敏感信息,如API密钥等
|
||||||
21
index.html
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" href="static/logo.svg">
|
||||||
|
<script>
|
||||||
|
const coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)')
|
||||||
|
|| CSS.supports('top: constant(a)'))
|
||||||
|
document.write(
|
||||||
|
`<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0${
|
||||||
|
coverSupport ? ', viewport-fit=cover' : ''}" />`)
|
||||||
|
</script>
|
||||||
|
<title></title>
|
||||||
|
<!--preload-links-->
|
||||||
|
<!--app-context-->
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"><!--app-html--></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
147
manifest.config.ts
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
import { defineManifestConfig } from '@uni-helper/vite-plugin-uni-manifest'
|
||||||
|
|
||||||
|
export default defineManifestConfig({
|
||||||
|
'name': '天远数据',
|
||||||
|
'appid': '__UNI__CC3DA09',
|
||||||
|
'description': '',
|
||||||
|
'versionName': '1.0.0',
|
||||||
|
'versionCode': '107',
|
||||||
|
'transformPx': false,
|
||||||
|
/* 5+App特有相关 */
|
||||||
|
'app-plus': {
|
||||||
|
usingComponents: true,
|
||||||
|
nvueStyleCompiler: 'uni-app',
|
||||||
|
compilerVersion: 3,
|
||||||
|
background: '#000000',
|
||||||
|
compatible: {
|
||||||
|
ignoreVersion: true,
|
||||||
|
},
|
||||||
|
splashscreen: {
|
||||||
|
alwaysShowBeforeRender: true,
|
||||||
|
waiting: true,
|
||||||
|
autoclose: true,
|
||||||
|
delay: 0,
|
||||||
|
},
|
||||||
|
|
||||||
|
/* 模块配置 */
|
||||||
|
modules: {
|
||||||
|
Share: {},
|
||||||
|
Camera: {},
|
||||||
|
PhotoLibrary: {},
|
||||||
|
},
|
||||||
|
/* 应用发布信息 */
|
||||||
|
distribute: {
|
||||||
|
/* android打包配置 */
|
||||||
|
android: {
|
||||||
|
package: 'com.quannengcha.app',
|
||||||
|
permissions: [
|
||||||
|
'<uses-permission android:name="android.permission.INTERNET"/>',
|
||||||
|
'<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>',
|
||||||
|
'<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
/* ios打包配置 */
|
||||||
|
ios: {
|
||||||
|
privacyDescription: {
|
||||||
|
NSLocalNetworkUsageDescription: '需要访问您的网络来提供更好的服务',
|
||||||
|
NSPhotoLibraryAddUsageDescription: "此应用需要访问您的相册以保存图片",
|
||||||
|
},
|
||||||
|
idfa: false,
|
||||||
|
bundleIdentifier: 'com.allinone.check',
|
||||||
|
},
|
||||||
|
/* SDK配置 */
|
||||||
|
sdkConfigs: {
|
||||||
|
// share: {
|
||||||
|
// weixin: {
|
||||||
|
// appid: 'wx开头的微信开放平台AppID',
|
||||||
|
// UniversalLinks: 'https://www.tianyuandb.com/app/',
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// oauth: {
|
||||||
|
// weixin: {
|
||||||
|
// appid: 'wx开头的微信开放平台AppID',
|
||||||
|
// appsecret: '微信开放平台AppSecret',
|
||||||
|
// UniversalLinks: 'https://www.tianyuandb.com/app/'
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
icons: {
|
||||||
|
android: {
|
||||||
|
hdpi: 'static/icons/72x72.png',
|
||||||
|
xhdpi: 'static/icons/96x96.png',
|
||||||
|
xxhdpi: 'static/icons/144x144.png',
|
||||||
|
xxxhdpi: 'static/icons/192x192.png',
|
||||||
|
},
|
||||||
|
ios: {
|
||||||
|
appstore: 'static/icons/1024x1024.png',
|
||||||
|
ipad: {
|
||||||
|
'app': 'static/icons/76x76.png',
|
||||||
|
'app@2x': 'static/icons/152x152.png',
|
||||||
|
'notification': 'static/icons/20x20.png',
|
||||||
|
'notification@2x': 'static/icons/40x40.png',
|
||||||
|
'proapp@2x': 'static/icons/167x167.png',
|
||||||
|
'settings': 'static/icons/29x29.png',
|
||||||
|
'settings@2x': 'static/icons/58x58.png',
|
||||||
|
'spotlight': 'static/icons/40x40.png',
|
||||||
|
'spotlight@2x': 'static/icons/80x80.png',
|
||||||
|
},
|
||||||
|
iphone: {
|
||||||
|
'app@2x': 'static/icons/120x120.png',
|
||||||
|
'app@3x': 'static/icons/180x180.png',
|
||||||
|
'notification@2x': 'static/icons/40x40.png',
|
||||||
|
'notification@3x': 'static/icons/60x60.png',
|
||||||
|
'settings@2x': 'static/icons/58x58.png',
|
||||||
|
'settings@3x': 'static/icons/87x87.png',
|
||||||
|
'spotlight@2x': 'static/icons/80x80.png',
|
||||||
|
'spotlight@3x': 'static/icons/120x120.png',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
/* 快应用特有相关 */
|
||||||
|
'quickapp': {},
|
||||||
|
/* 小程序特有相关 */
|
||||||
|
'mp-weixin': {
|
||||||
|
appid: '',
|
||||||
|
setting: {
|
||||||
|
urlCheck: false,
|
||||||
|
},
|
||||||
|
usingComponents: true,
|
||||||
|
darkmode: false,
|
||||||
|
themeLocation: 'theme.json',
|
||||||
|
},
|
||||||
|
'mp-alipay': {
|
||||||
|
usingComponents: true,
|
||||||
|
},
|
||||||
|
'mp-baidu': {
|
||||||
|
usingComponents: true,
|
||||||
|
},
|
||||||
|
'mp-toutiao': {
|
||||||
|
usingComponents: true,
|
||||||
|
},
|
||||||
|
'h5': {
|
||||||
|
darkmode: false,
|
||||||
|
themeLocation: 'theme.json',
|
||||||
|
},
|
||||||
|
'uniStatistics': {
|
||||||
|
enable: false,
|
||||||
|
},
|
||||||
|
'vueVersion': '3',
|
||||||
|
|
||||||
|
/* UTS 插件配置 */
|
||||||
|
'uts': {
|
||||||
|
'plugins': {
|
||||||
|
'lz-url-launch': {
|
||||||
|
'version': '1.0.0',
|
||||||
|
'description': 'SFSafariViewController插件,支持在iOS中使用系统浏览器打开网页',
|
||||||
|
'platforms': {
|
||||||
|
'ios': {
|
||||||
|
'appid': '__UNI_LZ_URL_LAUNCH_IOS',
|
||||||
|
'autostart': false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
106
package.json
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
{
|
||||||
|
"name": "uni-qnc",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"packageManager": "pnpm@9.9.0",
|
||||||
|
"license": "MIT",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "uni",
|
||||||
|
"dev:app": "uni -p app",
|
||||||
|
"dev:app-plus": "uni -p app-plus",
|
||||||
|
"dev:app-android": "uni -p app-android",
|
||||||
|
"dev:app-ios": "uni -p app-ios",
|
||||||
|
"dev:custom": "uni -p",
|
||||||
|
"dev:h5": "uni",
|
||||||
|
"dev:h5:ssr": "uni --ssr",
|
||||||
|
"dev:mp-alipay": "uni -p mp-alipay",
|
||||||
|
"dev:mp-baidu": "uni -p mp-baidu",
|
||||||
|
"dev:mp-kuaishou": "uni -p mp-kuaishou",
|
||||||
|
"dev:mp-lark": "uni -p mp-lark",
|
||||||
|
"dev:mp-qq": "uni -p mp-qq",
|
||||||
|
"dev:mp-toutiao": "uni -p mp-toutiao",
|
||||||
|
"dev:mp-weixin": "uni -p mp-weixin",
|
||||||
|
"dev:quickapp-webview": "uni -p quickapp-webview",
|
||||||
|
"dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei",
|
||||||
|
"dev:quickapp-webview-union": "uni -p quickapp-webview-union",
|
||||||
|
"build": "uni build",
|
||||||
|
"build:app": "uni build -p app",
|
||||||
|
"build:app-plus": "uni build -p app-plus",
|
||||||
|
"build:app-android": "uni build -p app-android",
|
||||||
|
"build:app-ios": "uni build -p app-ios",
|
||||||
|
"build:custom": "uni build -p",
|
||||||
|
"build:h5": "uni build",
|
||||||
|
"build:h5:ssr": "uni build --ssr",
|
||||||
|
"build:mp-alipay": "uni build -p mp-alipay",
|
||||||
|
"build:mp-baidu": "uni build -p mp-baidu",
|
||||||
|
"build:mp-kuaishou": "uni build -p mp-kuaishou",
|
||||||
|
"build:mp-lark": "uni build -p mp-lark",
|
||||||
|
"build:mp-qq": "uni build -p mp-qq",
|
||||||
|
"build:mp-toutiao": "uni build -p mp-toutiao",
|
||||||
|
"build:mp-weixin": "uni build -p mp-weixin",
|
||||||
|
"build:quickapp-webview": "uni build -p quickapp-webview",
|
||||||
|
"build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
|
||||||
|
"build:quickapp-webview-union": "uni build -p quickapp-webview-union",
|
||||||
|
"type-check": "vue-tsc --noEmit"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@dcloudio/uni-app": "3.0.0-4050520250307001",
|
||||||
|
"@dcloudio/uni-app-harmony": "3.0.0-4050520250307001",
|
||||||
|
"@dcloudio/uni-app-plus": "3.0.0-4050520250307001",
|
||||||
|
"@dcloudio/uni-components": "3.0.0-4050520250307001",
|
||||||
|
"@dcloudio/uni-h5": "3.0.0-4050520250307001",
|
||||||
|
"@dcloudio/uni-mp-alipay": "3.0.0-4050520250307001",
|
||||||
|
"@dcloudio/uni-mp-baidu": "3.0.0-4050520250307001",
|
||||||
|
"@dcloudio/uni-mp-jd": "3.0.0-4050520250307001",
|
||||||
|
"@dcloudio/uni-mp-kuaishou": "3.0.0-4050520250307001",
|
||||||
|
"@dcloudio/uni-mp-lark": "3.0.0-4050520250307001",
|
||||||
|
"@dcloudio/uni-mp-qq": "3.0.0-4050520250307001",
|
||||||
|
"@dcloudio/uni-mp-toutiao": "3.0.0-4050520250307001",
|
||||||
|
"@dcloudio/uni-mp-weixin": "3.0.0-4050520250307001",
|
||||||
|
"@dcloudio/uni-mp-xhs": "3.0.0-4050520250307001",
|
||||||
|
"@dcloudio/uni-quickapp-webview": "3.0.0-4050520250307001",
|
||||||
|
"@rollup/rollup-win32-x64-msvc": "^4.42.0",
|
||||||
|
"@vant/area-data": "^2.0.0",
|
||||||
|
"@vueuse/core": "^11.3.0",
|
||||||
|
"crypto-js": "^4.2.0",
|
||||||
|
"pinia": "^3.0.3",
|
||||||
|
"qrcode": "^1.5.4",
|
||||||
|
"uqrcodejs": "^4.0.7",
|
||||||
|
"vue": "~3.5.16",
|
||||||
|
"vue-i18n": "^9.14.4",
|
||||||
|
"wot-design-uni": "^1.9.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@dcloudio/types": "^3.4.15",
|
||||||
|
"@dcloudio/uni-automator": "3.0.0-4050520250307001",
|
||||||
|
"@dcloudio/uni-cli-shared": "3.0.0-4050520250307001",
|
||||||
|
"@dcloudio/uni-stacktracey": "3.0.0-4050520250307001",
|
||||||
|
"@dcloudio/uni-uts-v1": "3.0.0-4050520250307001",
|
||||||
|
"@dcloudio/uni-vue-devtools": "3.0.0-4020420240722002",
|
||||||
|
"@dcloudio/vite-plugin-uni": "3.0.0-4050520250307001",
|
||||||
|
"@iconify-json/carbon": "^1.2.9",
|
||||||
|
"@mini-types/alipay": "^3.0.14",
|
||||||
|
"@types/node": "^20.19.0",
|
||||||
|
"@uni-helper/eslint-config": "^0.1.0",
|
||||||
|
"@uni-helper/uni-env": "^0.1.7",
|
||||||
|
"@uni-helper/uni-types": "1.0.0-alpha.4",
|
||||||
|
"@uni-helper/unocss-preset-uni": "^0.2.11",
|
||||||
|
"@uni-helper/vite-plugin-uni-components": "^0.1.0",
|
||||||
|
"@uni-helper/vite-plugin-uni-layouts": "^0.1.10",
|
||||||
|
"@uni-helper/vite-plugin-uni-manifest": "^0.2.8",
|
||||||
|
"@uni-helper/vite-plugin-uni-pages": "^0.2.28",
|
||||||
|
"@uni-helper/volar-service-uni-pages": "^0.2.28",
|
||||||
|
"@unocss/eslint-config": "^0.62.4",
|
||||||
|
"@vue/runtime-core": "^3.5.16",
|
||||||
|
"@vue/tsconfig": "^0.5.1",
|
||||||
|
"miniprogram-api-typings": "^3.12.3",
|
||||||
|
"sass": "~1.79.6",
|
||||||
|
"sass-embedded": "~1.79.6",
|
||||||
|
"typescript": "~5.5.4",
|
||||||
|
"unocss": "^0.62.4",
|
||||||
|
"unplugin-auto-import": "^0.18.6",
|
||||||
|
"unplugin-vue-components": "^28.7.0",
|
||||||
|
"vite": "^5.4.19",
|
||||||
|
"vue-tsc": "^2.2.10"
|
||||||
|
}
|
||||||
|
}
|
||||||
39
pages.config.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { defineUniPages } from '@uni-helper/vite-plugin-uni-pages'
|
||||||
|
|
||||||
|
export default defineUniPages({
|
||||||
|
pages: [],
|
||||||
|
globalStyle: {
|
||||||
|
backgroundColor: '@bgColor',
|
||||||
|
backgroundColorBottom: '@bgColorBottom',
|
||||||
|
backgroundColorTop: '@bgColorTop',
|
||||||
|
backgroundTextStyle: '@bgTxtStyle',
|
||||||
|
navigationBarBackgroundColor: '#000000',
|
||||||
|
navigationBarTextStyle: '@navTxtStyle',
|
||||||
|
navigationBarTitleText: '天远数据',
|
||||||
|
navigationStyle: 'custom',
|
||||||
|
},
|
||||||
|
|
||||||
|
tabBar: {
|
||||||
|
backgroundColor: '@tabBgColor',
|
||||||
|
borderStyle: '@tabBorderStyle',
|
||||||
|
color: '@tabFontColor',
|
||||||
|
selectedColor: '@tabSelectedColor',
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
pagePath: 'pages/index',
|
||||||
|
text: '',
|
||||||
|
visible: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pagePath: 'pages/agent',
|
||||||
|
text: '',
|
||||||
|
visible: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pagePath: 'pages/me',
|
||||||
|
text: '',
|
||||||
|
visible: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})
|
||||||
13160
pnpm-lock.yaml
generated
Normal file
3
renovate.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"extends": ["github>uni-helper/renovate-config"]
|
||||||
|
}
|
||||||
110
src/App.vue
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
<script setup>
|
||||||
|
import { onLaunch } from '@dcloudio/uni-app'
|
||||||
|
import { refreshToken, getUserInfo, wxminiLogin } from '@/api/apis'
|
||||||
|
import { getAgentInfo } from '@/apis/agent'
|
||||||
|
|
||||||
|
|
||||||
|
onLaunch(() => {
|
||||||
|
login()
|
||||||
|
refreshTokenIfNeeded()
|
||||||
|
getUser()
|
||||||
|
getAgentInformation()
|
||||||
|
})
|
||||||
|
const login = () => {
|
||||||
|
const token = uni.getStorageSync("token")
|
||||||
|
if (token) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uni.login({
|
||||||
|
success: (res) => {
|
||||||
|
let code = res.code
|
||||||
|
wxminiLogin({ code }).then(res => {
|
||||||
|
if (res.code === 200) {
|
||||||
|
uni.setStorageSync("token", res.data.accessToken)
|
||||||
|
uni.setStorageSync("refreshAfter", res.data.refreshAfter)
|
||||||
|
uni.setStorageSync("accessExpire", res.data.accessExpire)
|
||||||
|
getUser()
|
||||||
|
getAgentInformation()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const refreshTokenIfNeeded = async () => {
|
||||||
|
const token = uni.getStorageSync("token")
|
||||||
|
const refreshAfter = uni.getStorageSync("refreshAfter")
|
||||||
|
const accessExpire = uni.getStorageSync("accessExpire")
|
||||||
|
const currentTime = new Date().getTime()
|
||||||
|
|
||||||
|
// 如果 token 已过期,直接返回
|
||||||
|
if (accessExpire) {
|
||||||
|
const accessExpireInMilliseconds = parseInt(accessExpire) * 1000 // 转换为毫秒级
|
||||||
|
if (currentTime > accessExpireInMilliseconds) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有 token,直接返回
|
||||||
|
if (!token) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果有 refreshAfter,检查当前时间是否超过 refreshAfter
|
||||||
|
if (refreshAfter) {
|
||||||
|
const refreshAfterInMilliseconds = parseInt(refreshAfter) * 1000 // 转换为毫秒级
|
||||||
|
if (currentTime < refreshAfterInMilliseconds) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有 refreshAfter 或者时间超过 refreshAfter,执行刷新 token 的请求
|
||||||
|
try {
|
||||||
|
const res = await refreshToken()
|
||||||
|
if (res.code === 200) {
|
||||||
|
uni.setStorageSync("token", res.data.accessToken)
|
||||||
|
uni.setStorageSync("refreshAfter", res.data.refreshAfter)
|
||||||
|
uni.setStorageSync("accessExpire", res.data.accessExpire)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('刷新token失败', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const getAgentInformation = async () => {
|
||||||
|
const token = uni.getStorageSync("token")
|
||||||
|
if (!token) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await getAgentInfo()
|
||||||
|
if (res.code === 200 && res.data) {
|
||||||
|
// 将代理信息存入缓存
|
||||||
|
uni.setStorageSync("agentInfo", {
|
||||||
|
level: res.data.level,
|
||||||
|
isAgent: res.data.is_agent, // 判断是否是代理
|
||||||
|
status: res.data.status, // 获取代理状态 0=待审核,1=审核通过,2=审核未通过,3=未申请
|
||||||
|
agentID: res.data.agent_id,
|
||||||
|
mobile: res.data.mobile,
|
||||||
|
isRealName: res.data.is_real_name,
|
||||||
|
expiryTime: res.data.expiry_time
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('代理信息已获取并存入缓存')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取代理信息失败', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getUser = async () => {
|
||||||
|
const token = uni.getStorageSync("token")
|
||||||
|
if (!token) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const res = await getUserInfo()
|
||||||
|
if (res.code === 200) {
|
||||||
|
console.log(res.data)
|
||||||
|
uni.setStorageSync("userInfo", res.data.userInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
179
src/api/apis.js
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
// api/index.js
|
||||||
|
import request from '@/utils/request.js'
|
||||||
|
|
||||||
|
export function getUserInfo(data) {
|
||||||
|
return request({
|
||||||
|
url: '/user/detail',
|
||||||
|
method: 'GET',
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function login(data) {
|
||||||
|
return request({
|
||||||
|
url: '/user/mobileCodeLogin',
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function wxminiLogin(data) {
|
||||||
|
return request({
|
||||||
|
url: '/user/wxMiniAuth',
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function bindMobile(data) {
|
||||||
|
return request({
|
||||||
|
url: '/user/bindMobile',
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function getCode(data) {
|
||||||
|
return request({
|
||||||
|
url: '/auth/sendSms',
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function getProduct(en) {
|
||||||
|
return request({
|
||||||
|
url: `/product/en/${en}`,
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function queryExample(params) {
|
||||||
|
return request({
|
||||||
|
url: `/query/example`,
|
||||||
|
method: 'GET',
|
||||||
|
params,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function queryMarriage(data) {
|
||||||
|
return request({
|
||||||
|
url: `/query/marriage`,
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function queryhomeService(data) {
|
||||||
|
return request({
|
||||||
|
url: `/query/homeService`,
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function queryriskAssessment(data) {
|
||||||
|
return request({
|
||||||
|
url: `/query/riskAssessment`,
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function querycompanyInfo(data) {
|
||||||
|
return request({
|
||||||
|
url: `/query/companyInfo`,
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function queryrentalInfo(data) {
|
||||||
|
return request({
|
||||||
|
url: `/query/rentalInfo`,
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function querypreLoanBackgroundCheck(data) {
|
||||||
|
return request({
|
||||||
|
url: `/query/preLoanBackgroundCheck`,
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function querybackgroundCheck(data) {
|
||||||
|
return request({
|
||||||
|
url: `/query/backgroundCheck`,
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function queryResultByOrder(orderID) {
|
||||||
|
return request({
|
||||||
|
url: `/query/orderId/${orderID}`,
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function queryList(params) {
|
||||||
|
return request({
|
||||||
|
url: `/query/list`,
|
||||||
|
method: 'GET',
|
||||||
|
params,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function queryProvisionalOrder(id) {
|
||||||
|
return request({
|
||||||
|
url: `/query/provisional_order/${id}`,
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function payment(data) {
|
||||||
|
return request({
|
||||||
|
url: `/pay/payment`,
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function iapPaymentCallback(data) {
|
||||||
|
return request({
|
||||||
|
url: `/pay/iap_callback`,
|
||||||
|
method: 'POST',
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function getAgentRevenue() {
|
||||||
|
return request({
|
||||||
|
url: '/agent/revenue',
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function refreshToken() {
|
||||||
|
return request({
|
||||||
|
url: '/user/getToken',
|
||||||
|
method: 'POST',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取最新版本信息
|
||||||
|
export function getLatestVersion() {
|
||||||
|
return request({
|
||||||
|
url: '/app/version',
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注销账号
|
||||||
|
*/
|
||||||
|
export function cancelAccount() {
|
||||||
|
return request({
|
||||||
|
url: '/user/cancelOut',
|
||||||
|
method: 'POST'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取APP产品信息
|
||||||
|
* @param {string} feature - 产品标识
|
||||||
|
*/
|
||||||
|
export function getAppProduct(feature) {
|
||||||
|
return request({
|
||||||
|
url: `/product/app_en/${feature}`,
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
}
|
||||||
163
src/apis/agent.js
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
// 代理相关API
|
||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取代理信息
|
||||||
|
*/
|
||||||
|
export const getAgentInfo = () => {
|
||||||
|
return request({
|
||||||
|
url: '/agent/info',
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取代理佣金列表
|
||||||
|
* @param {Object} params 查询参数 {page, page_size}
|
||||||
|
*/
|
||||||
|
export const getAgentCommission = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/agent/commission',
|
||||||
|
method: 'GET',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取代理奖励列表
|
||||||
|
* @param {Object} params 查询参数 {page, page_size}
|
||||||
|
*/
|
||||||
|
export const getAgentRewards = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/agent/rewards',
|
||||||
|
method: 'GET',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取代理状态
|
||||||
|
*/
|
||||||
|
export const getAgentStatus = () => {
|
||||||
|
return request({
|
||||||
|
url: '/agent/status',
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 代理申请
|
||||||
|
* @param {Object} data 申请数据 {region, mobile, wechat_id, code, ancestor?}
|
||||||
|
*/
|
||||||
|
export const applyAgent = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/agent/apply',
|
||||||
|
method: 'POST',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成为会员
|
||||||
|
* @param {Object} data 成为会员数据 {product_id}
|
||||||
|
*/
|
||||||
|
export const activateAgentMembership = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/agent/membership/activate',
|
||||||
|
method: 'POST',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取代理会员用户配置
|
||||||
|
* @param {Object} params 查询参数 {product_id}
|
||||||
|
*/
|
||||||
|
export const getAgentMembershipUserConfig = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/agent/membership/user_config',
|
||||||
|
method: 'GET',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存代理会员用户配置
|
||||||
|
* @param {Object} data 配置数据
|
||||||
|
*/
|
||||||
|
export const saveAgentMembershipUserConfig = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/agent/membership/save_user_config',
|
||||||
|
method: 'POST',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取产品配置
|
||||||
|
*/
|
||||||
|
export const getProductConfig = () => {
|
||||||
|
return request({
|
||||||
|
url: '/agent/product_config',
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成推广链接
|
||||||
|
* @param {Object} data 推广数据 {product, price}
|
||||||
|
*/
|
||||||
|
export const generatePromotionLink = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/agent/generating_link',
|
||||||
|
method: 'POST',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取代理收益
|
||||||
|
*/
|
||||||
|
export const getAgentRevenue = () => {
|
||||||
|
return request({
|
||||||
|
url: '/agent/revenue',
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 代理提现
|
||||||
|
* @param {Object} data 提现数据 {payee_account, amount, payee_name}
|
||||||
|
*/
|
||||||
|
export const agentWithdrawal = (data) => {
|
||||||
|
return request({
|
||||||
|
url: '/agent/withdrawal',
|
||||||
|
method: 'POST',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取提现记录
|
||||||
|
* @param {Object} params 查询参数 {page, page_size}
|
||||||
|
*/
|
||||||
|
export const getWithdrawalRecords = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/agent/withdrawal',
|
||||||
|
method: 'GET',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取推广二维码图片
|
||||||
|
* @param {Object} params 查询参数 {qrcode_type, qrcode_url}
|
||||||
|
*/
|
||||||
|
export const getPromotionQrcode = (params) => {
|
||||||
|
return request({
|
||||||
|
url: '/agent/promotion/qrcode',
|
||||||
|
method: 'GET',
|
||||||
|
params,
|
||||||
|
responseType: 'blob' // 接收图片数据
|
||||||
|
})
|
||||||
|
}
|
||||||
50
src/app.scss
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
:root {
|
||||||
|
--dark-bg: #18181c;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
margin: auto !important;
|
||||||
|
@apply max-w-lg;
|
||||||
|
font-size: 4px; // * 方便unocss计算:1单位 = 0.25rem = 1px
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
page,
|
||||||
|
#app {
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background: #f8f8f8
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark {
|
||||||
|
background: var(--dark-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card{
|
||||||
|
@apply border border-gray-200 rounded-xl bg-white p-6 shadow-md;
|
||||||
|
}
|
||||||
|
.card-p-0{
|
||||||
|
@apply border border-gray-200 rounded-xl bg-white shadow-md;
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
@apply mx-auto mt-2 w-68 border rounded-3xl bg-gradient-to-r from-blue-400 via-green-500 to-teal-500 py-2 text-center text-white font-bold;
|
||||||
|
}
|
||||||
|
.scrollbar-hidden {
|
||||||
|
scrollbar-width: none; /* Firefox */
|
||||||
|
}
|
||||||
|
.scrollbar-hidden::-webkit-scrollbar {
|
||||||
|
display: none; /* Chrome, Safari, and Edge */
|
||||||
|
}
|
||||||
|
|
||||||
|
.safe-area-top {
|
||||||
|
padding-top: env(safe-area-inset-top);
|
||||||
|
}
|
||||||
|
.safe-area-bottom {
|
||||||
|
padding-bottom: env(safe-area-inset-bottom);
|
||||||
|
}
|
||||||
642
src/auto-imports.d.ts
vendored
Normal file
@@ -0,0 +1,642 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
/* prettier-ignore */
|
||||||
|
// @ts-nocheck
|
||||||
|
// noinspection JSUnusedGlobalSymbols
|
||||||
|
// Generated by unplugin-auto-import
|
||||||
|
// biome-ignore lint: disable
|
||||||
|
export {}
|
||||||
|
declare global {
|
||||||
|
const EffectScope: typeof import('vue')['EffectScope']
|
||||||
|
const aesDecrypt: typeof import('./utils/crypto.js')['aesDecrypt']
|
||||||
|
const aesEncrypt: typeof import('./utils/crypto.js')['aesEncrypt']
|
||||||
|
const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
|
||||||
|
const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
|
||||||
|
const autoUpdateCheck: typeof import('./utils/autoUpdateCheck.js')['default']
|
||||||
|
const chatCrypto: typeof import('./utils/chatCrypto.js')['default']
|
||||||
|
const chatEncrypt: typeof import('./utils/chatEncrypt.js')['default']
|
||||||
|
const computed: typeof import('vue')['computed']
|
||||||
|
const computedAsync: typeof import('@vueuse/core')['computedAsync']
|
||||||
|
const computedEager: typeof import('@vueuse/core')['computedEager']
|
||||||
|
const computedInject: typeof import('@vueuse/core')['computedInject']
|
||||||
|
const computedWithControl: typeof import('@vueuse/core')['computedWithControl']
|
||||||
|
const controlledComputed: typeof import('@vueuse/core')['controlledComputed']
|
||||||
|
const controlledRef: typeof import('@vueuse/core')['controlledRef']
|
||||||
|
const createApp: typeof import('vue')['createApp']
|
||||||
|
const createEventHook: typeof import('@vueuse/core')['createEventHook']
|
||||||
|
const createGlobalState: typeof import('@vueuse/core')['createGlobalState']
|
||||||
|
const createInjectionState: typeof import('@vueuse/core')['createInjectionState']
|
||||||
|
const createReactiveFn: typeof import('@vueuse/core')['createReactiveFn']
|
||||||
|
const createReusableTemplate: typeof import('@vueuse/core')['createReusableTemplate']
|
||||||
|
const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable']
|
||||||
|
const createTemplatePromise: typeof import('@vueuse/core')['createTemplatePromise']
|
||||||
|
const createUnrefFn: typeof import('@vueuse/core')['createUnrefFn']
|
||||||
|
const customRef: typeof import('vue')['customRef']
|
||||||
|
const debouncedRef: typeof import('@vueuse/core')['debouncedRef']
|
||||||
|
const debouncedWatch: typeof import('@vueuse/core')['debouncedWatch']
|
||||||
|
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
|
||||||
|
const defineComponent: typeof import('vue')['defineComponent']
|
||||||
|
const eagerComputed: typeof import('@vueuse/core')['eagerComputed']
|
||||||
|
const effectScope: typeof import('vue')['effectScope']
|
||||||
|
const extendRef: typeof import('@vueuse/core')['extendRef']
|
||||||
|
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
|
||||||
|
const getCurrentScope: typeof import('vue')['getCurrentScope']
|
||||||
|
const h: typeof import('vue')['h']
|
||||||
|
const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch']
|
||||||
|
const inject: typeof import('vue')['inject']
|
||||||
|
const injectLocal: typeof import('@vueuse/core')['injectLocal']
|
||||||
|
const isDefined: typeof import('@vueuse/core')['isDefined']
|
||||||
|
const isProxy: typeof import('vue')['isProxy']
|
||||||
|
const isReactive: typeof import('vue')['isReactive']
|
||||||
|
const isReadonly: typeof import('vue')['isReadonly']
|
||||||
|
const isRef: typeof import('vue')['isRef']
|
||||||
|
const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable']
|
||||||
|
const markRaw: typeof import('vue')['markRaw']
|
||||||
|
const nextTick: typeof import('vue')['nextTick']
|
||||||
|
const onActivated: typeof import('vue')['onActivated']
|
||||||
|
const onAddToFavorites: typeof import('@dcloudio/uni-app')['onAddToFavorites']
|
||||||
|
const onBackPress: typeof import('@dcloudio/uni-app')['onBackPress']
|
||||||
|
const onBeforeMount: typeof import('vue')['onBeforeMount']
|
||||||
|
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
|
||||||
|
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
|
||||||
|
const onClickOutside: typeof import('@vueuse/core')['onClickOutside']
|
||||||
|
const onDeactivated: typeof import('vue')['onDeactivated']
|
||||||
|
const onError: typeof import('@dcloudio/uni-app')['onError']
|
||||||
|
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
|
||||||
|
const onHide: typeof import('@dcloudio/uni-app')['onHide']
|
||||||
|
const onKeyStroke: typeof import('@vueuse/core')['onKeyStroke']
|
||||||
|
const onLaunch: typeof import('@dcloudio/uni-app')['onLaunch']
|
||||||
|
const onLoad: typeof import('@dcloudio/uni-app')['onLoad']
|
||||||
|
const onLongPress: typeof import('@vueuse/core')['onLongPress']
|
||||||
|
const onMounted: typeof import('vue')['onMounted']
|
||||||
|
const onNavigationBarButtonTap: typeof import('@dcloudio/uni-app')['onNavigationBarButtonTap']
|
||||||
|
const onNavigationBarSearchInputChanged: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputChanged']
|
||||||
|
const onNavigationBarSearchInputClicked: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputClicked']
|
||||||
|
const onNavigationBarSearchInputConfirmed: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputConfirmed']
|
||||||
|
const onNavigationBarSearchInputFocusChanged: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputFocusChanged']
|
||||||
|
const onPageNotFound: typeof import('@dcloudio/uni-app')['onPageNotFound']
|
||||||
|
const onPageScroll: typeof import('@dcloudio/uni-app')['onPageScroll']
|
||||||
|
const onPullDownRefresh: typeof import('@dcloudio/uni-app')['onPullDownRefresh']
|
||||||
|
const onReachBottom: typeof import('@dcloudio/uni-app')['onReachBottom']
|
||||||
|
const onReady: typeof import('@dcloudio/uni-app')['onReady']
|
||||||
|
const onRenderTracked: typeof import('vue')['onRenderTracked']
|
||||||
|
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
|
||||||
|
const onResize: typeof import('@dcloudio/uni-app')['onResize']
|
||||||
|
const onScopeDispose: typeof import('vue')['onScopeDispose']
|
||||||
|
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
|
||||||
|
const onShareAppMessage: typeof import('@dcloudio/uni-app')['onShareAppMessage']
|
||||||
|
const onShareTimeline: typeof import('@dcloudio/uni-app')['onShareTimeline']
|
||||||
|
const onShow: typeof import('@dcloudio/uni-app')['onShow']
|
||||||
|
const onStartTyping: typeof import('@vueuse/core')['onStartTyping']
|
||||||
|
const onTabItemTap: typeof import('@dcloudio/uni-app')['onTabItemTap']
|
||||||
|
const onThemeChange: typeof import('@dcloudio/uni-app')['onThemeChange']
|
||||||
|
const onUnhandledRejection: typeof import('@dcloudio/uni-app')['onUnhandledRejection']
|
||||||
|
const onUnload: typeof import('@dcloudio/uni-app')['onUnload']
|
||||||
|
const onUnmounted: typeof import('vue')['onUnmounted']
|
||||||
|
const onUpdated: typeof import('vue')['onUpdated']
|
||||||
|
const onWatcherCleanup: typeof import('vue')['onWatcherCleanup']
|
||||||
|
const pausableWatch: typeof import('@vueuse/core')['pausableWatch']
|
||||||
|
const provide: typeof import('vue')['provide']
|
||||||
|
const provideLocal: typeof import('@vueuse/core')['provideLocal']
|
||||||
|
const reactify: typeof import('@vueuse/core')['reactify']
|
||||||
|
const reactifyObject: typeof import('@vueuse/core')['reactifyObject']
|
||||||
|
const reactive: typeof import('vue')['reactive']
|
||||||
|
const reactiveComputed: typeof import('@vueuse/core')['reactiveComputed']
|
||||||
|
const reactiveOmit: typeof import('@vueuse/core')['reactiveOmit']
|
||||||
|
const reactivePick: typeof import('@vueuse/core')['reactivePick']
|
||||||
|
const readonly: typeof import('vue')['readonly']
|
||||||
|
const ref: typeof import('vue')['ref']
|
||||||
|
const refAutoReset: typeof import('@vueuse/core')['refAutoReset']
|
||||||
|
const refDebounced: typeof import('@vueuse/core')['refDebounced']
|
||||||
|
const refDefault: typeof import('@vueuse/core')['refDefault']
|
||||||
|
const refThrottled: typeof import('@vueuse/core')['refThrottled']
|
||||||
|
const refWithControl: typeof import('@vueuse/core')['refWithControl']
|
||||||
|
const request: typeof import('./utils/request.js')['default']
|
||||||
|
const resolveComponent: typeof import('vue')['resolveComponent']
|
||||||
|
const resolveRef: typeof import('@vueuse/core')['resolveRef']
|
||||||
|
const resolveUnref: typeof import('@vueuse/core')['resolveUnref']
|
||||||
|
const setupRouterGuard: typeof import('./utils/routerGuard.js')['setupRouterGuard']
|
||||||
|
const shallowReactive: typeof import('vue')['shallowReactive']
|
||||||
|
const shallowReadonly: typeof import('vue')['shallowReadonly']
|
||||||
|
const shallowRef: typeof import('vue')['shallowRef']
|
||||||
|
const syncRef: typeof import('@vueuse/core')['syncRef']
|
||||||
|
const syncRefs: typeof import('@vueuse/core')['syncRefs']
|
||||||
|
const templateRef: typeof import('@vueuse/core')['templateRef']
|
||||||
|
const throttledRef: typeof import('@vueuse/core')['throttledRef']
|
||||||
|
const throttledWatch: typeof import('@vueuse/core')['throttledWatch']
|
||||||
|
const toRaw: typeof import('vue')['toRaw']
|
||||||
|
const toReactive: typeof import('@vueuse/core')['toReactive']
|
||||||
|
const toRef: typeof import('vue')['toRef']
|
||||||
|
const toRefs: typeof import('vue')['toRefs']
|
||||||
|
const toValue: typeof import('vue')['toValue']
|
||||||
|
const triggerRef: typeof import('vue')['triggerRef']
|
||||||
|
const tryOnBeforeMount: typeof import('@vueuse/core')['tryOnBeforeMount']
|
||||||
|
const tryOnBeforeUnmount: typeof import('@vueuse/core')['tryOnBeforeUnmount']
|
||||||
|
const tryOnMounted: typeof import('@vueuse/core')['tryOnMounted']
|
||||||
|
const tryOnScopeDispose: typeof import('@vueuse/core')['tryOnScopeDispose']
|
||||||
|
const tryOnUnmounted: typeof import('@vueuse/core')['tryOnUnmounted']
|
||||||
|
const unref: typeof import('vue')['unref']
|
||||||
|
const unrefElement: typeof import('@vueuse/core')['unrefElement']
|
||||||
|
const until: typeof import('@vueuse/core')['until']
|
||||||
|
const useActiveElement: typeof import('@vueuse/core')['useActiveElement']
|
||||||
|
const useAnimate: typeof import('@vueuse/core')['useAnimate']
|
||||||
|
const useArrayDifference: typeof import('@vueuse/core')['useArrayDifference']
|
||||||
|
const useArrayEvery: typeof import('@vueuse/core')['useArrayEvery']
|
||||||
|
const useArrayFilter: typeof import('@vueuse/core')['useArrayFilter']
|
||||||
|
const useArrayFind: typeof import('@vueuse/core')['useArrayFind']
|
||||||
|
const useArrayFindIndex: typeof import('@vueuse/core')['useArrayFindIndex']
|
||||||
|
const useArrayFindLast: typeof import('@vueuse/core')['useArrayFindLast']
|
||||||
|
const useArrayIncludes: typeof import('@vueuse/core')['useArrayIncludes']
|
||||||
|
const useArrayJoin: typeof import('@vueuse/core')['useArrayJoin']
|
||||||
|
const useArrayMap: typeof import('@vueuse/core')['useArrayMap']
|
||||||
|
const useArrayReduce: typeof import('@vueuse/core')['useArrayReduce']
|
||||||
|
const useArraySome: typeof import('@vueuse/core')['useArraySome']
|
||||||
|
const useArrayUnique: typeof import('@vueuse/core')['useArrayUnique']
|
||||||
|
const useAsyncQueue: typeof import('@vueuse/core')['useAsyncQueue']
|
||||||
|
const useAsyncState: typeof import('@vueuse/core')['useAsyncState']
|
||||||
|
const useAttrs: typeof import('vue')['useAttrs']
|
||||||
|
const useBase64: typeof import('@vueuse/core')['useBase64']
|
||||||
|
const useBattery: typeof import('@vueuse/core')['useBattery']
|
||||||
|
const useBluetooth: typeof import('@vueuse/core')['useBluetooth']
|
||||||
|
const useBreakpoints: typeof import('@vueuse/core')['useBreakpoints']
|
||||||
|
const useBroadcastChannel: typeof import('@vueuse/core')['useBroadcastChannel']
|
||||||
|
const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation']
|
||||||
|
const useCached: typeof import('@vueuse/core')['useCached']
|
||||||
|
const useClipboard: typeof import('@vueuse/core')['useClipboard']
|
||||||
|
const useClipboardItems: typeof import('@vueuse/core')['useClipboardItems']
|
||||||
|
const useCloned: typeof import('@vueuse/core')['useCloned']
|
||||||
|
const useColorMode: typeof import('@vueuse/core')['useColorMode']
|
||||||
|
const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog']
|
||||||
|
const useCounter: typeof import('@vueuse/core')['useCounter']
|
||||||
|
const useCssModule: typeof import('vue')['useCssModule']
|
||||||
|
const useCssVar: typeof import('@vueuse/core')['useCssVar']
|
||||||
|
const useCssVars: typeof import('vue')['useCssVars']
|
||||||
|
const useCurrentElement: typeof import('@vueuse/core')['useCurrentElement']
|
||||||
|
const useCycleList: typeof import('@vueuse/core')['useCycleList']
|
||||||
|
const useDark: typeof import('@vueuse/core')['useDark']
|
||||||
|
const useDateFormat: typeof import('@vueuse/core')['useDateFormat']
|
||||||
|
const useDebounce: typeof import('@vueuse/core')['useDebounce']
|
||||||
|
const useDebounceFn: typeof import('@vueuse/core')['useDebounceFn']
|
||||||
|
const useDebouncedRefHistory: typeof import('@vueuse/core')['useDebouncedRefHistory']
|
||||||
|
const useDeviceMotion: typeof import('@vueuse/core')['useDeviceMotion']
|
||||||
|
const useDeviceOrientation: typeof import('@vueuse/core')['useDeviceOrientation']
|
||||||
|
const useDevicePixelRatio: typeof import('@vueuse/core')['useDevicePixelRatio']
|
||||||
|
const useDevicesList: typeof import('@vueuse/core')['useDevicesList']
|
||||||
|
const useDisablePullRefresh: typeof import('./composables/useDisablePullRefresh.js')['useDisablePullRefresh']
|
||||||
|
const useDisplayMedia: typeof import('@vueuse/core')['useDisplayMedia']
|
||||||
|
const useDocumentVisibility: typeof import('@vueuse/core')['useDocumentVisibility']
|
||||||
|
const useDraggable: typeof import('@vueuse/core')['useDraggable']
|
||||||
|
const useDropZone: typeof import('@vueuse/core')['useDropZone']
|
||||||
|
const useElementBounding: typeof import('@vueuse/core')['useElementBounding']
|
||||||
|
const useElementByPoint: typeof import('@vueuse/core')['useElementByPoint']
|
||||||
|
const useElementHover: typeof import('@vueuse/core')['useElementHover']
|
||||||
|
const useElementSize: typeof import('@vueuse/core')['useElementSize']
|
||||||
|
const useElementVisibility: typeof import('@vueuse/core')['useElementVisibility']
|
||||||
|
const useEventBus: typeof import('@vueuse/core')['useEventBus']
|
||||||
|
const useEventListener: typeof import('@vueuse/core')['useEventListener']
|
||||||
|
const useEventSource: typeof import('@vueuse/core')['useEventSource']
|
||||||
|
const useEyeDropper: typeof import('@vueuse/core')['useEyeDropper']
|
||||||
|
const useFavicon: typeof import('@vueuse/core')['useFavicon']
|
||||||
|
const useFetch: typeof import('@vueuse/core')['useFetch']
|
||||||
|
const useFileDialog: typeof import('@vueuse/core')['useFileDialog']
|
||||||
|
const useFileSystemAccess: typeof import('@vueuse/core')['useFileSystemAccess']
|
||||||
|
const useFocus: typeof import('@vueuse/core')['useFocus']
|
||||||
|
const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin']
|
||||||
|
const useFps: typeof import('@vueuse/core')['useFps']
|
||||||
|
const useFullscreen: typeof import('@vueuse/core')['useFullscreen']
|
||||||
|
const useGamepad: typeof import('@vueuse/core')['useGamepad']
|
||||||
|
const useGeolocation: typeof import('@vueuse/core')['useGeolocation']
|
||||||
|
const useHotUpdate: typeof import('./composables/useHotUpdate.js')['useHotUpdate']
|
||||||
|
const useId: typeof import('vue')['useId']
|
||||||
|
const useIdle: typeof import('@vueuse/core')['useIdle']
|
||||||
|
const useImage: typeof import('@vueuse/core')['useImage']
|
||||||
|
const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll']
|
||||||
|
const useIntersectionObserver: typeof import('@vueuse/core')['useIntersectionObserver']
|
||||||
|
const useInterval: typeof import('@vueuse/core')['useInterval']
|
||||||
|
const useIntervalFn: typeof import('@vueuse/core')['useIntervalFn']
|
||||||
|
const useKeyModifier: typeof import('@vueuse/core')['useKeyModifier']
|
||||||
|
const useLastChanged: typeof import('@vueuse/core')['useLastChanged']
|
||||||
|
const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage']
|
||||||
|
const useMagicKeys: typeof import('@vueuse/core')['useMagicKeys']
|
||||||
|
const useManualRefHistory: typeof import('@vueuse/core')['useManualRefHistory']
|
||||||
|
const useMediaControls: typeof import('@vueuse/core')['useMediaControls']
|
||||||
|
const useMediaQuery: typeof import('@vueuse/core')['useMediaQuery']
|
||||||
|
const useMemoize: typeof import('@vueuse/core')['useMemoize']
|
||||||
|
const useMemory: typeof import('@vueuse/core')['useMemory']
|
||||||
|
const useModel: typeof import('vue')['useModel']
|
||||||
|
const useMounted: typeof import('@vueuse/core')['useMounted']
|
||||||
|
const useMouse: typeof import('@vueuse/core')['useMouse']
|
||||||
|
const useMouseInElement: typeof import('@vueuse/core')['useMouseInElement']
|
||||||
|
const useMousePressed: typeof import('@vueuse/core')['useMousePressed']
|
||||||
|
const useMutationObserver: typeof import('@vueuse/core')['useMutationObserver']
|
||||||
|
const useNavigatorLanguage: typeof import('@vueuse/core')['useNavigatorLanguage']
|
||||||
|
const useNetwork: typeof import('@vueuse/core')['useNetwork']
|
||||||
|
const useNow: typeof import('@vueuse/core')['useNow']
|
||||||
|
const useObjectUrl: typeof import('@vueuse/core')['useObjectUrl']
|
||||||
|
const useOffsetPagination: typeof import('@vueuse/core')['useOffsetPagination']
|
||||||
|
const useOnline: typeof import('@vueuse/core')['useOnline']
|
||||||
|
const usePageLeave: typeof import('@vueuse/core')['usePageLeave']
|
||||||
|
const useParallax: typeof import('@vueuse/core')['useParallax']
|
||||||
|
const useParentElement: typeof import('@vueuse/core')['useParentElement']
|
||||||
|
const usePerformanceObserver: typeof import('@vueuse/core')['usePerformanceObserver']
|
||||||
|
const usePermission: typeof import('@vueuse/core')['usePermission']
|
||||||
|
const usePointer: typeof import('@vueuse/core')['usePointer']
|
||||||
|
const usePointerLock: typeof import('@vueuse/core')['usePointerLock']
|
||||||
|
const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe']
|
||||||
|
const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme']
|
||||||
|
const usePreferredContrast: typeof import('@vueuse/core')['usePreferredContrast']
|
||||||
|
const usePreferredDark: typeof import('@vueuse/core')['usePreferredDark']
|
||||||
|
const usePreferredLanguages: typeof import('@vueuse/core')['usePreferredLanguages']
|
||||||
|
const usePreferredReducedMotion: typeof import('@vueuse/core')['usePreferredReducedMotion']
|
||||||
|
const usePrevious: typeof import('@vueuse/core')['usePrevious']
|
||||||
|
const useRafFn: typeof import('@vueuse/core')['useRafFn']
|
||||||
|
const useRefHistory: typeof import('@vueuse/core')['useRefHistory']
|
||||||
|
const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver']
|
||||||
|
const useScreenOrientation: typeof import('@vueuse/core')['useScreenOrientation']
|
||||||
|
const useScreenSafeArea: typeof import('@vueuse/core')['useScreenSafeArea']
|
||||||
|
const useScriptTag: typeof import('@vueuse/core')['useScriptTag']
|
||||||
|
const useScroll: typeof import('@vueuse/core')['useScroll']
|
||||||
|
const useScrollLock: typeof import('@vueuse/core')['useScrollLock']
|
||||||
|
const useSessionStorage: typeof import('@vueuse/core')['useSessionStorage']
|
||||||
|
const useShare: typeof import('@vueuse/core')['useShare']
|
||||||
|
const useSlots: typeof import('vue')['useSlots']
|
||||||
|
const useSorted: typeof import('@vueuse/core')['useSorted']
|
||||||
|
const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition']
|
||||||
|
const useSpeechSynthesis: typeof import('@vueuse/core')['useSpeechSynthesis']
|
||||||
|
const useStepper: typeof import('@vueuse/core')['useStepper']
|
||||||
|
const useStorage: typeof import('@vueuse/core')['useStorage']
|
||||||
|
const useStorageAsync: typeof import('@vueuse/core')['useStorageAsync']
|
||||||
|
const useStyleTag: typeof import('@vueuse/core')['useStyleTag']
|
||||||
|
const useSupported: typeof import('@vueuse/core')['useSupported']
|
||||||
|
const useSwipe: typeof import('@vueuse/core')['useSwipe']
|
||||||
|
const useTemplateRef: typeof import('vue')['useTemplateRef']
|
||||||
|
const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList']
|
||||||
|
const useTextDirection: typeof import('@vueuse/core')['useTextDirection']
|
||||||
|
const useTextSelection: typeof import('@vueuse/core')['useTextSelection']
|
||||||
|
const useTextareaAutosize: typeof import('@vueuse/core')['useTextareaAutosize']
|
||||||
|
const useThrottle: typeof import('@vueuse/core')['useThrottle']
|
||||||
|
const useThrottleFn: typeof import('@vueuse/core')['useThrottleFn']
|
||||||
|
const useThrottledRefHistory: typeof import('@vueuse/core')['useThrottledRefHistory']
|
||||||
|
const useTimeAgo: typeof import('@vueuse/core')['useTimeAgo']
|
||||||
|
const useTimeout: typeof import('@vueuse/core')['useTimeout']
|
||||||
|
const useTimeoutFn: typeof import('@vueuse/core')['useTimeoutFn']
|
||||||
|
const useTimeoutPoll: typeof import('@vueuse/core')['useTimeoutPoll']
|
||||||
|
const useTimestamp: typeof import('@vueuse/core')['useTimestamp']
|
||||||
|
const useTitle: typeof import('@vueuse/core')['useTitle']
|
||||||
|
const useToNumber: typeof import('@vueuse/core')['useToNumber']
|
||||||
|
const useToString: typeof import('@vueuse/core')['useToString']
|
||||||
|
const useToggle: typeof import('@vueuse/core')['useToggle']
|
||||||
|
const useTransition: typeof import('@vueuse/core')['useTransition']
|
||||||
|
const useUrlSearchParams: typeof import('@vueuse/core')['useUrlSearchParams']
|
||||||
|
const useUserMedia: typeof import('@vueuse/core')['useUserMedia']
|
||||||
|
const useVModel: typeof import('@vueuse/core')['useVModel']
|
||||||
|
const useVModels: typeof import('@vueuse/core')['useVModels']
|
||||||
|
const useVibrate: typeof import('@vueuse/core')['useVibrate']
|
||||||
|
const useVirtualList: typeof import('@vueuse/core')['useVirtualList']
|
||||||
|
const useWakeLock: typeof import('@vueuse/core')['useWakeLock']
|
||||||
|
const useWebNotification: typeof import('@vueuse/core')['useWebNotification']
|
||||||
|
const useWebSocket: typeof import('@vueuse/core')['useWebSocket']
|
||||||
|
const useWebWorker: typeof import('@vueuse/core')['useWebWorker']
|
||||||
|
const useWebWorkerFn: typeof import('@vueuse/core')['useWebWorkerFn']
|
||||||
|
const useWindowFocus: typeof import('@vueuse/core')['useWindowFocus']
|
||||||
|
const useWindowScroll: typeof import('@vueuse/core')['useWindowScroll']
|
||||||
|
const useWindowSize: typeof import('@vueuse/core')['useWindowSize']
|
||||||
|
const watch: typeof import('vue')['watch']
|
||||||
|
const watchArray: typeof import('@vueuse/core')['watchArray']
|
||||||
|
const watchAtMost: typeof import('@vueuse/core')['watchAtMost']
|
||||||
|
const watchDebounced: typeof import('@vueuse/core')['watchDebounced']
|
||||||
|
const watchDeep: typeof import('@vueuse/core')['watchDeep']
|
||||||
|
const watchEffect: typeof import('vue')['watchEffect']
|
||||||
|
const watchIgnorable: typeof import('@vueuse/core')['watchIgnorable']
|
||||||
|
const watchImmediate: typeof import('@vueuse/core')['watchImmediate']
|
||||||
|
const watchOnce: typeof import('@vueuse/core')['watchOnce']
|
||||||
|
const watchPausable: typeof import('@vueuse/core')['watchPausable']
|
||||||
|
const watchPostEffect: typeof import('vue')['watchPostEffect']
|
||||||
|
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
|
||||||
|
const watchThrottled: typeof import('@vueuse/core')['watchThrottled']
|
||||||
|
const watchTriggerable: typeof import('@vueuse/core')['watchTriggerable']
|
||||||
|
const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter']
|
||||||
|
const whenever: typeof import('@vueuse/core')['whenever']
|
||||||
|
}
|
||||||
|
// for type re-export
|
||||||
|
declare global {
|
||||||
|
// @ts-ignore
|
||||||
|
export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
|
||||||
|
import('vue')
|
||||||
|
}
|
||||||
|
|
||||||
|
// for vue template auto import
|
||||||
|
import { UnwrapRef } from 'vue'
|
||||||
|
declare module 'vue' {
|
||||||
|
interface GlobalComponents {}
|
||||||
|
interface ComponentCustomProperties {
|
||||||
|
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
|
||||||
|
readonly aesDecrypt: UnwrapRef<typeof import('./utils/crypto.js')['aesDecrypt']>
|
||||||
|
readonly aesEncrypt: UnwrapRef<typeof import('./utils/crypto.js')['aesEncrypt']>
|
||||||
|
readonly asyncComputed: UnwrapRef<typeof import('@vueuse/core')['asyncComputed']>
|
||||||
|
readonly autoResetRef: UnwrapRef<typeof import('@vueuse/core')['autoResetRef']>
|
||||||
|
readonly autoUpdateCheck: UnwrapRef<typeof import('./utils/autoUpdateCheck.js')['default']>
|
||||||
|
readonly chatCrypto: UnwrapRef<typeof import('./utils/chatCrypto.js')['default']>
|
||||||
|
readonly chatEncrypt: UnwrapRef<typeof import('./utils/chatEncrypt.js')['default']>
|
||||||
|
readonly computed: UnwrapRef<typeof import('vue')['computed']>
|
||||||
|
readonly computedAsync: UnwrapRef<typeof import('@vueuse/core')['computedAsync']>
|
||||||
|
readonly computedEager: UnwrapRef<typeof import('@vueuse/core')['computedEager']>
|
||||||
|
readonly computedInject: UnwrapRef<typeof import('@vueuse/core')['computedInject']>
|
||||||
|
readonly computedWithControl: UnwrapRef<typeof import('@vueuse/core')['computedWithControl']>
|
||||||
|
readonly controlledComputed: UnwrapRef<typeof import('@vueuse/core')['controlledComputed']>
|
||||||
|
readonly controlledRef: UnwrapRef<typeof import('@vueuse/core')['controlledRef']>
|
||||||
|
readonly createApp: UnwrapRef<typeof import('vue')['createApp']>
|
||||||
|
readonly createEventHook: UnwrapRef<typeof import('@vueuse/core')['createEventHook']>
|
||||||
|
readonly createGlobalState: UnwrapRef<typeof import('@vueuse/core')['createGlobalState']>
|
||||||
|
readonly createInjectionState: UnwrapRef<typeof import('@vueuse/core')['createInjectionState']>
|
||||||
|
readonly createReactiveFn: UnwrapRef<typeof import('@vueuse/core')['createReactiveFn']>
|
||||||
|
readonly createReusableTemplate: UnwrapRef<typeof import('@vueuse/core')['createReusableTemplate']>
|
||||||
|
readonly createSharedComposable: UnwrapRef<typeof import('@vueuse/core')['createSharedComposable']>
|
||||||
|
readonly createTemplatePromise: UnwrapRef<typeof import('@vueuse/core')['createTemplatePromise']>
|
||||||
|
readonly createUnrefFn: UnwrapRef<typeof import('@vueuse/core')['createUnrefFn']>
|
||||||
|
readonly customRef: UnwrapRef<typeof import('vue')['customRef']>
|
||||||
|
readonly debouncedRef: UnwrapRef<typeof import('@vueuse/core')['debouncedRef']>
|
||||||
|
readonly debouncedWatch: UnwrapRef<typeof import('@vueuse/core')['debouncedWatch']>
|
||||||
|
readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']>
|
||||||
|
readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']>
|
||||||
|
readonly eagerComputed: UnwrapRef<typeof import('@vueuse/core')['eagerComputed']>
|
||||||
|
readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']>
|
||||||
|
readonly extendRef: UnwrapRef<typeof import('@vueuse/core')['extendRef']>
|
||||||
|
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
|
||||||
|
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
|
||||||
|
readonly h: UnwrapRef<typeof import('vue')['h']>
|
||||||
|
readonly ignorableWatch: UnwrapRef<typeof import('@vueuse/core')['ignorableWatch']>
|
||||||
|
readonly inject: UnwrapRef<typeof import('vue')['inject']>
|
||||||
|
readonly injectLocal: UnwrapRef<typeof import('@vueuse/core')['injectLocal']>
|
||||||
|
readonly isDefined: UnwrapRef<typeof import('@vueuse/core')['isDefined']>
|
||||||
|
readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
|
||||||
|
readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
|
||||||
|
readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']>
|
||||||
|
readonly isRef: UnwrapRef<typeof import('vue')['isRef']>
|
||||||
|
readonly makeDestructurable: UnwrapRef<typeof import('@vueuse/core')['makeDestructurable']>
|
||||||
|
readonly markRaw: UnwrapRef<typeof import('vue')['markRaw']>
|
||||||
|
readonly nextTick: UnwrapRef<typeof import('vue')['nextTick']>
|
||||||
|
readonly onActivated: UnwrapRef<typeof import('vue')['onActivated']>
|
||||||
|
readonly onAddToFavorites: UnwrapRef<typeof import('@dcloudio/uni-app')['onAddToFavorites']>
|
||||||
|
readonly onBackPress: UnwrapRef<typeof import('@dcloudio/uni-app')['onBackPress']>
|
||||||
|
readonly onBeforeMount: UnwrapRef<typeof import('vue')['onBeforeMount']>
|
||||||
|
readonly onBeforeUnmount: UnwrapRef<typeof import('vue')['onBeforeUnmount']>
|
||||||
|
readonly onBeforeUpdate: UnwrapRef<typeof import('vue')['onBeforeUpdate']>
|
||||||
|
readonly onClickOutside: UnwrapRef<typeof import('@vueuse/core')['onClickOutside']>
|
||||||
|
readonly onDeactivated: UnwrapRef<typeof import('vue')['onDeactivated']>
|
||||||
|
readonly onError: UnwrapRef<typeof import('@dcloudio/uni-app')['onError']>
|
||||||
|
readonly onErrorCaptured: UnwrapRef<typeof import('vue')['onErrorCaptured']>
|
||||||
|
readonly onHide: UnwrapRef<typeof import('@dcloudio/uni-app')['onHide']>
|
||||||
|
readonly onKeyStroke: UnwrapRef<typeof import('@vueuse/core')['onKeyStroke']>
|
||||||
|
readonly onLaunch: UnwrapRef<typeof import('@dcloudio/uni-app')['onLaunch']>
|
||||||
|
readonly onLoad: UnwrapRef<typeof import('@dcloudio/uni-app')['onLoad']>
|
||||||
|
readonly onLongPress: UnwrapRef<typeof import('@vueuse/core')['onLongPress']>
|
||||||
|
readonly onMounted: UnwrapRef<typeof import('vue')['onMounted']>
|
||||||
|
readonly onNavigationBarButtonTap: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarButtonTap']>
|
||||||
|
readonly onNavigationBarSearchInputChanged: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputChanged']>
|
||||||
|
readonly onNavigationBarSearchInputClicked: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputClicked']>
|
||||||
|
readonly onNavigationBarSearchInputConfirmed: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputConfirmed']>
|
||||||
|
readonly onNavigationBarSearchInputFocusChanged: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputFocusChanged']>
|
||||||
|
readonly onPageNotFound: UnwrapRef<typeof import('@dcloudio/uni-app')['onPageNotFound']>
|
||||||
|
readonly onPageScroll: UnwrapRef<typeof import('@dcloudio/uni-app')['onPageScroll']>
|
||||||
|
readonly onPullDownRefresh: UnwrapRef<typeof import('@dcloudio/uni-app')['onPullDownRefresh']>
|
||||||
|
readonly onReachBottom: UnwrapRef<typeof import('@dcloudio/uni-app')['onReachBottom']>
|
||||||
|
readonly onReady: UnwrapRef<typeof import('@dcloudio/uni-app')['onReady']>
|
||||||
|
readonly onRenderTracked: UnwrapRef<typeof import('vue')['onRenderTracked']>
|
||||||
|
readonly onRenderTriggered: UnwrapRef<typeof import('vue')['onRenderTriggered']>
|
||||||
|
readonly onResize: UnwrapRef<typeof import('@dcloudio/uni-app')['onResize']>
|
||||||
|
readonly onScopeDispose: UnwrapRef<typeof import('vue')['onScopeDispose']>
|
||||||
|
readonly onServerPrefetch: UnwrapRef<typeof import('vue')['onServerPrefetch']>
|
||||||
|
readonly onShareAppMessage: UnwrapRef<typeof import('@dcloudio/uni-app')['onShareAppMessage']>
|
||||||
|
readonly onShareTimeline: UnwrapRef<typeof import('@dcloudio/uni-app')['onShareTimeline']>
|
||||||
|
readonly onShow: UnwrapRef<typeof import('@dcloudio/uni-app')['onShow']>
|
||||||
|
readonly onStartTyping: UnwrapRef<typeof import('@vueuse/core')['onStartTyping']>
|
||||||
|
readonly onTabItemTap: UnwrapRef<typeof import('@dcloudio/uni-app')['onTabItemTap']>
|
||||||
|
readonly onThemeChange: UnwrapRef<typeof import('@dcloudio/uni-app')['onThemeChange']>
|
||||||
|
readonly onUnhandledRejection: UnwrapRef<typeof import('@dcloudio/uni-app')['onUnhandledRejection']>
|
||||||
|
readonly onUnload: UnwrapRef<typeof import('@dcloudio/uni-app')['onUnload']>
|
||||||
|
readonly onUnmounted: UnwrapRef<typeof import('vue')['onUnmounted']>
|
||||||
|
readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
|
||||||
|
readonly onWatcherCleanup: UnwrapRef<typeof import('vue')['onWatcherCleanup']>
|
||||||
|
readonly pausableWatch: UnwrapRef<typeof import('@vueuse/core')['pausableWatch']>
|
||||||
|
readonly provide: UnwrapRef<typeof import('vue')['provide']>
|
||||||
|
readonly provideLocal: UnwrapRef<typeof import('@vueuse/core')['provideLocal']>
|
||||||
|
readonly reactify: UnwrapRef<typeof import('@vueuse/core')['reactify']>
|
||||||
|
readonly reactifyObject: UnwrapRef<typeof import('@vueuse/core')['reactifyObject']>
|
||||||
|
readonly reactive: UnwrapRef<typeof import('vue')['reactive']>
|
||||||
|
readonly reactiveComputed: UnwrapRef<typeof import('@vueuse/core')['reactiveComputed']>
|
||||||
|
readonly reactiveOmit: UnwrapRef<typeof import('@vueuse/core')['reactiveOmit']>
|
||||||
|
readonly reactivePick: UnwrapRef<typeof import('@vueuse/core')['reactivePick']>
|
||||||
|
readonly readonly: UnwrapRef<typeof import('vue')['readonly']>
|
||||||
|
readonly ref: UnwrapRef<typeof import('vue')['ref']>
|
||||||
|
readonly refAutoReset: UnwrapRef<typeof import('@vueuse/core')['refAutoReset']>
|
||||||
|
readonly refDebounced: UnwrapRef<typeof import('@vueuse/core')['refDebounced']>
|
||||||
|
readonly refDefault: UnwrapRef<typeof import('@vueuse/core')['refDefault']>
|
||||||
|
readonly refThrottled: UnwrapRef<typeof import('@vueuse/core')['refThrottled']>
|
||||||
|
readonly refWithControl: UnwrapRef<typeof import('@vueuse/core')['refWithControl']>
|
||||||
|
readonly request: UnwrapRef<typeof import('./utils/request.js')['default']>
|
||||||
|
readonly resolveComponent: UnwrapRef<typeof import('vue')['resolveComponent']>
|
||||||
|
readonly resolveRef: UnwrapRef<typeof import('@vueuse/core')['resolveRef']>
|
||||||
|
readonly resolveUnref: UnwrapRef<typeof import('@vueuse/core')['resolveUnref']>
|
||||||
|
readonly setupRouterGuard: UnwrapRef<typeof import('./utils/routerGuard.js')['setupRouterGuard']>
|
||||||
|
readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
|
||||||
|
readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']>
|
||||||
|
readonly shallowRef: UnwrapRef<typeof import('vue')['shallowRef']>
|
||||||
|
readonly syncRef: UnwrapRef<typeof import('@vueuse/core')['syncRef']>
|
||||||
|
readonly syncRefs: UnwrapRef<typeof import('@vueuse/core')['syncRefs']>
|
||||||
|
readonly templateRef: UnwrapRef<typeof import('@vueuse/core')['templateRef']>
|
||||||
|
readonly throttledRef: UnwrapRef<typeof import('@vueuse/core')['throttledRef']>
|
||||||
|
readonly throttledWatch: UnwrapRef<typeof import('@vueuse/core')['throttledWatch']>
|
||||||
|
readonly toRaw: UnwrapRef<typeof import('vue')['toRaw']>
|
||||||
|
readonly toReactive: UnwrapRef<typeof import('@vueuse/core')['toReactive']>
|
||||||
|
readonly toRef: UnwrapRef<typeof import('vue')['toRef']>
|
||||||
|
readonly toRefs: UnwrapRef<typeof import('vue')['toRefs']>
|
||||||
|
readonly toValue: UnwrapRef<typeof import('vue')['toValue']>
|
||||||
|
readonly triggerRef: UnwrapRef<typeof import('vue')['triggerRef']>
|
||||||
|
readonly tryOnBeforeMount: UnwrapRef<typeof import('@vueuse/core')['tryOnBeforeMount']>
|
||||||
|
readonly tryOnBeforeUnmount: UnwrapRef<typeof import('@vueuse/core')['tryOnBeforeUnmount']>
|
||||||
|
readonly tryOnMounted: UnwrapRef<typeof import('@vueuse/core')['tryOnMounted']>
|
||||||
|
readonly tryOnScopeDispose: UnwrapRef<typeof import('@vueuse/core')['tryOnScopeDispose']>
|
||||||
|
readonly tryOnUnmounted: UnwrapRef<typeof import('@vueuse/core')['tryOnUnmounted']>
|
||||||
|
readonly unref: UnwrapRef<typeof import('vue')['unref']>
|
||||||
|
readonly unrefElement: UnwrapRef<typeof import('@vueuse/core')['unrefElement']>
|
||||||
|
readonly until: UnwrapRef<typeof import('@vueuse/core')['until']>
|
||||||
|
readonly useActiveElement: UnwrapRef<typeof import('@vueuse/core')['useActiveElement']>
|
||||||
|
readonly useAnimate: UnwrapRef<typeof import('@vueuse/core')['useAnimate']>
|
||||||
|
readonly useArrayDifference: UnwrapRef<typeof import('@vueuse/core')['useArrayDifference']>
|
||||||
|
readonly useArrayEvery: UnwrapRef<typeof import('@vueuse/core')['useArrayEvery']>
|
||||||
|
readonly useArrayFilter: UnwrapRef<typeof import('@vueuse/core')['useArrayFilter']>
|
||||||
|
readonly useArrayFind: UnwrapRef<typeof import('@vueuse/core')['useArrayFind']>
|
||||||
|
readonly useArrayFindIndex: UnwrapRef<typeof import('@vueuse/core')['useArrayFindIndex']>
|
||||||
|
readonly useArrayFindLast: UnwrapRef<typeof import('@vueuse/core')['useArrayFindLast']>
|
||||||
|
readonly useArrayIncludes: UnwrapRef<typeof import('@vueuse/core')['useArrayIncludes']>
|
||||||
|
readonly useArrayJoin: UnwrapRef<typeof import('@vueuse/core')['useArrayJoin']>
|
||||||
|
readonly useArrayMap: UnwrapRef<typeof import('@vueuse/core')['useArrayMap']>
|
||||||
|
readonly useArrayReduce: UnwrapRef<typeof import('@vueuse/core')['useArrayReduce']>
|
||||||
|
readonly useArraySome: UnwrapRef<typeof import('@vueuse/core')['useArraySome']>
|
||||||
|
readonly useArrayUnique: UnwrapRef<typeof import('@vueuse/core')['useArrayUnique']>
|
||||||
|
readonly useAsyncQueue: UnwrapRef<typeof import('@vueuse/core')['useAsyncQueue']>
|
||||||
|
readonly useAsyncState: UnwrapRef<typeof import('@vueuse/core')['useAsyncState']>
|
||||||
|
readonly useAttrs: UnwrapRef<typeof import('vue')['useAttrs']>
|
||||||
|
readonly useBase64: UnwrapRef<typeof import('@vueuse/core')['useBase64']>
|
||||||
|
readonly useBattery: UnwrapRef<typeof import('@vueuse/core')['useBattery']>
|
||||||
|
readonly useBluetooth: UnwrapRef<typeof import('@vueuse/core')['useBluetooth']>
|
||||||
|
readonly useBreakpoints: UnwrapRef<typeof import('@vueuse/core')['useBreakpoints']>
|
||||||
|
readonly useBroadcastChannel: UnwrapRef<typeof import('@vueuse/core')['useBroadcastChannel']>
|
||||||
|
readonly useBrowserLocation: UnwrapRef<typeof import('@vueuse/core')['useBrowserLocation']>
|
||||||
|
readonly useCached: UnwrapRef<typeof import('@vueuse/core')['useCached']>
|
||||||
|
readonly useClipboard: UnwrapRef<typeof import('@vueuse/core')['useClipboard']>
|
||||||
|
readonly useClipboardItems: UnwrapRef<typeof import('@vueuse/core')['useClipboardItems']>
|
||||||
|
readonly useCloned: UnwrapRef<typeof import('@vueuse/core')['useCloned']>
|
||||||
|
readonly useColorMode: UnwrapRef<typeof import('@vueuse/core')['useColorMode']>
|
||||||
|
readonly useConfirmDialog: UnwrapRef<typeof import('@vueuse/core')['useConfirmDialog']>
|
||||||
|
readonly useCounter: UnwrapRef<typeof import('@vueuse/core')['useCounter']>
|
||||||
|
readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
|
||||||
|
readonly useCssVar: UnwrapRef<typeof import('@vueuse/core')['useCssVar']>
|
||||||
|
readonly useCssVars: UnwrapRef<typeof import('vue')['useCssVars']>
|
||||||
|
readonly useCurrentElement: UnwrapRef<typeof import('@vueuse/core')['useCurrentElement']>
|
||||||
|
readonly useCycleList: UnwrapRef<typeof import('@vueuse/core')['useCycleList']>
|
||||||
|
readonly useDark: UnwrapRef<typeof import('@vueuse/core')['useDark']>
|
||||||
|
readonly useDateFormat: UnwrapRef<typeof import('@vueuse/core')['useDateFormat']>
|
||||||
|
readonly useDebounce: UnwrapRef<typeof import('@vueuse/core')['useDebounce']>
|
||||||
|
readonly useDebounceFn: UnwrapRef<typeof import('@vueuse/core')['useDebounceFn']>
|
||||||
|
readonly useDebouncedRefHistory: UnwrapRef<typeof import('@vueuse/core')['useDebouncedRefHistory']>
|
||||||
|
readonly useDeviceMotion: UnwrapRef<typeof import('@vueuse/core')['useDeviceMotion']>
|
||||||
|
readonly useDeviceOrientation: UnwrapRef<typeof import('@vueuse/core')['useDeviceOrientation']>
|
||||||
|
readonly useDevicePixelRatio: UnwrapRef<typeof import('@vueuse/core')['useDevicePixelRatio']>
|
||||||
|
readonly useDevicesList: UnwrapRef<typeof import('@vueuse/core')['useDevicesList']>
|
||||||
|
readonly useDisplayMedia: UnwrapRef<typeof import('@vueuse/core')['useDisplayMedia']>
|
||||||
|
readonly useDocumentVisibility: UnwrapRef<typeof import('@vueuse/core')['useDocumentVisibility']>
|
||||||
|
readonly useDraggable: UnwrapRef<typeof import('@vueuse/core')['useDraggable']>
|
||||||
|
readonly useDropZone: UnwrapRef<typeof import('@vueuse/core')['useDropZone']>
|
||||||
|
readonly useElementBounding: UnwrapRef<typeof import('@vueuse/core')['useElementBounding']>
|
||||||
|
readonly useElementByPoint: UnwrapRef<typeof import('@vueuse/core')['useElementByPoint']>
|
||||||
|
readonly useElementHover: UnwrapRef<typeof import('@vueuse/core')['useElementHover']>
|
||||||
|
readonly useElementSize: UnwrapRef<typeof import('@vueuse/core')['useElementSize']>
|
||||||
|
readonly useElementVisibility: UnwrapRef<typeof import('@vueuse/core')['useElementVisibility']>
|
||||||
|
readonly useEventBus: UnwrapRef<typeof import('@vueuse/core')['useEventBus']>
|
||||||
|
readonly useEventListener: UnwrapRef<typeof import('@vueuse/core')['useEventListener']>
|
||||||
|
readonly useEventSource: UnwrapRef<typeof import('@vueuse/core')['useEventSource']>
|
||||||
|
readonly useEyeDropper: UnwrapRef<typeof import('@vueuse/core')['useEyeDropper']>
|
||||||
|
readonly useFavicon: UnwrapRef<typeof import('@vueuse/core')['useFavicon']>
|
||||||
|
readonly useFetch: UnwrapRef<typeof import('@vueuse/core')['useFetch']>
|
||||||
|
readonly useFileDialog: UnwrapRef<typeof import('@vueuse/core')['useFileDialog']>
|
||||||
|
readonly useFileSystemAccess: UnwrapRef<typeof import('@vueuse/core')['useFileSystemAccess']>
|
||||||
|
readonly useFocus: UnwrapRef<typeof import('@vueuse/core')['useFocus']>
|
||||||
|
readonly useFocusWithin: UnwrapRef<typeof import('@vueuse/core')['useFocusWithin']>
|
||||||
|
readonly useFps: UnwrapRef<typeof import('@vueuse/core')['useFps']>
|
||||||
|
readonly useFullscreen: UnwrapRef<typeof import('@vueuse/core')['useFullscreen']>
|
||||||
|
readonly useGamepad: UnwrapRef<typeof import('@vueuse/core')['useGamepad']>
|
||||||
|
readonly useGeolocation: UnwrapRef<typeof import('@vueuse/core')['useGeolocation']>
|
||||||
|
readonly useHotUpdate: UnwrapRef<typeof import('./composables/useHotUpdate.js')['useHotUpdate']>
|
||||||
|
readonly useId: UnwrapRef<typeof import('vue')['useId']>
|
||||||
|
readonly useIdle: UnwrapRef<typeof import('@vueuse/core')['useIdle']>
|
||||||
|
readonly useImage: UnwrapRef<typeof import('@vueuse/core')['useImage']>
|
||||||
|
readonly useInfiniteScroll: UnwrapRef<typeof import('@vueuse/core')['useInfiniteScroll']>
|
||||||
|
readonly useIntersectionObserver: UnwrapRef<typeof import('@vueuse/core')['useIntersectionObserver']>
|
||||||
|
readonly useInterval: UnwrapRef<typeof import('@vueuse/core')['useInterval']>
|
||||||
|
readonly useIntervalFn: UnwrapRef<typeof import('@vueuse/core')['useIntervalFn']>
|
||||||
|
readonly useKeyModifier: UnwrapRef<typeof import('@vueuse/core')['useKeyModifier']>
|
||||||
|
readonly useLastChanged: UnwrapRef<typeof import('@vueuse/core')['useLastChanged']>
|
||||||
|
readonly useLocalStorage: UnwrapRef<typeof import('@vueuse/core')['useLocalStorage']>
|
||||||
|
readonly useMagicKeys: UnwrapRef<typeof import('@vueuse/core')['useMagicKeys']>
|
||||||
|
readonly useManualRefHistory: UnwrapRef<typeof import('@vueuse/core')['useManualRefHistory']>
|
||||||
|
readonly useMediaControls: UnwrapRef<typeof import('@vueuse/core')['useMediaControls']>
|
||||||
|
readonly useMediaQuery: UnwrapRef<typeof import('@vueuse/core')['useMediaQuery']>
|
||||||
|
readonly useMemoize: UnwrapRef<typeof import('@vueuse/core')['useMemoize']>
|
||||||
|
readonly useMemory: UnwrapRef<typeof import('@vueuse/core')['useMemory']>
|
||||||
|
readonly useModel: UnwrapRef<typeof import('vue')['useModel']>
|
||||||
|
readonly useMounted: UnwrapRef<typeof import('@vueuse/core')['useMounted']>
|
||||||
|
readonly useMouse: UnwrapRef<typeof import('@vueuse/core')['useMouse']>
|
||||||
|
readonly useMouseInElement: UnwrapRef<typeof import('@vueuse/core')['useMouseInElement']>
|
||||||
|
readonly useMousePressed: UnwrapRef<typeof import('@vueuse/core')['useMousePressed']>
|
||||||
|
readonly useMutationObserver: UnwrapRef<typeof import('@vueuse/core')['useMutationObserver']>
|
||||||
|
readonly useNavigatorLanguage: UnwrapRef<typeof import('@vueuse/core')['useNavigatorLanguage']>
|
||||||
|
readonly useNetwork: UnwrapRef<typeof import('@vueuse/core')['useNetwork']>
|
||||||
|
readonly useNow: UnwrapRef<typeof import('@vueuse/core')['useNow']>
|
||||||
|
readonly useObjectUrl: UnwrapRef<typeof import('@vueuse/core')['useObjectUrl']>
|
||||||
|
readonly useOffsetPagination: UnwrapRef<typeof import('@vueuse/core')['useOffsetPagination']>
|
||||||
|
readonly useOnline: UnwrapRef<typeof import('@vueuse/core')['useOnline']>
|
||||||
|
readonly usePageLeave: UnwrapRef<typeof import('@vueuse/core')['usePageLeave']>
|
||||||
|
readonly useParallax: UnwrapRef<typeof import('@vueuse/core')['useParallax']>
|
||||||
|
readonly useParentElement: UnwrapRef<typeof import('@vueuse/core')['useParentElement']>
|
||||||
|
readonly usePerformanceObserver: UnwrapRef<typeof import('@vueuse/core')['usePerformanceObserver']>
|
||||||
|
readonly usePermission: UnwrapRef<typeof import('@vueuse/core')['usePermission']>
|
||||||
|
readonly usePointer: UnwrapRef<typeof import('@vueuse/core')['usePointer']>
|
||||||
|
readonly usePointerLock: UnwrapRef<typeof import('@vueuse/core')['usePointerLock']>
|
||||||
|
readonly usePointerSwipe: UnwrapRef<typeof import('@vueuse/core')['usePointerSwipe']>
|
||||||
|
readonly usePreferredColorScheme: UnwrapRef<typeof import('@vueuse/core')['usePreferredColorScheme']>
|
||||||
|
readonly usePreferredContrast: UnwrapRef<typeof import('@vueuse/core')['usePreferredContrast']>
|
||||||
|
readonly usePreferredDark: UnwrapRef<typeof import('@vueuse/core')['usePreferredDark']>
|
||||||
|
readonly usePreferredLanguages: UnwrapRef<typeof import('@vueuse/core')['usePreferredLanguages']>
|
||||||
|
readonly usePreferredReducedMotion: UnwrapRef<typeof import('@vueuse/core')['usePreferredReducedMotion']>
|
||||||
|
readonly usePrevious: UnwrapRef<typeof import('@vueuse/core')['usePrevious']>
|
||||||
|
readonly useRafFn: UnwrapRef<typeof import('@vueuse/core')['useRafFn']>
|
||||||
|
readonly useRefHistory: UnwrapRef<typeof import('@vueuse/core')['useRefHistory']>
|
||||||
|
readonly useResizeObserver: UnwrapRef<typeof import('@vueuse/core')['useResizeObserver']>
|
||||||
|
readonly useScreenOrientation: UnwrapRef<typeof import('@vueuse/core')['useScreenOrientation']>
|
||||||
|
readonly useScreenSafeArea: UnwrapRef<typeof import('@vueuse/core')['useScreenSafeArea']>
|
||||||
|
readonly useScriptTag: UnwrapRef<typeof import('@vueuse/core')['useScriptTag']>
|
||||||
|
readonly useScroll: UnwrapRef<typeof import('@vueuse/core')['useScroll']>
|
||||||
|
readonly useScrollLock: UnwrapRef<typeof import('@vueuse/core')['useScrollLock']>
|
||||||
|
readonly useSessionStorage: UnwrapRef<typeof import('@vueuse/core')['useSessionStorage']>
|
||||||
|
readonly useShare: UnwrapRef<typeof import('@vueuse/core')['useShare']>
|
||||||
|
readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']>
|
||||||
|
readonly useSorted: UnwrapRef<typeof import('@vueuse/core')['useSorted']>
|
||||||
|
readonly useSpeechRecognition: UnwrapRef<typeof import('@vueuse/core')['useSpeechRecognition']>
|
||||||
|
readonly useSpeechSynthesis: UnwrapRef<typeof import('@vueuse/core')['useSpeechSynthesis']>
|
||||||
|
readonly useStepper: UnwrapRef<typeof import('@vueuse/core')['useStepper']>
|
||||||
|
readonly useStorage: UnwrapRef<typeof import('@vueuse/core')['useStorage']>
|
||||||
|
readonly useStorageAsync: UnwrapRef<typeof import('@vueuse/core')['useStorageAsync']>
|
||||||
|
readonly useStyleTag: UnwrapRef<typeof import('@vueuse/core')['useStyleTag']>
|
||||||
|
readonly useSupported: UnwrapRef<typeof import('@vueuse/core')['useSupported']>
|
||||||
|
readonly useSwipe: UnwrapRef<typeof import('@vueuse/core')['useSwipe']>
|
||||||
|
readonly useTemplateRef: UnwrapRef<typeof import('vue')['useTemplateRef']>
|
||||||
|
readonly useTemplateRefsList: UnwrapRef<typeof import('@vueuse/core')['useTemplateRefsList']>
|
||||||
|
readonly useTextDirection: UnwrapRef<typeof import('@vueuse/core')['useTextDirection']>
|
||||||
|
readonly useTextSelection: UnwrapRef<typeof import('@vueuse/core')['useTextSelection']>
|
||||||
|
readonly useTextareaAutosize: UnwrapRef<typeof import('@vueuse/core')['useTextareaAutosize']>
|
||||||
|
readonly useThrottle: UnwrapRef<typeof import('@vueuse/core')['useThrottle']>
|
||||||
|
readonly useThrottleFn: UnwrapRef<typeof import('@vueuse/core')['useThrottleFn']>
|
||||||
|
readonly useThrottledRefHistory: UnwrapRef<typeof import('@vueuse/core')['useThrottledRefHistory']>
|
||||||
|
readonly useTimeAgo: UnwrapRef<typeof import('@vueuse/core')['useTimeAgo']>
|
||||||
|
readonly useTimeout: UnwrapRef<typeof import('@vueuse/core')['useTimeout']>
|
||||||
|
readonly useTimeoutFn: UnwrapRef<typeof import('@vueuse/core')['useTimeoutFn']>
|
||||||
|
readonly useTimeoutPoll: UnwrapRef<typeof import('@vueuse/core')['useTimeoutPoll']>
|
||||||
|
readonly useTimestamp: UnwrapRef<typeof import('@vueuse/core')['useTimestamp']>
|
||||||
|
readonly useTitle: UnwrapRef<typeof import('@vueuse/core')['useTitle']>
|
||||||
|
readonly useToNumber: UnwrapRef<typeof import('@vueuse/core')['useToNumber']>
|
||||||
|
readonly useToString: UnwrapRef<typeof import('@vueuse/core')['useToString']>
|
||||||
|
readonly useToggle: UnwrapRef<typeof import('@vueuse/core')['useToggle']>
|
||||||
|
readonly useTransition: UnwrapRef<typeof import('@vueuse/core')['useTransition']>
|
||||||
|
readonly useUrlSearchParams: UnwrapRef<typeof import('@vueuse/core')['useUrlSearchParams']>
|
||||||
|
readonly useUserMedia: UnwrapRef<typeof import('@vueuse/core')['useUserMedia']>
|
||||||
|
readonly useVModel: UnwrapRef<typeof import('@vueuse/core')['useVModel']>
|
||||||
|
readonly useVModels: UnwrapRef<typeof import('@vueuse/core')['useVModels']>
|
||||||
|
readonly useVibrate: UnwrapRef<typeof import('@vueuse/core')['useVibrate']>
|
||||||
|
readonly useVirtualList: UnwrapRef<typeof import('@vueuse/core')['useVirtualList']>
|
||||||
|
readonly useWakeLock: UnwrapRef<typeof import('@vueuse/core')['useWakeLock']>
|
||||||
|
readonly useWebNotification: UnwrapRef<typeof import('@vueuse/core')['useWebNotification']>
|
||||||
|
readonly useWebSocket: UnwrapRef<typeof import('@vueuse/core')['useWebSocket']>
|
||||||
|
readonly useWebWorker: UnwrapRef<typeof import('@vueuse/core')['useWebWorker']>
|
||||||
|
readonly useWebWorkerFn: UnwrapRef<typeof import('@vueuse/core')['useWebWorkerFn']>
|
||||||
|
readonly useWindowFocus: UnwrapRef<typeof import('@vueuse/core')['useWindowFocus']>
|
||||||
|
readonly useWindowScroll: UnwrapRef<typeof import('@vueuse/core')['useWindowScroll']>
|
||||||
|
readonly useWindowSize: UnwrapRef<typeof import('@vueuse/core')['useWindowSize']>
|
||||||
|
readonly watch: UnwrapRef<typeof import('vue')['watch']>
|
||||||
|
readonly watchArray: UnwrapRef<typeof import('@vueuse/core')['watchArray']>
|
||||||
|
readonly watchAtMost: UnwrapRef<typeof import('@vueuse/core')['watchAtMost']>
|
||||||
|
readonly watchDebounced: UnwrapRef<typeof import('@vueuse/core')['watchDebounced']>
|
||||||
|
readonly watchDeep: UnwrapRef<typeof import('@vueuse/core')['watchDeep']>
|
||||||
|
readonly watchEffect: UnwrapRef<typeof import('vue')['watchEffect']>
|
||||||
|
readonly watchIgnorable: UnwrapRef<typeof import('@vueuse/core')['watchIgnorable']>
|
||||||
|
readonly watchImmediate: UnwrapRef<typeof import('@vueuse/core')['watchImmediate']>
|
||||||
|
readonly watchOnce: UnwrapRef<typeof import('@vueuse/core')['watchOnce']>
|
||||||
|
readonly watchPausable: UnwrapRef<typeof import('@vueuse/core')['watchPausable']>
|
||||||
|
readonly watchPostEffect: UnwrapRef<typeof import('vue')['watchPostEffect']>
|
||||||
|
readonly watchSyncEffect: UnwrapRef<typeof import('vue')['watchSyncEffect']>
|
||||||
|
readonly watchThrottled: UnwrapRef<typeof import('@vueuse/core')['watchThrottled']>
|
||||||
|
readonly watchTriggerable: UnwrapRef<typeof import('@vueuse/core')['watchTriggerable']>
|
||||||
|
readonly watchWithFilter: UnwrapRef<typeof import('@vueuse/core')['watchWithFilter']>
|
||||||
|
readonly whenever: UnwrapRef<typeof import('@vueuse/core')['whenever']>
|
||||||
|
}
|
||||||
|
}
|
||||||
32
src/components.d.ts
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
/* prettier-ignore */
|
||||||
|
// @ts-nocheck
|
||||||
|
// Generated by vite-plugin-uni-components
|
||||||
|
// Read more: https://github.com/vuejs/core/pull/3399
|
||||||
|
export {}
|
||||||
|
|
||||||
|
declare module 'vue' {
|
||||||
|
export interface GlobalComponents {
|
||||||
|
AgentApplicationForm: typeof import('./components/AgentApplicationForm.vue')['default']
|
||||||
|
GzhQrcode: typeof import('./components/GzhQrcode.vue')['default']
|
||||||
|
Payment: typeof import('./components/Payment.vue')['default']
|
||||||
|
PriceInputPopup: typeof import('./components/PriceInputPopup.vue')['default']
|
||||||
|
QRcode: typeof import('./components/QRcode.vue')['default']
|
||||||
|
VipBanner: typeof import('./components/VipBanner.vue')['default']
|
||||||
|
WdButton: typeof import('wot-design-uni/components/wd-button/wd-button.vue')['default']
|
||||||
|
WdCell: typeof import('wot-design-uni/components/wd-cell/wd-cell.vue')['default']
|
||||||
|
WdCellGroup: typeof import('wot-design-uni/components/wd-cell-group/wd-cell-group.vue')['default']
|
||||||
|
WdCheckbox: typeof import('wot-design-uni/components/wd-checkbox/wd-checkbox.vue')['default']
|
||||||
|
WdColPicker: typeof import('wot-design-uni/components/wd-col-picker/wd-col-picker.vue')['default']
|
||||||
|
WdForm: typeof import('wot-design-uni/components/wd-form/wd-form.vue')['default']
|
||||||
|
WdFormItem: typeof import('wot-design-uni/components/wd-form-item/wd-form-item.vue')['default']
|
||||||
|
WdIcon: typeof import('wot-design-uni/components/wd-icon/wd-icon.vue')['default']
|
||||||
|
WdInput: typeof import('wot-design-uni/components/wd-input/wd-input.vue')['default']
|
||||||
|
WdNavbar: typeof import('wot-design-uni/components/wd-navbar/wd-navbar.vue')['default']
|
||||||
|
WdPicker: typeof import('wot-design-uni/components/wd-picker/wd-picker.vue')['default']
|
||||||
|
WdPopup: typeof import('wot-design-uni/components/wd-popup/wd-popup.vue')['default']
|
||||||
|
WdTabbar: typeof import('wot-design-uni/components/wd-tabbar/wd-tabbar.vue')['default']
|
||||||
|
WdTabbarItem: typeof import('wot-design-uni/components/wd-tabbar-item/wd-tabbar-item.vue')['default']
|
||||||
|
WdToast: typeof import('wot-design-uni/components/wd-toast/wd-toast.vue')['default']
|
||||||
|
}
|
||||||
|
}
|
||||||
339
src/components/AgentApplicationForm.vue
Normal file
@@ -0,0 +1,339 @@
|
|||||||
|
<template>
|
||||||
|
<wd-popup v-model="popupVisible" position="bottom" close-on-click-modal @close="handleClose">
|
||||||
|
<view class="bg-white rounded-t-lg p-4">
|
||||||
|
<view class="flex justify-between items-center mb-4">
|
||||||
|
<text class="text-gray-400" @click="cancel">取消</text>
|
||||||
|
<text class="text-lg font-medium">申请成为代理</text>
|
||||||
|
<text class="text-gray-400" @click="cancel">关闭</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view>
|
||||||
|
|
||||||
|
</view>
|
||||||
|
<wd-form :model="form" :rules="rules">
|
||||||
|
<wd-cell-group border>
|
||||||
|
<!-- 区域选择 -->
|
||||||
|
<wd-col-picker v-model="region" label="代理区域" label-width="100px" prop="region" placeholder="请选择代理区域"
|
||||||
|
:columns="columns" :column-change="handleColumnChange" @confirm="handleRegionConfirm"
|
||||||
|
:display-format="displayFormat" />
|
||||||
|
<!-- 手机号 -->
|
||||||
|
<wd-input label="手机号码" label-width="100px" type="number" v-model="form.mobile" prop="mobile"
|
||||||
|
placeholder="请输入您的手机号" :disabled="true" readonly :rules="[
|
||||||
|
{ required: true, message: '请输入手机号' },
|
||||||
|
{ required: true, pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号' }
|
||||||
|
]" />
|
||||||
|
|
||||||
|
<!-- 验证码 -->
|
||||||
|
<wd-input label="验证码" label-width="100px" type="number" v-model="form.code" prop="code" placeholder="请输入验证码"
|
||||||
|
:rules="[{ required: true, message: '请输入验证码' }]" use-suffix-slot>
|
||||||
|
<template #suffix>
|
||||||
|
<button class="verify-btn" :disabled="countdown > 0" @click.stop="sendVerifyCode">
|
||||||
|
{{ countdown > 0 ? `${countdown}s` : '获取验证码' }}
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
</wd-input>
|
||||||
|
</wd-cell-group>
|
||||||
|
|
||||||
|
<!-- 同意条款的复选框 -->
|
||||||
|
<view class="p-4">
|
||||||
|
<view class="flex items-start">
|
||||||
|
<wd-checkbox v-model="isAgreed" size="16px" class="flex-shrink-0 mr-2"></wd-checkbox>
|
||||||
|
<view class="text-xs text-gray-400 leading-tight">
|
||||||
|
我已阅读并同意
|
||||||
|
<text class="text-blue-400" @click.stop="toUserAgreement">《用户协议》</text>
|
||||||
|
<text class="text-blue-400" @click.stop="toServiceAgreement">《信息技术服务合同》</text>
|
||||||
|
<text class="text-blue-400" @click.stop="toAgentManageAgreement">《推广方管理制度协议》</text>
|
||||||
|
<view class="text-xs text-gray-400 mt-1">点击勾选即代表您同意上述法律文书的相关条款并签署上述法律文书</view>
|
||||||
|
<view class="text-xs text-gray-400 mt-1">手机号未在本平台注册账号则申请后将自动生成账号</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="p-4">
|
||||||
|
<wd-button type="primary" block @click="submitForm">提交申请</wd-button>
|
||||||
|
<wd-button type="default" block class="mt-2" @click="cancel">取消</wd-button>
|
||||||
|
</view>
|
||||||
|
</wd-form>
|
||||||
|
</view>
|
||||||
|
</wd-popup>
|
||||||
|
<wd-toast />
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, watch, onUnmounted, onMounted } from 'vue'
|
||||||
|
import { useColPickerData } from '../hooks/useColPickerData'
|
||||||
|
import { useToast } from 'wot-design-uni'
|
||||||
|
import { getCode } from '@/api/apis.js' // 导入getCode API
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
show: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
ancestor: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const region = ref([]) // 存储地区代码数组
|
||||||
|
const regionText = ref('') // 存储地区文本
|
||||||
|
const isAgreed = ref(false) // 用户是否同意协议
|
||||||
|
|
||||||
|
// 格式化显示文本
|
||||||
|
const displayFormat = (selectedItems) => {
|
||||||
|
if (selectedItems.length === 0) return ''
|
||||||
|
return selectedItems.map(item => item.label).join(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:show', 'submit', 'close'])
|
||||||
|
const form = ref({
|
||||||
|
mobile: '',
|
||||||
|
code: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
// 使用wot-design-ui的toast组件
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
|
// 不再需要监听region变化触发表单验证
|
||||||
|
// watch(region, (newVal) => {
|
||||||
|
// // 当region变化时,触发表单验证
|
||||||
|
// if (formRef.value) {
|
||||||
|
// formRef.value.validate(['region'])
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
|
||||||
|
const popupVisible = ref(false)
|
||||||
|
const countdown = ref(0)
|
||||||
|
let timer = null
|
||||||
|
|
||||||
|
// 初始化省市区数据
|
||||||
|
const { colPickerData, findChildrenByCode } = useColPickerData()
|
||||||
|
const columns = ref([
|
||||||
|
colPickerData.map((item) => {
|
||||||
|
return {
|
||||||
|
value: item.value,
|
||||||
|
label: item.text
|
||||||
|
}
|
||||||
|
})
|
||||||
|
])
|
||||||
|
|
||||||
|
// 表单验证规则 - 仍然保留以供wd-form组件使用
|
||||||
|
const rules = {
|
||||||
|
region: [{
|
||||||
|
required: true,
|
||||||
|
message: '请选择代理区域',
|
||||||
|
validator: (value) => {
|
||||||
|
// 这里直接检查region.value而不是传入的value
|
||||||
|
if (Array.isArray(region.value) && region.value.length === 3) {
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
return Promise.reject('请选择完整的省市区')
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
mobile: [
|
||||||
|
{ required: true, message: '请输入手机号' },
|
||||||
|
{ required: true, pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号' }
|
||||||
|
],
|
||||||
|
code: [{ required: true, message: '请输入验证码' }],
|
||||||
|
}
|
||||||
|
|
||||||
|
// 协议跳转函数
|
||||||
|
const toUserAgreement = () => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/agreement?type=user'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const toServiceAgreement = () => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/agreement?type=service'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const toAgentManageAgreement = () => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/agreement?type=manage'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听show属性变化
|
||||||
|
watch(() => props.show, (newVal) => {
|
||||||
|
popupVisible.value = newVal
|
||||||
|
})
|
||||||
|
|
||||||
|
// 监听popupVisible同步回props.show
|
||||||
|
watch(() => popupVisible.value, (newVal) => {
|
||||||
|
emit('update:show', newVal)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 组件挂载时获取用户信息并填入手机号
|
||||||
|
onMounted(() => {
|
||||||
|
const userInfo = uni.getStorageSync('userInfo')
|
||||||
|
if (userInfo && userInfo.mobile) {
|
||||||
|
form.value.mobile = userInfo.mobile
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Popup关闭事件
|
||||||
|
const handleClose = () => {
|
||||||
|
emit('close')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消按钮
|
||||||
|
const cancel = () => {
|
||||||
|
popupVisible.value = false
|
||||||
|
emit('close')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理列变化
|
||||||
|
const handleColumnChange = ({ selectedItem, resolve, finish }) => {
|
||||||
|
const children = findChildrenByCode(colPickerData, selectedItem.value)
|
||||||
|
if (children && children.length) {
|
||||||
|
resolve(children.map(item => ({
|
||||||
|
label: item.text,
|
||||||
|
value: item.value
|
||||||
|
})))
|
||||||
|
} else {
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 区域选择确认 - 获取选中项的文本值
|
||||||
|
const handleRegionConfirm = ({ value, selectedItems }) => {
|
||||||
|
// 存储地区文本
|
||||||
|
regionText.value = selectedItems.map(item => item.label).join('-')
|
||||||
|
console.log('地区选择完成', regionText.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断手机号是否有效
|
||||||
|
const isPhoneNumberValid = computed(() => {
|
||||||
|
return /^1[3-9]\d{9}$/.test(form.value.mobile)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 发送验证码
|
||||||
|
const sendVerifyCode = async () => {
|
||||||
|
// 验证手机号
|
||||||
|
if (!form.value.mobile) {
|
||||||
|
toast.info('请输入手机号')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!isPhoneNumberValid.value) {
|
||||||
|
toast.info('手机号格式不正确')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送验证码请求
|
||||||
|
getCode({
|
||||||
|
mobile: form.value.mobile,
|
||||||
|
actionType: 'agentApply',
|
||||||
|
}).then((res) => {
|
||||||
|
if (res.code === 200) {
|
||||||
|
toast.success('验证码已发送')
|
||||||
|
// 开始倒计时
|
||||||
|
startCountdown()
|
||||||
|
} else {
|
||||||
|
toast.error(res.msg || '发送失败')
|
||||||
|
}
|
||||||
|
}).catch((err) => {
|
||||||
|
toast.error('网络错误')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 开始倒计时
|
||||||
|
const startCountdown = () => {
|
||||||
|
countdown.value = 60
|
||||||
|
if (timer) clearInterval(timer)
|
||||||
|
|
||||||
|
timer = setInterval(() => {
|
||||||
|
countdown.value--
|
||||||
|
if (countdown.value <= 0) {
|
||||||
|
clearInterval(timer)
|
||||||
|
timer = null
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 组件卸载时清除计时器
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (timer) {
|
||||||
|
clearInterval(timer)
|
||||||
|
timer = null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 提交表单 - 不再使用formRef验证
|
||||||
|
const submitForm = () => {
|
||||||
|
try {
|
||||||
|
// 检查区域是否已选择
|
||||||
|
if (!Array.isArray(region.value) || region.value.length < 3) {
|
||||||
|
console.log('区域选择不完整:', region.value)
|
||||||
|
toast.info('请选择完整的代理区域')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!regionText.value) {
|
||||||
|
toast.info('请选择代理区域')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查手机号
|
||||||
|
if (!form.value.mobile) {
|
||||||
|
console.log('手机号为空')
|
||||||
|
toast.info('请输入手机号')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isPhoneNumberValid.value) {
|
||||||
|
console.log('手机号格式不正确:', form.value.mobile)
|
||||||
|
toast.info('手机号格式不正确')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查验证码
|
||||||
|
if (!form.value.code) {
|
||||||
|
console.log('验证码为空')
|
||||||
|
toast.info('请输入验证码')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查用户是否同意协议
|
||||||
|
if (!isAgreed.value) {
|
||||||
|
toast.info('请阅读并同意相关协议')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 所有验证通过,构建表单数据
|
||||||
|
const formData = {
|
||||||
|
...form.value,
|
||||||
|
region: regionText.value,
|
||||||
|
}
|
||||||
|
console.log('提交的表单数据:', formData)
|
||||||
|
|
||||||
|
// 提交完整数据
|
||||||
|
emit('submit', formData)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('提交表单过程中出现异常:', error)
|
||||||
|
toast.error('系统错误,请稍后重试')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.verify-btn {
|
||||||
|
height: 32px;
|
||||||
|
padding: 0 12px;
|
||||||
|
background-color: #3b82f6;
|
||||||
|
color: white;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.verify-btn[disabled] {
|
||||||
|
background-color: #a0aec0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
212
src/components/GzhQrcode.vue
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref, defineProps, defineEmits, computed } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: 'example', // 'example' | 'withdraw'
|
||||||
|
validator: (value) => ['example', 'withdraw'].includes(value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['close'])
|
||||||
|
|
||||||
|
// 根据类型计算显示内容
|
||||||
|
const modalConfig = computed(() => {
|
||||||
|
if (props.type === 'withdraw') {
|
||||||
|
return {
|
||||||
|
title: '提现说明',
|
||||||
|
highlight: '点击二维码全屏查看',
|
||||||
|
instruction: '全屏后长按识别关注公众号',
|
||||||
|
description: '提现功能请在公众号内操作'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认是查看示例报告
|
||||||
|
return {
|
||||||
|
title: '关注公众号',
|
||||||
|
highlight: '点击二维码全屏查看',
|
||||||
|
instruction: '全屏后长按识别关注公众号',
|
||||||
|
description: '关注公众号查看示例报告'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleClose() {
|
||||||
|
emit('close')
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleMaskClick() {
|
||||||
|
handleClose()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预览图片
|
||||||
|
function previewImage() {
|
||||||
|
uni.previewImage({
|
||||||
|
urls: ['/static/image/gzh_qrcode.jpg'],
|
||||||
|
current: '/static/image/gzh_qrcode.jpg'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view v-if="visible" class="gzh-qrcode-modal">
|
||||||
|
<view class="modal-mask" @click="handleMaskClick" />
|
||||||
|
<view class="modal-content">
|
||||||
|
<view class="modal-header">
|
||||||
|
<view class="modal-title">{{ modalConfig.title }}</view>
|
||||||
|
<view class="close-btn" @click="handleClose">
|
||||||
|
<text class="close-icon">×</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="qrcode-container">
|
||||||
|
<image
|
||||||
|
class="qrcode-image"
|
||||||
|
src="/static/image/gzh_qrcode.jpg"
|
||||||
|
mode="aspectFit"
|
||||||
|
@click="previewImage"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="modal-message">
|
||||||
|
<text class="highlight">{{ modalConfig.highlight }}</text>
|
||||||
|
<text class="instruction">{{ modalConfig.instruction }}</text>
|
||||||
|
<text class="description">{{ modalConfig.description }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.gzh-qrcode-modal {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-mask {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 12px;
|
||||||
|
width: 300px;
|
||||||
|
max-width: 85%;
|
||||||
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
|
||||||
|
z-index: 1001;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header {
|
||||||
|
position: relative;
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-btn {
|
||||||
|
position: absolute;
|
||||||
|
right: 16px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-icon {
|
||||||
|
font-size: 20px;
|
||||||
|
color: #999;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qrcode-container {
|
||||||
|
padding: 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qrcode-image {
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-message {
|
||||||
|
padding: 0 20px 24px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-message text {
|
||||||
|
display: block;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight {
|
||||||
|
color: #007aff !important;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* 动画效果 */
|
||||||
|
.gzh-qrcode-modal {
|
||||||
|
animation: fadeIn 0.3s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
animation: slideIn 0.3s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideIn {
|
||||||
|
from {
|
||||||
|
transform: scale(0.8);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: scale(1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
159
src/components/Payment.vue
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
<template>
|
||||||
|
<wd-popup v-model="show" position="bottom" z-index="3000" custom-style="height: 50%; display: flex; flex-direction: column; justify-content: space-between; padding: 24px;">
|
||||||
|
<view class="text-center">
|
||||||
|
<text class="text-lg font-bold block">支付</text>
|
||||||
|
</view>
|
||||||
|
<view class="text-center">
|
||||||
|
<text class="font-bold text-xl block">{{ data.product_name }}</text>
|
||||||
|
<view class="text-3xl text-red-500 font-bold">
|
||||||
|
<!-- 显示原价和折扣价格 -->
|
||||||
|
<view v-if="discountPrice" class="line-through text-gray-500 mt-4" :class="{ 'text-2xl': discountPrice }">
|
||||||
|
¥ {{ data.sell_price }}
|
||||||
|
</view>
|
||||||
|
<view>¥ {{ discountPrice ? (data.sell_price * 0.2).toFixed(2) : data.sell_price }}</view>
|
||||||
|
</view>
|
||||||
|
<!-- 仅在折扣时显示活动说明 -->
|
||||||
|
<text v-if="discountPrice" class="text-sm text-red-500 mt-1 block">活动价:2折优惠</text>
|
||||||
|
</view>
|
||||||
|
<!-- 支付方式选择 -->
|
||||||
|
<view class="">
|
||||||
|
<wd-cell-group>
|
||||||
|
<wd-cell clickable>
|
||||||
|
<template #title>
|
||||||
|
<text class="text-lg font-medium">微信支付</text>
|
||||||
|
</template>
|
||||||
|
<template #right-icon>
|
||||||
|
<view class="w-5 h-5 rounded-full border-2 border-green-500 bg-green-500 flex items-center justify-center">
|
||||||
|
<view class="w-2 h-2 bg-white rounded-full"></view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</wd-cell>
|
||||||
|
</wd-cell-group>
|
||||||
|
</view>
|
||||||
|
<!-- 确认按钮 -->
|
||||||
|
<view class="">
|
||||||
|
<view @click="getPayment" class="w-full py-4 bg-green-500 text-center rounded-full shadow-lg active:bg-green-600 transition-colors">
|
||||||
|
<text class="text-white text-lg font-medium">确认支付</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</wd-popup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
import { payment } from '@/api/apis.js'
|
||||||
|
import { useToast } from 'wot-design-uni'
|
||||||
|
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const show = defineModel()
|
||||||
|
const orderNo = ref('')
|
||||||
|
const discountPrice = ref(false) // 是否应用折扣
|
||||||
|
|
||||||
|
async function getPayment() {
|
||||||
|
try {
|
||||||
|
const res = await payment({
|
||||||
|
id: props.id,
|
||||||
|
pay_method: 'wechat',
|
||||||
|
pay_type: props.type,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (res.data) {
|
||||||
|
orderNo.value = res.data.order_no
|
||||||
|
|
||||||
|
// 微信支付
|
||||||
|
const paymentData = res.data.prepay_data || res.data
|
||||||
|
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
uni.requestPayment({
|
||||||
|
provider: 'wxpay',
|
||||||
|
timeStamp: paymentData.timeStamp || paymentData.timestamp,
|
||||||
|
nonceStr: paymentData.nonceStr || paymentData.noncestr,
|
||||||
|
package: paymentData.package,
|
||||||
|
signType: paymentData.signType || 'MD5',
|
||||||
|
paySign: paymentData.paySign || paymentData.sign,
|
||||||
|
success: (result) => {
|
||||||
|
toast.success('支付成功')
|
||||||
|
handlePaymentSuccess()
|
||||||
|
},
|
||||||
|
fail: (error) => {
|
||||||
|
toast.error('支付失败')
|
||||||
|
console.error('支付失败:', error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
uni.requestPayment({
|
||||||
|
provider: 'wxpay',
|
||||||
|
orderInfo: {
|
||||||
|
appid: paymentData.appid,
|
||||||
|
noncestr: paymentData.nonceStr || paymentData.noncestr,
|
||||||
|
package: paymentData.package,
|
||||||
|
partnerid: paymentData.partnerid,
|
||||||
|
prepayid: paymentData.prepayid,
|
||||||
|
timestamp: paymentData.timeStamp || paymentData.timestamp,
|
||||||
|
sign: paymentData.paySign || paymentData.sign
|
||||||
|
},
|
||||||
|
success: (result) => {
|
||||||
|
toast.success('支付成功')
|
||||||
|
handlePaymentSuccess()
|
||||||
|
},
|
||||||
|
fail: (error) => {
|
||||||
|
toast.error('支付失败')
|
||||||
|
console.error('支付失败:', error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef H5
|
||||||
|
if (typeof WeixinJSBridge !== 'undefined') {
|
||||||
|
WeixinJSBridge.invoke('getBrandWCPayRequest', paymentData, function (result) {
|
||||||
|
if (result.err_msg === 'get_brand_wcpay_request:ok') {
|
||||||
|
toast.success('支付成功')
|
||||||
|
handlePaymentSuccess()
|
||||||
|
} else {
|
||||||
|
toast.error('支付失败')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
toast.error('当前环境不支持微信支付')
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
} else {
|
||||||
|
toast.error('获取支付信息失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('支付请求失败:', error)
|
||||||
|
toast.error('支付请求失败')
|
||||||
|
}
|
||||||
|
|
||||||
|
show.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePaymentSuccess() {
|
||||||
|
// 支付成功后的处理
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.switchTab({
|
||||||
|
url: `/pages/me`
|
||||||
|
})
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
179
src/components/PriceInputPopup.vue
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
<template>
|
||||||
|
<wd-popup v-model="show" position="bottom" round close-on-click-modal destroy-on-close>
|
||||||
|
<div class="min-h-[500px] bg-gray-50 text-gray-600">
|
||||||
|
<div class="h-10 bg-white flex items-center justify-center font-semibold text-lg">设置客户查询价
|
||||||
|
</div>
|
||||||
|
<div class="card m-4">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div class="text-lg">
|
||||||
|
客户查询价 (元)</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="border-b border-gray-200">
|
||||||
|
<wd-input v-model="price" type="number" label="¥" label-width="28px" size="large"
|
||||||
|
:placeholder="`${productConfig.price_range_min} - ${productConfig.price_range_max}`"
|
||||||
|
@blur="onBlurPrice" custom-class="wd-input" />
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-between mt-2">
|
||||||
|
<div>推广收益为<span class="text-orange-500"> {{ promotionRevenue }} </span>元</div>
|
||||||
|
<div>我的成本为<span class="text-orange-500"> {{ costPrice }} </span>元</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card m-4">
|
||||||
|
<div class="text-lg mb-2">收益与成本说明</div>
|
||||||
|
|
||||||
|
<div>推广收益 = 客户查询价 - 我的成本</div>
|
||||||
|
<div>我的成本 = 提价成本 + 底价成本</div>
|
||||||
|
<div class="mt-1">提价成本:超过平台标准定价部分,平台会收取部分成本价</div>
|
||||||
|
<div class="">设定范围:<span class="text-orange-500">{{
|
||||||
|
productConfig.price_range_min }}</span>元 - <span class="text-orange-500">{{
|
||||||
|
productConfig.price_range_max }}</span>元</div>
|
||||||
|
</div>
|
||||||
|
<div class="px-4 pb-4">
|
||||||
|
<wd-button class="w-full" round type="primary" size="large" @click="onConfirm">确认</wd-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</wd-popup>
|
||||||
|
<wd-toast />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, watch, toRefs } from 'vue'
|
||||||
|
import { useToast } from 'wot-design-uni'
|
||||||
|
const props = defineProps({
|
||||||
|
defaultPrice: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
productConfig: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const { defaultPrice, productConfig } = toRefs(props)
|
||||||
|
const emit = defineEmits(["change"])
|
||||||
|
const show = defineModel("show")
|
||||||
|
const price = ref(null)
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
|
watch(show, () => {
|
||||||
|
price.value = defaultPrice.value
|
||||||
|
})
|
||||||
|
|
||||||
|
const costPrice = computed(() => {
|
||||||
|
if (!productConfig.value) return 0.00
|
||||||
|
// 平台定价成本
|
||||||
|
let platformPricing = 0
|
||||||
|
platformPricing += productConfig.value.cost_price
|
||||||
|
if (price.value > productConfig.value.p_pricing_standard) {
|
||||||
|
platformPricing += (price.value - productConfig.value.p_pricing_standard) * productConfig.value.p_overpricing_ratio
|
||||||
|
}
|
||||||
|
|
||||||
|
if (productConfig.value.a_pricing_standard > platformPricing && productConfig.value.a_pricing_end > platformPricing && productConfig.value.a_overpricing_ratio > 0) {
|
||||||
|
if (price.value > productConfig.value.a_pricing_standard) {
|
||||||
|
if (price.value > productConfig.value.a_pricing_end) {
|
||||||
|
platformPricing += (productConfig.value.a_pricing_end - productConfig.value.a_pricing_standard) * productConfig.value.a_overpricing_ratio
|
||||||
|
} else {
|
||||||
|
platformPricing += (price.value - productConfig.value.a_pricing_standard) * productConfig.value.a_overpricing_ratio
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return safeTruncate(platformPricing)
|
||||||
|
})
|
||||||
|
|
||||||
|
const promotionRevenue = computed(() => {
|
||||||
|
return safeTruncate(price.value - costPrice.value)
|
||||||
|
});
|
||||||
|
|
||||||
|
// 价格校验与修正逻辑
|
||||||
|
const validatePrice = (currentPrice) => {
|
||||||
|
const min = productConfig.value.price_range_min;
|
||||||
|
const max = productConfig.value.price_range_max;
|
||||||
|
let newPrice = Number(currentPrice);
|
||||||
|
let message = '';
|
||||||
|
|
||||||
|
// 处理无效输入
|
||||||
|
if (isNaN(newPrice)) {
|
||||||
|
newPrice = defaultPrice.value;
|
||||||
|
return { newPrice, message: '输入无效,请输入价格' };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理小数位数(兼容科学计数法)
|
||||||
|
try {
|
||||||
|
const priceString = newPrice.toString()
|
||||||
|
const [_, decimalPart = ""] = priceString.split('.');
|
||||||
|
console.log(priceString, decimalPart)
|
||||||
|
// 当小数位数超过2位时处理
|
||||||
|
if (decimalPart.length > 2) {
|
||||||
|
newPrice = parseFloat(safeTruncate(newPrice));
|
||||||
|
message = '价格已自动格式化为两位小数';
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('价格格式化异常:', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 范围校验(基于可能格式化后的值)
|
||||||
|
if (newPrice < min) {
|
||||||
|
message = `价格不能低于 ${min} 元`;
|
||||||
|
newPrice = min;
|
||||||
|
} else if (newPrice > max) {
|
||||||
|
message = `价格不能高于 ${max} 元`;
|
||||||
|
newPrice = max;
|
||||||
|
}
|
||||||
|
console.log(newPrice, message)
|
||||||
|
return { newPrice, message };
|
||||||
|
}
|
||||||
|
function safeTruncate(num, decimals = 2) {
|
||||||
|
if (isNaN(num) || !isFinite(num)) return "0.00";
|
||||||
|
|
||||||
|
const factor = 10 ** decimals;
|
||||||
|
const scaled = Math.trunc(num * factor);
|
||||||
|
const truncated = scaled / factor;
|
||||||
|
|
||||||
|
return truncated.toFixed(decimals);
|
||||||
|
}
|
||||||
|
const isManualConfirm = ref(false)
|
||||||
|
const onConfirm = () => {
|
||||||
|
if (isManualConfirm.value) return
|
||||||
|
const { newPrice, message } = validatePrice(price.value)
|
||||||
|
if (message) {
|
||||||
|
price.value = newPrice
|
||||||
|
toast.show(message)
|
||||||
|
} else {
|
||||||
|
emit("change", price.value)
|
||||||
|
show.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const onBlurPrice = () => {
|
||||||
|
const { newPrice, message } = validatePrice(price.value)
|
||||||
|
if (message) {
|
||||||
|
isManualConfirm.value = true
|
||||||
|
price.value = newPrice
|
||||||
|
toast.show(message)
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
isManualConfirm.value = false
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.wd-input {
|
||||||
|
display: flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
justify-content: center !important;
|
||||||
|
:deep(.wd-input__label-inner) {
|
||||||
|
font-size: 24px !important; /* 增大label字体 */
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.wd-input__inner) {
|
||||||
|
font-size: 24px !important; /* 增大输入框内字体 */
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.wd-input) {
|
||||||
|
height: auto !important; /* 确保高度自适应 */
|
||||||
|
padding: 8px 0 !important; /* 增加垂直内边距 */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
266
src/components/QRcode.vue
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
<template>
|
||||||
|
<wd-popup v-model="show" position="bottom" round>
|
||||||
|
<view class="max-h-[calc(100vh-100px)] m-4">
|
||||||
|
<view class="p-4 flex justify-center">
|
||||||
|
<!-- 直接显示图片 -->
|
||||||
|
<view class="max-h-[70vh] rounded-xl overflow-hidden">
|
||||||
|
<image
|
||||||
|
:src="posterImageUrl"
|
||||||
|
class="rounded-xl shadow poster-image"
|
||||||
|
:style="{ width: imageWidth + 'px', height: imageHeight + 'px' }"
|
||||||
|
mode="aspectFit"
|
||||||
|
@load="onImageLoad"
|
||||||
|
@error="onImageError"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="divider">分享到好友</view>
|
||||||
|
|
||||||
|
<view class="flex items-center justify-around">
|
||||||
|
<view class="flex flex-col items-center justify-center" @click="savePoster">
|
||||||
|
<image src="/static/image/icon_share_img.svg" class="w-10 h-10 rounded-full" />
|
||||||
|
<view class="text-center mt-1 text-gray-600 text-xs">保存图片</view>
|
||||||
|
</view>
|
||||||
|
<!-- <view class="flex flex-col items-center justify-center" @click="shareToWechatFriend">
|
||||||
|
<image src="/static/image/icon_share_wechat.svg" class="w-10 h-10 rounded-full" />
|
||||||
|
<view class="text-center mt-1 text-gray-600 text-xs">微信好友</view>
|
||||||
|
</view>
|
||||||
|
<view class="flex flex-col items-center justify-center" @click="shareToWechatMoments">
|
||||||
|
<image src="/static/image/icon_share_friends.svg" class="w-10 h-10 rounded-full" />
|
||||||
|
<view class="text-center mt-1 text-gray-600 text-xs">微信朋友圈</view>
|
||||||
|
</view> -->
|
||||||
|
<view class="flex flex-col items-center justify-center" @click="copyUrl">
|
||||||
|
<image src="/static/image/icon_share_url.svg" class="w-10 h-10 rounded-full" />
|
||||||
|
<view class="text-center mt-1 text-gray-600 text-xs">复制链接</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</wd-popup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, toRefs } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
linkIdentifier: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
mode: {
|
||||||
|
type: String,
|
||||||
|
default: "promote", // 例如 "promote" | "invitation"
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { linkIdentifier, mode } = toRefs(props);
|
||||||
|
const show = defineModel('show');
|
||||||
|
|
||||||
|
// 图片尺寸变量
|
||||||
|
const imageWidth = ref(300);
|
||||||
|
const imageHeight = ref(500);
|
||||||
|
const imageLoaded = ref(false);
|
||||||
|
|
||||||
|
const url = computed(() => {
|
||||||
|
// 在uniapp中获取当前站点的基础URL
|
||||||
|
const baseUrl = import.meta.env.VITE_APP_BASE_URL || 'https://www.tianyuandb.com';
|
||||||
|
return mode.value === "promote"
|
||||||
|
? `${baseUrl}/agent/promotionInquire/`
|
||||||
|
: `${baseUrl}/agent/invitationAgentApply/`;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 生成链接URL
|
||||||
|
const generalUrl = () => {
|
||||||
|
return url.value + encodeURIComponent(linkIdentifier.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 计算二维码图片URL - 直接构造带参数的接口URL
|
||||||
|
const posterImageUrl = computed(() => {
|
||||||
|
const baseUrl = import.meta.env.VITE_APP_BASE_URL || 'https://www.tianyuandb.com';
|
||||||
|
const qrcodeUrl = generalUrl();
|
||||||
|
// 构造带参数的图片URL
|
||||||
|
let url = `${baseUrl}/api/v1/agent/promotion/qrcode?qrcode_type=${mode.value}&qrcode_url=${encodeURIComponent(qrcodeUrl)}`;
|
||||||
|
console.log('[DEBUG] 二维码图片URL:', url);
|
||||||
|
return url;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 计算适合屏幕的图片尺寸
|
||||||
|
const calculateImageSize = (imgWidth, imgHeight) => {
|
||||||
|
// 获取系统信息
|
||||||
|
const sysInfo = uni.getSystemInfoSync();
|
||||||
|
|
||||||
|
// 计算可用高度(屏幕高度的70%)
|
||||||
|
const maxHeight = sysInfo.windowHeight * 0.6;
|
||||||
|
// 控制最大宽度(屏幕宽度的80%)
|
||||||
|
const maxWidth = sysInfo.windowWidth * 0.8;
|
||||||
|
|
||||||
|
// 初始尺寸
|
||||||
|
let width = 300;
|
||||||
|
let height = width * (imgHeight / imgWidth);
|
||||||
|
|
||||||
|
// 如果高度超出限制,按高度缩放
|
||||||
|
if (height > maxHeight) {
|
||||||
|
height = maxHeight;
|
||||||
|
width = height * (imgWidth / imgHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果宽度超出限制,按宽度缩放
|
||||||
|
if (width > maxWidth) {
|
||||||
|
width = maxWidth;
|
||||||
|
height = width * (imgHeight / imgWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回最终计算的尺寸
|
||||||
|
return {
|
||||||
|
width: Math.floor(width),
|
||||||
|
height: Math.floor(height)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// 图片加载成功处理
|
||||||
|
const onImageLoad = (e) => {
|
||||||
|
console.log('[DEBUG] 图片加载成功:', e);
|
||||||
|
|
||||||
|
// 获取图片实际尺寸
|
||||||
|
uni.getImageInfo({
|
||||||
|
src: posterImageUrl.value,
|
||||||
|
success: (res) => {
|
||||||
|
console.log('[DEBUG] 获取图片信息成功:', res);
|
||||||
|
|
||||||
|
// 计算适合屏幕的尺寸
|
||||||
|
const size = calculateImageSize(res.width, res.height);
|
||||||
|
imageWidth.value = size.width;
|
||||||
|
imageHeight.value = size.height;
|
||||||
|
|
||||||
|
imageLoaded.value = true;
|
||||||
|
|
||||||
|
console.log('[DEBUG] 图片尺寸设置完成:', size);
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('[DEBUG] 获取图片信息失败:', err);
|
||||||
|
// 设置默认尺寸
|
||||||
|
imageLoaded.value = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 图片加载失败处理
|
||||||
|
const onImageError = (e) => {
|
||||||
|
console.error('[DEBUG] 图片加载失败:', e);
|
||||||
|
uni.showToast({
|
||||||
|
title: '图片加载失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 保存海报图片
|
||||||
|
const savePoster = () => {
|
||||||
|
// 显示加载状态
|
||||||
|
uni.showLoading({
|
||||||
|
title: '正在保存...'
|
||||||
|
});
|
||||||
|
|
||||||
|
// 先下载网络图片到本地临时目录
|
||||||
|
uni.downloadFile({
|
||||||
|
url: posterImageUrl.value,
|
||||||
|
success: (downloadRes) => {
|
||||||
|
console.log('[DEBUG] 图片下载成功:', downloadRes);
|
||||||
|
|
||||||
|
// 使用下载后的临时文件路径保存到相册
|
||||||
|
uni.saveImageToPhotosAlbum({
|
||||||
|
filePath: downloadRes.tempFilePath,
|
||||||
|
success: () => {
|
||||||
|
uni.hideLoading();
|
||||||
|
uni.showToast({
|
||||||
|
title: '保存成功',
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
uni.hideLoading();
|
||||||
|
console.error('保存到相册失败:', err);
|
||||||
|
// 如果是用户拒绝授权导致的
|
||||||
|
if (err.errMsg && err.errMsg.includes('auth deny')) {
|
||||||
|
uni.showModal({
|
||||||
|
title: '提示',
|
||||||
|
content: '需要您授权保存图片到相册',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
uni.openSetting();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: '保存失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
uni.hideLoading();
|
||||||
|
console.error('图片下载失败:', err);
|
||||||
|
uni.showToast({
|
||||||
|
title: '图片下载失败',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const copyUrl = () => {
|
||||||
|
uni.setClipboardData({
|
||||||
|
data: generalUrl(),
|
||||||
|
success: () => {
|
||||||
|
uni.showToast({
|
||||||
|
title: '链接已复制!',
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.divider {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin: 16px 0;
|
||||||
|
color: #969799;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
&::before,
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
height: 1px;
|
||||||
|
flex: 1;
|
||||||
|
background-color: #ebedf0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.poster-image {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
36
src/components/VipBanner.vue
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<template>
|
||||||
|
<view class="card mb-4 relative overflow-hidden" @click="goToVip">
|
||||||
|
<view class="absolute inset-0 bg-gradient-to-r from-yellow-400 to-yellow-300 opacity-40"></view>
|
||||||
|
<view class="p-2 relative z-10">
|
||||||
|
<view class="flex justify-between items-center">
|
||||||
|
<view>
|
||||||
|
<view class="text-lg font-bold text-yellow-800">会员专享特权</view>
|
||||||
|
<view class="text-sm text-yellow-700 mt-1">升级VIP获得更多收益</view>
|
||||||
|
</view>
|
||||||
|
<view class="bg-yellow-500 px-3 py-1 rounded-full text-white text-sm shadow-sm">
|
||||||
|
立即查看
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- 装饰元素 -->
|
||||||
|
<view class="absolute -right-4 -top-4 w-16 h-16 bg-yellow-200 rounded-full opacity-60"></view>
|
||||||
|
<view class="absolute right-5 -bottom-4 w-12 h-12 bg-yellow-100 rounded-full opacity-40"></view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
// 跳转到VIP页面
|
||||||
|
const goToVip = () => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/agentVipApply'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.card {
|
||||||
|
border-radius: 12px;
|
||||||
|
background-color: white;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
340
src/composables/useHotUpdate.js
Normal file
@@ -0,0 +1,340 @@
|
|||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WGT热更新处理
|
||||||
|
* 静默下载更新,无需重启应用
|
||||||
|
*/
|
||||||
|
export function useHotUpdate() {
|
||||||
|
const updating = ref(false)
|
||||||
|
const hasNewVersion = ref(false)
|
||||||
|
const currentVersion = ref('')
|
||||||
|
const latestVersion = ref('')
|
||||||
|
const downloadProgress = ref(0)
|
||||||
|
const serverWgtUrl = ref('') // 保存服务器更新包地址
|
||||||
|
|
||||||
|
// 测试模式相关状态
|
||||||
|
const isTestMode = ref(false)
|
||||||
|
const mockWgtUrl = 'https://example.com/mock-update.wgt' // 模拟更新包地址
|
||||||
|
|
||||||
|
// 获取当前应用版本
|
||||||
|
const getCurrentVersion = () => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
plus.runtime.getProperty(plus.runtime.appid, function(inf) {
|
||||||
|
const wgtVer = inf.version
|
||||||
|
currentVersion.value = wgtVer
|
||||||
|
resolve(wgtVer)
|
||||||
|
})
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifndef APP-PLUS
|
||||||
|
const defaultVersion = '1.0.0' // 非APP环境下的默认值
|
||||||
|
currentVersion.value = defaultVersion
|
||||||
|
resolve(defaultVersion)
|
||||||
|
// #endif
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 只检查版本,不自动更新
|
||||||
|
* 适用于手动触发的更新检查
|
||||||
|
*/
|
||||||
|
const checkVersionOnly = async () => {
|
||||||
|
try {
|
||||||
|
// 测试模式直接返回有新版本
|
||||||
|
if (isTestMode.value) {
|
||||||
|
await getCurrentVersion()
|
||||||
|
latestVersion.value = incrementVersion(currentVersion.value)
|
||||||
|
serverWgtUrl.value = mockWgtUrl
|
||||||
|
hasNewVersion.value = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前版本
|
||||||
|
await getCurrentVersion()
|
||||||
|
|
||||||
|
// 替换为使用回调函数的方式
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
uni.request({
|
||||||
|
url: import.meta.env.VITE_APP_BASE_URL + '/api/v1/app/version',
|
||||||
|
method: 'GET',
|
||||||
|
success: (res) => {
|
||||||
|
if (!res || res.statusCode !== 200 || !res.data || res.data.code !== 200 || !res.data.data) {
|
||||||
|
resolve(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const serverInfo = res.data.data
|
||||||
|
latestVersion.value = serverInfo.version
|
||||||
|
|
||||||
|
// 保存服务器wgt地址,以便后续手动更新使用
|
||||||
|
if (serverInfo.wgtUrl) {
|
||||||
|
serverWgtUrl.value = serverInfo.wgtUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
// 比较版本号,检查是否有新版本
|
||||||
|
hasNewVersion.value = compareVersion(serverInfo.version, currentVersion.value) > 0
|
||||||
|
|
||||||
|
resolve(hasNewVersion.value)
|
||||||
|
},
|
||||||
|
fail: (error) => {
|
||||||
|
console.error('检查版本失败', error)
|
||||||
|
resolve(false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('检查版本失败', error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动增加版本号,用于测试
|
||||||
|
*/
|
||||||
|
const incrementVersion = (version) => {
|
||||||
|
const parts = version.split('.')
|
||||||
|
const lastPart = parseInt(parts[parts.length - 1]) + 1
|
||||||
|
parts[parts.length - 1] = lastPart.toString()
|
||||||
|
return parts.join('.')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启用或禁用测试模式
|
||||||
|
*/
|
||||||
|
const toggleTestMode = (enabled = true) => {
|
||||||
|
isTestMode.value = enabled
|
||||||
|
console.log('测试模式已' + (enabled ? '启用' : '禁用'))
|
||||||
|
return isTestMode.value
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查更新并自动静默更新(App.vue使用)
|
||||||
|
const checkUpdate = async () => {
|
||||||
|
try {
|
||||||
|
// 如果是测试模式,不进行实际的静默更新
|
||||||
|
if (isTestMode.value) {
|
||||||
|
await getCurrentVersion()
|
||||||
|
latestVersion.value = incrementVersion(currentVersion.value)
|
||||||
|
hasNewVersion.value = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前版本
|
||||||
|
await getCurrentVersion()
|
||||||
|
|
||||||
|
// 替换为使用回调函数的方式
|
||||||
|
uni.request({
|
||||||
|
url: import.meta.env.VITE_APP_BASE_URL + '/api/v1/app/version',
|
||||||
|
method: 'GET',
|
||||||
|
success: (res) => {
|
||||||
|
if (!res || res.statusCode !== 200 || !res.data || res.data.code !== 200 || !res.data.data) return
|
||||||
|
console.log('update version res', res)
|
||||||
|
const serverInfo = res.data.data
|
||||||
|
latestVersion.value = serverInfo.version
|
||||||
|
|
||||||
|
// 比较版本号,检查是否有新版本
|
||||||
|
if (compareVersion(serverInfo.version, currentVersion.value) > 0) {
|
||||||
|
hasNewVersion.value = true
|
||||||
|
// 如果有wgt下载地址,执行静默更新
|
||||||
|
if (serverInfo.wgtUrl) {
|
||||||
|
serverWgtUrl.value = serverInfo.wgtUrl
|
||||||
|
silentUpdate(serverInfo.wgtUrl)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hasNewVersion.value = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: (error) => {
|
||||||
|
console.error('检查更新失败', error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
console.error('检查更新失败', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 比较版本号 (v1 > v2 返回1,v1 < v2 返回-1,相等返回0)
|
||||||
|
const compareVersion = (v1, v2) => {
|
||||||
|
const v1Parts = v1.split('.').map(Number)
|
||||||
|
const v2Parts = v2.split('.').map(Number)
|
||||||
|
|
||||||
|
for (let i = 0; i < Math.max(v1Parts.length, v2Parts.length); i++) {
|
||||||
|
const v1Part = v1Parts[i] || 0
|
||||||
|
const v2Part = v2Parts[i] || 0
|
||||||
|
|
||||||
|
if (v1Part > v2Part) return 1
|
||||||
|
if (v1Part < v2Part) return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 静默下载并安装更新
|
||||||
|
const silentUpdate = (wgtUrl) => {
|
||||||
|
if (updating.value) return Promise.reject('更新已在进行中')
|
||||||
|
|
||||||
|
updating.value = true
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
const dtask = plus.downloader.createDownload(wgtUrl, {
|
||||||
|
filename: '_doc/update/'
|
||||||
|
}, (download, status) => {
|
||||||
|
if (status === 200) {
|
||||||
|
installing(download.filename)
|
||||||
|
.then(() => {
|
||||||
|
updating.value = false
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
updating.value = false
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
updating.value = false
|
||||||
|
reject('下载更新包失败')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
dtask.start()
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifndef APP-PLUS
|
||||||
|
updating.value = false
|
||||||
|
resolve() // 非APP环境下直接返回成功
|
||||||
|
// #endif
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 安装wgt包
|
||||||
|
const installing = (filePath) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
plus.runtime.install(filePath, {
|
||||||
|
force: false // 不重启应用
|
||||||
|
}, () => {
|
||||||
|
console.log('安装wgt成功')
|
||||||
|
resolve()
|
||||||
|
// 删除下载的安装包
|
||||||
|
plus.io.resolveLocalFileSystemURL(filePath, (entry) => {
|
||||||
|
entry.remove()
|
||||||
|
})
|
||||||
|
}, (error) => {
|
||||||
|
console.error('安装wgt失败', error)
|
||||||
|
reject(error)
|
||||||
|
})
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifndef APP-PLUS
|
||||||
|
resolve() // 非APP环境下直接返回成功
|
||||||
|
// #endif
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手动更新,提供进度回调
|
||||||
|
* @param {string} wgtUrl 更新包下载地址
|
||||||
|
* @returns {Promise} 返回更新结果Promise
|
||||||
|
*/
|
||||||
|
const manualUpdate = (wgtUrl) => {
|
||||||
|
if (updating.value) return Promise.reject('更新已在进行中')
|
||||||
|
|
||||||
|
updating.value = true
|
||||||
|
downloadProgress.value = 0
|
||||||
|
|
||||||
|
// 如果是测试模式,模拟下载进度而不实际下载
|
||||||
|
if (isTestMode.value) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
let progress = 0
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
progress += 5
|
||||||
|
downloadProgress.value = progress
|
||||||
|
|
||||||
|
if (progress >= 100) {
|
||||||
|
clearInterval(interval)
|
||||||
|
setTimeout(() => {
|
||||||
|
updating.value = false
|
||||||
|
resolve()
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
}, 200)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
const dtask = plus.downloader.createDownload(wgtUrl, {
|
||||||
|
filename: '_doc/update/'
|
||||||
|
}, (download, status) => {
|
||||||
|
if (status === 200) {
|
||||||
|
installing(download.filename)
|
||||||
|
.then(() => {
|
||||||
|
updating.value = false
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
updating.value = false
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
updating.value = false
|
||||||
|
reject('下载更新包失败')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 监听下载进度
|
||||||
|
dtask.addEventListener('statechanged', (task, status) => {
|
||||||
|
switch (task.state) {
|
||||||
|
case 1: // 开始
|
||||||
|
console.log('开始下载更新...')
|
||||||
|
break
|
||||||
|
case 2: // 已连接到服务器
|
||||||
|
console.log('已连接到服务器...')
|
||||||
|
break
|
||||||
|
case 3: // 下载中
|
||||||
|
const totalSize = task.totalSize
|
||||||
|
const downloadedSize = task.downloadedSize
|
||||||
|
const progress = Math.round(downloadedSize / totalSize * 100) || 0
|
||||||
|
downloadProgress.value = progress
|
||||||
|
console.log(`下载进度: ${progress}%`)
|
||||||
|
break
|
||||||
|
case 4: // 下载完成
|
||||||
|
console.log('下载完成')
|
||||||
|
downloadProgress.value = 100
|
||||||
|
break
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
dtask.start()
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifndef APP-PLUS
|
||||||
|
// 模拟下载进度
|
||||||
|
let progress = 0
|
||||||
|
const timer = setInterval(() => {
|
||||||
|
progress += 5
|
||||||
|
downloadProgress.value = progress
|
||||||
|
if (progress >= 100) {
|
||||||
|
clearInterval(timer)
|
||||||
|
updating.value = false
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
}, 200)
|
||||||
|
// #endif
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
updating,
|
||||||
|
hasNewVersion,
|
||||||
|
currentVersion,
|
||||||
|
latestVersion,
|
||||||
|
downloadProgress,
|
||||||
|
serverWgtUrl,
|
||||||
|
isTestMode,
|
||||||
|
checkUpdate,
|
||||||
|
checkVersionOnly,
|
||||||
|
manualUpdate,
|
||||||
|
toggleTestMode
|
||||||
|
}
|
||||||
|
}
|
||||||
1
src/env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
38
src/hooks/useColPickerData.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// 可以将此代码放置于项目src/hooks/useColPickerData.ts中
|
||||||
|
import { useCascaderAreaData } from '@vant/area-data'
|
||||||
|
|
||||||
|
export type CascaderOption = {
|
||||||
|
text: string
|
||||||
|
value: string
|
||||||
|
children?: CascaderOption[]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用'@vant/area-data'作为数据源,构造ColPicker组件的数据
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function useColPickerData() {
|
||||||
|
// '@vant/area-data' 数据源
|
||||||
|
const colPickerData: CascaderOption[] = useCascaderAreaData()
|
||||||
|
|
||||||
|
// 根据code查找子节点,不传code则返回所有节点
|
||||||
|
function findChildrenByCode(data: CascaderOption[], code?: string): CascaderOption[] | null {
|
||||||
|
if (!code) {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
for (const item of data) {
|
||||||
|
if (item.value === code) {
|
||||||
|
return item.children || null
|
||||||
|
}
|
||||||
|
if (item.children) {
|
||||||
|
const childrenResult = findChildrenByCode(item.children, code)
|
||||||
|
if (childrenResult) {
|
||||||
|
return childrenResult
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return { colPickerData, findChildrenByCode }
|
||||||
|
}
|
||||||
103
src/layouts/home.vue
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
<script setup>
|
||||||
|
const tabbar = ref('index')
|
||||||
|
const menu = reactive([{ title: '首页', icon: 'home', name: 'index' }, { title: '资产', icon: 'money-circle', name: 'agent' }, { title: '我的', icon: 'user', name: 'me' }])
|
||||||
|
// 获取当前页面名称
|
||||||
|
const currentPage = ref('')
|
||||||
|
|
||||||
|
function updateCurrentPage() {
|
||||||
|
const pages = getCurrentPages()
|
||||||
|
if (pages.length > 0) {
|
||||||
|
const page = pages[pages.length - 1].route
|
||||||
|
currentPage.value = page.split('/').pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function tabChange({ value }) {
|
||||||
|
uni.switchTab({
|
||||||
|
url: `/pages/${value}`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onShow(() => {
|
||||||
|
updateCurrentPage()
|
||||||
|
tabbar.value = currentPage.value
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
uni.hideTabBar()
|
||||||
|
updateCurrentPage()
|
||||||
|
tabbar.value = currentPage.value
|
||||||
|
})
|
||||||
|
|
||||||
|
function toComplaint() {
|
||||||
|
// 直接使用plus.runtime.openURL在系统浏览器中打开链接
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
plus.runtime.openURL('https://work.weixin.qq.com/kfid/kfc5c19b2b93a5e73b9');
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef H5
|
||||||
|
window.location.href = 'https://work.weixin.qq.com/kfid/kfc5c19b2b93a5e73b9';
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
options: {
|
||||||
|
styleIsolation: 'shared',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view
|
||||||
|
class="safe-area-top min-h-screen from-blue-100 to-white bg-gradient-to-b min-h-screen flex flex-col flex-1 pb-16 box-border">
|
||||||
|
<slot />
|
||||||
|
</view>
|
||||||
|
<view>
|
||||||
|
<wd-tabbar v-model="tabbar" custom-class="qnc-tabbar" shape="round" safe-area-inset-bottom fixed
|
||||||
|
@change="tabChange">
|
||||||
|
<wd-tabbar-item v-for="(item, index) in menu" :key="index" :name="item.name" :title="item.title"
|
||||||
|
:icon="item.icon" />
|
||||||
|
</wd-tabbar>
|
||||||
|
</view>
|
||||||
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
|
<button v-if="currentPage !== 'ai'" open-type="contact"
|
||||||
|
class="fixed bottom-24 right-4 z-1000 flex items-center justify-center py-2 rounded-full bg-gradient-to-r from-[#ffb86c] to-[#ff6e6a] shadow-lg active:scale-95 transition-all border-none"
|
||||||
|
style="min-width: 72px;">
|
||||||
|
<view class="text-xs font-bold tracking-wide text-white text-center">投诉</view>
|
||||||
|
</button>
|
||||||
|
<!-- #endif -->
|
||||||
|
|
||||||
|
<!-- #ifndef MP-WEIXIN -->
|
||||||
|
<view v-if="currentPage !== 'ai'"
|
||||||
|
class="fixed bottom-24 right-4 z-1000 flex items-center justify-center py-2 rounded-full bg-gradient-to-r from-[#ffb86c] to-[#ff6e6a] shadow-lg active:scale-95 transition-all"
|
||||||
|
style="min-width: 72px;" @click="toComplaint">
|
||||||
|
<view class="text-xs font-bold tracking-wide text-white text-center">投诉</view>
|
||||||
|
</view>
|
||||||
|
<!-- #endif -->
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
:deep(.qnc-tabbar) {
|
||||||
|
bottom: 16px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 微信小程序客服按钮样式重置 */
|
||||||
|
/* #ifdef MP-WEIXIN */
|
||||||
|
button[open-type="contact"]::after {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button[open-type="contact"] {
|
||||||
|
margin: 0 !important;
|
||||||
|
line-height: inherit !important;
|
||||||
|
font-size: inherit !important;
|
||||||
|
display: flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
justify-content: center !important;
|
||||||
|
border: none !important;
|
||||||
|
border-radius: 9999px !important; /* rounded-full */
|
||||||
|
}
|
||||||
|
/* #endif */
|
||||||
|
</style>
|
||||||
31
src/layouts/login.vue
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<script setup>
|
||||||
|
function handleClickLeft() {
|
||||||
|
uni.reLaunch({
|
||||||
|
url: '/pages/index',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<!-- -->
|
||||||
|
<view class="h-screen bg-[#EBF1FD]">
|
||||||
|
<view class="login-layout min-h-full">
|
||||||
|
<wd-navbar
|
||||||
|
title="用户登录"
|
||||||
|
left-arrow
|
||||||
|
safe-area-inset-top
|
||||||
|
custom-style="background-color: transparent !important;"
|
||||||
|
@click-left="handleClickLeft"
|
||||||
|
/>
|
||||||
|
<slot />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.login-layout{
|
||||||
|
background: url("/static/image/login_bg.png") no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
35
src/layouts/page.vue
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<script setup>
|
||||||
|
import pagesJson from '@/pages.json'
|
||||||
|
|
||||||
|
const pagesConfig = pagesJson.pages
|
||||||
|
const title = ref('')
|
||||||
|
function getPageTitle() {
|
||||||
|
const pages = getCurrentPages()
|
||||||
|
const currentPage = pages[pages.length - 1] // 当前页面
|
||||||
|
const currentRoute = currentPage.route // 当前页面路径,例如 "pages/authorization"
|
||||||
|
|
||||||
|
// 根据路径查找 pages.json 中的配置
|
||||||
|
const currentPageConfig = pagesConfig.find(page => page.path === currentRoute)
|
||||||
|
console.log('currentPageConfig', currentPageConfig)
|
||||||
|
// 返回页面标题,如果未找到,则返回默认标题
|
||||||
|
return currentPageConfig?.title || ''
|
||||||
|
}
|
||||||
|
onLoad(() => {
|
||||||
|
title.value = getPageTitle()
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleClickLeft() {
|
||||||
|
uni.navigateBack()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<wd-navbar :title="title" left-text="返回" placeholder left-arrow safe-area-inset-top fixed @click-left="handleClickLeft" />
|
||||||
|
<view class="box-border min-h-screen">
|
||||||
|
<slot />
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
16
src/main.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { createSSRApp } from 'vue'
|
||||||
|
import App from './App.vue'
|
||||||
|
import 'uno.css'
|
||||||
|
import '@/app.scss'
|
||||||
|
import { setupRouterGuard } from '@/utils/routerGuard'
|
||||||
|
|
||||||
|
export function createApp() {
|
||||||
|
const app = createSSRApp(App)
|
||||||
|
|
||||||
|
// 初始化路由守卫
|
||||||
|
setupRouterGuard()
|
||||||
|
|
||||||
|
return {
|
||||||
|
app
|
||||||
|
}
|
||||||
|
}
|
||||||
120
src/manifest.json
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
{
|
||||||
|
"name": "天远数据",
|
||||||
|
"appid": "__UNI__CC3DA09",
|
||||||
|
"description": "",
|
||||||
|
"versionName": "1.0.0",
|
||||||
|
"versionCode": "107",
|
||||||
|
"transformPx": false,
|
||||||
|
"app-plus": {
|
||||||
|
"usingComponents": true,
|
||||||
|
"nvueStyleCompiler": "uni-app",
|
||||||
|
"compilerVersion": 3,
|
||||||
|
"splashscreen": {
|
||||||
|
"alwaysShowBeforeRender": true,
|
||||||
|
"waiting": true,
|
||||||
|
"autoclose": true,
|
||||||
|
"delay": 0
|
||||||
|
},
|
||||||
|
"modules": {
|
||||||
|
"Share": {},
|
||||||
|
"Camera": {},
|
||||||
|
"PhotoLibrary": {}
|
||||||
|
},
|
||||||
|
"distribute": {
|
||||||
|
"android": {
|
||||||
|
"permissions": [
|
||||||
|
"<uses-permission android:name=\"android.permission.INTERNET\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
|
||||||
|
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>"
|
||||||
|
],
|
||||||
|
"package": "com.quannengcha.app"
|
||||||
|
},
|
||||||
|
"ios": {
|
||||||
|
"privacyDescription": {
|
||||||
|
"NSLocalNetworkUsageDescription": "需要访问您的网络来提供更好的服务",
|
||||||
|
"NSPhotoLibraryAddUsageDescription": "此应用需要访问您的相册以保存图片"
|
||||||
|
},
|
||||||
|
"idfa": false,
|
||||||
|
"bundleIdentifier": "com.allinone.check"
|
||||||
|
},
|
||||||
|
"sdkConfigs": {},
|
||||||
|
"icons": {
|
||||||
|
"android": {
|
||||||
|
"hdpi": "static/icons/72x72.png",
|
||||||
|
"xhdpi": "static/icons/96x96.png",
|
||||||
|
"xxhdpi": "static/icons/144x144.png",
|
||||||
|
"xxxhdpi": "static/icons/192x192.png"
|
||||||
|
},
|
||||||
|
"ios": {
|
||||||
|
"appstore": "static/icons/1024x1024.png",
|
||||||
|
"ipad": {
|
||||||
|
"app": "static/icons/76x76.png",
|
||||||
|
"app@2x": "static/icons/152x152.png",
|
||||||
|
"notification": "static/icons/20x20.png",
|
||||||
|
"notification@2x": "static/icons/40x40.png",
|
||||||
|
"proapp@2x": "static/icons/167x167.png",
|
||||||
|
"settings": "static/icons/29x29.png",
|
||||||
|
"settings@2x": "static/icons/58x58.png",
|
||||||
|
"spotlight": "static/icons/40x40.png",
|
||||||
|
"spotlight@2x": "static/icons/80x80.png"
|
||||||
|
},
|
||||||
|
"iphone": {
|
||||||
|
"app@2x": "static/icons/120x120.png",
|
||||||
|
"app@3x": "static/icons/180x180.png",
|
||||||
|
"notification@2x": "static/icons/40x40.png",
|
||||||
|
"notification@3x": "static/icons/60x60.png",
|
||||||
|
"settings@2x": "static/icons/58x58.png",
|
||||||
|
"settings@3x": "static/icons/87x87.png",
|
||||||
|
"spotlight@2x": "static/icons/80x80.png",
|
||||||
|
"spotlight@3x": "static/icons/120x120.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"background": "#000000",
|
||||||
|
"compatible": {
|
||||||
|
"ignoreVersion": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"quickapp": {},
|
||||||
|
"mp-weixin": {
|
||||||
|
"appid": "",
|
||||||
|
"setting": {
|
||||||
|
"urlCheck": false
|
||||||
|
},
|
||||||
|
"usingComponents": true,
|
||||||
|
"darkmode": false,
|
||||||
|
"themeLocation": "theme.json"
|
||||||
|
},
|
||||||
|
"mp-alipay": {
|
||||||
|
"usingComponents": true
|
||||||
|
},
|
||||||
|
"mp-baidu": {
|
||||||
|
"usingComponents": true
|
||||||
|
},
|
||||||
|
"mp-toutiao": {
|
||||||
|
"usingComponents": true
|
||||||
|
},
|
||||||
|
"uniStatistics": {
|
||||||
|
"enable": false
|
||||||
|
},
|
||||||
|
"vueVersion": "3",
|
||||||
|
"h5": {
|
||||||
|
"darkmode": false,
|
||||||
|
"themeLocation": "theme.json"
|
||||||
|
},
|
||||||
|
"uts": {
|
||||||
|
"plugins": {
|
||||||
|
"lz-url-launch": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "SFSafariViewController插件,支持在iOS中使用系统浏览器打开网页",
|
||||||
|
"platforms": {
|
||||||
|
"ios": {
|
||||||
|
"appid": "__UNI_LZ_URL_LAUNCH_IOS",
|
||||||
|
"autostart": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
161
src/pages.json
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
{
|
||||||
|
"pages": [
|
||||||
|
{
|
||||||
|
"path": "pages/index",
|
||||||
|
"type": "home",
|
||||||
|
"layout": "home",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTextStyle": "black",
|
||||||
|
"navigationStyle": "default",
|
||||||
|
"navigationBarBackgroundColor": "#e3f0ff"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/agent",
|
||||||
|
"type": "page",
|
||||||
|
"layout": "home",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTextStyle": "black",
|
||||||
|
"navigationStyle": "default",
|
||||||
|
"navigationBarBackgroundColor": "#e3f0ff"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/agentVip",
|
||||||
|
"type": "page",
|
||||||
|
"layout": "page",
|
||||||
|
"title": "代理会员",
|
||||||
|
"agent": true,
|
||||||
|
"auth": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/agentVipApply",
|
||||||
|
"type": "page",
|
||||||
|
"layout": "page",
|
||||||
|
"title": "开通代理会员",
|
||||||
|
"agent": true,
|
||||||
|
"auth": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/agentVipConfig",
|
||||||
|
"type": "page",
|
||||||
|
"layout": "page",
|
||||||
|
"title": "会员代理报告配置",
|
||||||
|
"agent": true,
|
||||||
|
"auth": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/agreement",
|
||||||
|
"type": "page",
|
||||||
|
"layout": "page",
|
||||||
|
"title": "协议",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTextStyle": "black",
|
||||||
|
"navigationStyle": "custom",
|
||||||
|
"navigationBarBackgroundColor": "#e3f0ff"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/invitation",
|
||||||
|
"type": "page",
|
||||||
|
"layout": "page",
|
||||||
|
"title": "邀请下级",
|
||||||
|
"auth": true,
|
||||||
|
"agent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/invitationAgentApply",
|
||||||
|
"type": "page",
|
||||||
|
"layout": "page",
|
||||||
|
"title": "代理申请",
|
||||||
|
"auth": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/login",
|
||||||
|
"type": "page",
|
||||||
|
"layout": "login",
|
||||||
|
"title": "绑定手机号"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/me",
|
||||||
|
"type": "page",
|
||||||
|
"layout": "home",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTextStyle": "black",
|
||||||
|
"navigationStyle": "default",
|
||||||
|
"navigationBarBackgroundColor": "#e3f0ff"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/promote",
|
||||||
|
"type": "page",
|
||||||
|
"layout": "page",
|
||||||
|
"title": "推广",
|
||||||
|
"agent": true,
|
||||||
|
"auth": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/promoteDetails",
|
||||||
|
"type": "page",
|
||||||
|
"layout": "page",
|
||||||
|
"title": "直推报告",
|
||||||
|
"agent": true,
|
||||||
|
"auth": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/rewardsDetails",
|
||||||
|
"type": "page",
|
||||||
|
"layout": "page",
|
||||||
|
"title": "收益明细",
|
||||||
|
"agent": true,
|
||||||
|
"auth": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/vip",
|
||||||
|
"type": "page"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/withdrawDetails",
|
||||||
|
"type": "page",
|
||||||
|
"layout": "page",
|
||||||
|
"title": "提现记录",
|
||||||
|
"auth": true,
|
||||||
|
"agent": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"globalStyle": {
|
||||||
|
"backgroundColor": "@bgColor",
|
||||||
|
"backgroundColorBottom": "@bgColorBottom",
|
||||||
|
"backgroundColorTop": "@bgColorTop",
|
||||||
|
"backgroundTextStyle": "@bgTxtStyle",
|
||||||
|
"navigationBarBackgroundColor": "#000000",
|
||||||
|
"navigationBarTextStyle": "@navTxtStyle",
|
||||||
|
"navigationBarTitleText": "天远数据",
|
||||||
|
"navigationStyle": "custom"
|
||||||
|
},
|
||||||
|
"tabBar": {
|
||||||
|
"backgroundColor": "@tabBgColor",
|
||||||
|
"borderStyle": "@tabBorderStyle",
|
||||||
|
"color": "@tabFontColor",
|
||||||
|
"selectedColor": "@tabSelectedColor",
|
||||||
|
"list": [
|
||||||
|
{
|
||||||
|
"pagePath": "pages/index",
|
||||||
|
"text": "",
|
||||||
|
"visible": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pagePath": "pages/agent",
|
||||||
|
"text": "",
|
||||||
|
"visible": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pagePath": "pages/me",
|
||||||
|
"text": "",
|
||||||
|
"visible": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"__esModule": true,
|
||||||
|
"subPackages": []
|
||||||
|
}
|
||||||
287
src/pages/agent.vue
Normal file
@@ -0,0 +1,287 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref, computed } from 'vue'
|
||||||
|
import { getAgentRevenue } from '@/api/apis'
|
||||||
|
import GzhQrcode from '@/components/GzhQrcode.vue'
|
||||||
|
|
||||||
|
// 日期选项映射
|
||||||
|
const dateRangeMap = {
|
||||||
|
today: 'today',
|
||||||
|
week: 'last7d',
|
||||||
|
month: 'last30d'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 日期文本映射
|
||||||
|
const dateTextMap = {
|
||||||
|
today: '今日',
|
||||||
|
week: '近7天',
|
||||||
|
month: '近1月'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 直推报告数据
|
||||||
|
const promoteDateOptions = [
|
||||||
|
{ label: '今日', value: 'today' },
|
||||||
|
{ label: '近7天', value: 'week' },
|
||||||
|
{ label: '近1月', value: 'month' }
|
||||||
|
]
|
||||||
|
const selectedPromoteDate = ref('today')
|
||||||
|
|
||||||
|
// 活跃下级数据
|
||||||
|
const activeDateOptions = [
|
||||||
|
{ label: '今日', value: 'today' },
|
||||||
|
{ label: '近7天', value: 'week' },
|
||||||
|
{ label: '近1月', value: 'month' }
|
||||||
|
]
|
||||||
|
const selectedActiveDate = ref('today')
|
||||||
|
|
||||||
|
const data = ref(null)
|
||||||
|
|
||||||
|
// 控制公众号二维码弹窗显示
|
||||||
|
const showGzhQrcode = ref(false)
|
||||||
|
|
||||||
|
// 计算当前直推数据
|
||||||
|
const currentPromoteData = computed(() => {
|
||||||
|
const range = dateRangeMap[selectedPromoteDate.value]
|
||||||
|
return data.value?.direct_push?.[range] || { commission: 0, report: 0 }
|
||||||
|
})
|
||||||
|
|
||||||
|
// 计算当前活跃数据
|
||||||
|
const currentActiveData = computed(() => {
|
||||||
|
const range = dateRangeMap[selectedActiveDate.value]
|
||||||
|
return data.value?.active_reward?.[range] || {
|
||||||
|
active_reward: 0,
|
||||||
|
sub_promote_reward: 0,
|
||||||
|
sub_upgrade_reward: 0,
|
||||||
|
sub_withdraw_reward: 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 计算日期文本
|
||||||
|
const promoteTimeText = computed(() => {
|
||||||
|
return dateTextMap[selectedPromoteDate.value] || '今日'
|
||||||
|
})
|
||||||
|
|
||||||
|
const activeTimeText = computed(() => {
|
||||||
|
return dateTextMap[selectedActiveDate.value] || '今日'
|
||||||
|
})
|
||||||
|
|
||||||
|
const getData = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getAgentRevenue()
|
||||||
|
if (res.code === 200) {
|
||||||
|
data.value = res.data
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
if (uni.getStorageSync('token')) {
|
||||||
|
getData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 路由跳转
|
||||||
|
function goToPromoteDetail() {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/promoteDetails'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function goToActiveDetail() {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/rewardsDetails'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function toWithdraw() {
|
||||||
|
// 弹出公众号二维码提示提现
|
||||||
|
showGzhQrcode.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭公众号二维码弹窗
|
||||||
|
function closeGzhQrcode() {
|
||||||
|
showGzhQrcode.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function toWithdrawDetails() {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/withdrawDetails'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="safe-area-top min-h-screen">
|
||||||
|
<view class="p-4">
|
||||||
|
<!-- 资产卡片 -->
|
||||||
|
<view class="rounded-xl shadow-lg mb-4 bg-gradient-to-r from-blue-50/70 to-blue-100/50 p-6">
|
||||||
|
<view class="flex justify-between items-center mb-3">
|
||||||
|
<view class="flex items-center">
|
||||||
|
|
||||||
|
|
||||||
|
<text class="text-lg font-bold text-gray-800">余额</text>
|
||||||
|
</view>
|
||||||
|
<text class="text-3xl text-blue-600 font-bold">¥ {{ (data?.balance || 0).toFixed(2) }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="text-sm text-gray-500 mb-2">累计收益:¥ {{ (data?.total_earnings || 0).toFixed(2) }}</view>
|
||||||
|
<view class="text-sm text-gray-500 mb-6">冻结余额:¥ {{ (data?.frozen_balance || 0).toFixed(2) }}</view>
|
||||||
|
<view class="grid grid-cols-2 gap-3">
|
||||||
|
<view @click="toWithdraw"
|
||||||
|
class="bg-gradient-to-r from-blue-500 to-blue-400 text-white rounded-full py-2 shadow-md flex items-center justify-center">
|
||||||
|
<text>提现</text>
|
||||||
|
</view>
|
||||||
|
<view @click="toWithdrawDetails"
|
||||||
|
class="bg-white/90 text-gray-600 border border-gray-200/50 rounded-full py-2 shadow-sm flex items-center justify-center">
|
||||||
|
<text>提现记录</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 直推报告收益 -->
|
||||||
|
<view class="rounded-xl shadow-lg mb-4 bg-gradient-to-r from-blue-50/40 to-cyan-50/50 p-6">
|
||||||
|
<view class="flex justify-between items-center mb-4">
|
||||||
|
<view class="flex items-center">
|
||||||
|
<text class="text-lg font-bold text-gray-800">直推报告收益</text>
|
||||||
|
</view>
|
||||||
|
<view class="text-right">
|
||||||
|
<text class="text-2xl text-blue-600 font-bold">¥ {{ (data?.direct_push?.total_commission || 0).toFixed(2)
|
||||||
|
}}</text>
|
||||||
|
<view class="text-sm text-gray-500 mt-1">有效报告 {{ data?.direct_push?.total_report || 0 }} 份</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 日期选择 -->
|
||||||
|
<view class="grid grid-cols-3 gap-2 mb-6">
|
||||||
|
<view v-for="item in promoteDateOptions" :key="item.value" @click="selectedPromoteDate = item.value" :class="[
|
||||||
|
'rounded-full transition-all py-1 px-4 text-sm text-center',
|
||||||
|
selectedPromoteDate === item.value
|
||||||
|
? 'bg-blue-500 text-white shadow-md'
|
||||||
|
: 'bg-white/90 text-gray-600 border border-gray-200/50'
|
||||||
|
]">
|
||||||
|
{{ item.label }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="grid grid-cols-2 gap-4 mb-6">
|
||||||
|
<view class="bg-blue-50/60 p-3 rounded-lg backdrop-blur-sm">
|
||||||
|
<view class="flex items-center text-sm text-gray-500">
|
||||||
|
<text>{{ promoteTimeText }}收益</text>
|
||||||
|
</view>
|
||||||
|
<text class="text-xl text-blue-600 font-bold mt-1">¥ {{ currentPromoteData.commission?.toFixed(2) || '0.00'
|
||||||
|
}}</text>
|
||||||
|
</view>
|
||||||
|
<view class="bg-blue-50/60 p-3 rounded-lg backdrop-blur-sm">
|
||||||
|
<view class="flex items-center text-sm text-gray-500">
|
||||||
|
<text>有效报告</text>
|
||||||
|
</view>
|
||||||
|
<text class="text-xl text-blue-600 font-bold mt-1">{{ currentPromoteData.report || 0 }} 份</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="flex items-center justify-between text-blue-500 text-sm font-semibold" @click="goToPromoteDetail">
|
||||||
|
<text>查看收益明细</text>
|
||||||
|
<text class="text-lg">→</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 活跃下级奖励 -->
|
||||||
|
<view class="rounded-xl shadow-lg bg-gradient-to-r from-green-50/40 to-cyan-50/30 p-6">
|
||||||
|
<view class="flex justify-between items-center mb-4">
|
||||||
|
<view class="flex items-center">
|
||||||
|
<text class="text-lg font-bold text-gray-800">活跃下级奖励</text>
|
||||||
|
</view>
|
||||||
|
<view class="text-right">
|
||||||
|
<text class="text-2xl text-green-600 font-bold">¥ {{ (data?.active_reward?.total_reward || 0).toFixed(2)
|
||||||
|
}}</text>
|
||||||
|
<view class="text-sm text-gray-500 mt-1">活跃下级 0 位</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 日期选择 -->
|
||||||
|
<view class="grid grid-cols-3 gap-2 mb-6">
|
||||||
|
<view v-for="item in activeDateOptions" :key="item.value" @click="selectedActiveDate = item.value" :class="[
|
||||||
|
'rounded-full transition-all py-1 px-4 text-sm text-center',
|
||||||
|
selectedActiveDate === item.value
|
||||||
|
? 'bg-green-500 text-white shadow-md'
|
||||||
|
: 'bg-white/90 text-gray-600 border border-gray-200/50'
|
||||||
|
]">
|
||||||
|
{{ item.label }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="grid grid-cols-2 gap-2 mb-6">
|
||||||
|
<view class="bg-green-50/60 p-3 rounded-lg backdrop-blur-sm">
|
||||||
|
<view class="flex items-center text-sm text-gray-500">
|
||||||
|
<text>{{ activeTimeText }}奖励</text>
|
||||||
|
</view>
|
||||||
|
<text class="text-xl text-green-600 font-bold mt-1">¥ {{ (currentActiveData.active_reward || 0).toFixed(2)
|
||||||
|
}}</text>
|
||||||
|
</view>
|
||||||
|
<view class="bg-green-50/60 p-3 rounded-lg backdrop-blur-sm">
|
||||||
|
<view class="flex items-center text-sm text-gray-500">
|
||||||
|
<text>下级推广奖励</text>
|
||||||
|
</view>
|
||||||
|
<text class="text-xl text-green-600 font-bold mt-1">¥ {{ (currentActiveData.sub_promote_reward ||
|
||||||
|
0).toFixed(2) }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="bg-green-50/60 p-3 rounded-lg backdrop-blur-sm">
|
||||||
|
<view class="flex items-center text-sm text-gray-500">
|
||||||
|
<text>新增活跃奖励</text>
|
||||||
|
</view>
|
||||||
|
<text class="text-xl text-green-600 font-bold mt-1">¥ {{ (currentActiveData.sub_upgrade_reward ||
|
||||||
|
0).toFixed(2) }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="bg-green-50/60 p-3 rounded-lg backdrop-blur-sm">
|
||||||
|
<view class="flex items-center text-sm text-gray-500">
|
||||||
|
<text>下级转化奖励</text>
|
||||||
|
</view>
|
||||||
|
<text class="text-xl text-green-600 font-bold mt-1">¥ {{ (currentActiveData.sub_withdraw_reward ||
|
||||||
|
0).toFixed(2) }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="flex items-center justify-between text-green-500 text-sm font-semibold" @click="goToActiveDetail">
|
||||||
|
<text>查看奖励明细</text>
|
||||||
|
<text class="text-lg">→</text>
|
||||||
|
</view>
|
||||||
|
<!-- 添加查看下级按钮 -->
|
||||||
|
<!-- <view class="mt-4 px-4">
|
||||||
|
<view
|
||||||
|
@click="toSubordinateList"
|
||||||
|
class=" mx-auto bg-gradient-to-r from-green-500 to-green-400 text-white rounded-full py-2 px-4 shadow-md flex items-center justify-center active:opacity-80"
|
||||||
|
hover-class="opacity-90 scale-98"
|
||||||
|
>
|
||||||
|
<text>查看我的下级</text>
|
||||||
|
</view>
|
||||||
|
</view> -->
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 公众号二维码弹窗 -->
|
||||||
|
<GzhQrcode
|
||||||
|
:visible="showGzhQrcode"
|
||||||
|
type="withdraw"
|
||||||
|
@close="closeGzhQrcode"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
button {
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<route lang="json">{
|
||||||
|
"layout": "home",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTextStyle": "black",
|
||||||
|
"navigationStyle": "default",
|
||||||
|
"navigationBarBackgroundColor": "#e3f0ff"
|
||||||
|
}
|
||||||
|
}</route>
|
||||||
63
src/pages/agentVip.vue
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<template>
|
||||||
|
<view class="relative">
|
||||||
|
<image class="w-full" src="/static/image/vip_bg.png" mode="widthFix" />
|
||||||
|
<view @click="toService" class="service-btn">
|
||||||
|
点击马上报名
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
function toService() {
|
||||||
|
// 直接使用plus.runtime.openURL在系统浏览器中打开链接
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
plus.runtime.openURL('https://work.weixin.qq.com/kfid/kfc5c19b2b93a5e73b9');
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef H5
|
||||||
|
window.location.href = 'https://work.weixin.qq.com/kfid/kfc5c19b2b93a5e73b9';
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef MP
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/agreement?url=' + encodeURIComponent('https://work.weixin.qq.com/kfid/kfc5c19b2b93a5e73b9')
|
||||||
|
});
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.relative {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-btn {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
bottom: 600rpx;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
background: linear-gradient(to right, #2d3748, #000000, #2d3748);
|
||||||
|
padding: 10rpx 20rpx;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
color: #ffffff;
|
||||||
|
font-size: 48rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
box-shadow: 0 0 30rpx rgba(255, 255, 255, 0.3);
|
||||||
|
transition: transform 0.3s;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translateX(-50%) scale(1.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<route lang="json">
|
||||||
|
{
|
||||||
|
"layout": "page",
|
||||||
|
"title": "代理会员",
|
||||||
|
"agent": true,
|
||||||
|
"auth": true
|
||||||
|
}
|
||||||
|
</route>
|
||||||
816
src/pages/agentVipApply.vue
Normal file
@@ -0,0 +1,816 @@
|
|||||||
|
<template>
|
||||||
|
<view class="agent-VIP-apply w-full min-h-screen bg-gradient-to-b from-amber-50 via-amber-100 to-amber-50 pb-24">
|
||||||
|
<!-- 装饰元素 -->
|
||||||
|
<view
|
||||||
|
class="absolute top-0 right-0 w-32 h-32 bg-gradient-to-br from-amber-300 to-amber-500 rounded-bl-full opacity-20">
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="absolute top-40 left-0 w-16 h-16 bg-gradient-to-tr from-amber-400 to-amber-600 rounded-tr-full opacity-20">
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="absolute bottom-60 right-0 w-24 h-24 bg-gradient-to-bl from-amber-300 to-amber-500 rounded-tl-full opacity-20">
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 顶部标题区域 -->
|
||||||
|
<view class="header relative pt-8 px-4 pb-6 text-center">
|
||||||
|
<view
|
||||||
|
class="animate-pulse absolute -top-2 left-1/2 -translate-x-1/2 w-24 h-1 bg-gradient-to-r from-amber-300 via-amber-500 to-amber-300 rounded-full">
|
||||||
|
</view>
|
||||||
|
<text class="text-3xl font-bold text-amber-800 mb-1 block">
|
||||||
|
{{ isVipOrSvip ? '代理会员续费' : 'VIP代理申请' }}
|
||||||
|
</text>
|
||||||
|
<view class="text-sm text-amber-700 mt-2 max-w-xs mx-auto">
|
||||||
|
<block v-if="isVipOrSvip">
|
||||||
|
您的会员有效期至 {{ formatExpiryTime(ExpiryTime) }},续费后有效期至
|
||||||
|
{{ renewalExpiryTime }}
|
||||||
|
</block>
|
||||||
|
<block v-else>
|
||||||
|
平台为疯狂推广者定制的赚买计划,助您收益<text class="text-red-500 font-bold">翻倍增升</text>
|
||||||
|
</block>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 装饰性金币图标 -->
|
||||||
|
<view class="absolute top-6 left-4 transform -rotate-12">
|
||||||
|
<view
|
||||||
|
class="w-8 h-8 bg-gradient-to-br from-yellow-300 to-yellow-500 rounded-full flex items-center justify-center shadow-lg">
|
||||||
|
<text class="text-white font-bold text-xs">¥</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="absolute top-10 right-6 transform rotate-12">
|
||||||
|
<view
|
||||||
|
class="w-6 h-6 bg-gradient-to-br from-yellow-400 to-yellow-600 rounded-full flex items-center justify-center shadow-lg">
|
||||||
|
<text class="text-white font-bold text-xs">¥</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 选择代理类型 -->
|
||||||
|
<view class="card-container px-4 mb-8">
|
||||||
|
<view class="bg-white rounded-xl shadow-lg overflow-hidden border border-amber-100 transform transition-all">
|
||||||
|
<view
|
||||||
|
class="bg-gradient-to-r from-amber-500 to-amber-600 text-white py-3 px-4 text-center font-bold relative overflow-hidden">
|
||||||
|
<text class="relative z-10">选择代理类型</text>
|
||||||
|
<view class="absolute inset-0 bg-amber-500 opacity-30">
|
||||||
|
<view
|
||||||
|
class="absolute top-0 left-0 w-full h-full bg-gradient-to-r from-transparent via-white to-transparent opacity-20 transform -skew-x-30 translate-x-full animate-shimmer">
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="flex p-6 gap-4">
|
||||||
|
<view
|
||||||
|
class="flex-1 border-2 rounded-lg p-4 text-center cursor-pointer transition-all duration-300 relative transform hover:-translate-y-1"
|
||||||
|
:class="[
|
||||||
|
selectedType === 'vip'
|
||||||
|
? 'border-amber-500 bg-amber-50 shadow-md'
|
||||||
|
: 'border-gray-200 hover:border-amber-300',
|
||||||
|
]" @click="selectType('vip')">
|
||||||
|
<text class="text-xl font-bold text-amber-700 block">VIP代理</text>
|
||||||
|
<text class="text-amber-600 font-bold mt-1 text-lg block">{{ vipConfig.price }}{{ vipConfig.priceUnit
|
||||||
|
}}</text>
|
||||||
|
<text class="mt-2 text-gray-600 text-sm block">标准VIP权益</text>
|
||||||
|
<view v-if="selectedType === 'vip'"
|
||||||
|
class="absolute -top-2 -right-2 w-6 h-6 bg-gradient-to-br from-amber-500 to-amber-600 rounded-full flex items-center justify-center shadow-md">
|
||||||
|
<wd-icon name="check" color="#fff" size="14" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view
|
||||||
|
class="flex-1 border-2 rounded-lg p-4 text-center cursor-pointer transition-all duration-300 relative transform hover:-translate-y-1"
|
||||||
|
:class="[
|
||||||
|
selectedType === 'svip'
|
||||||
|
? 'border-amber-500 bg-amber-50 shadow-md'
|
||||||
|
: 'border-gray-200 hover:border-amber-300',
|
||||||
|
]" @click="selectType('svip')">
|
||||||
|
<text class="text-xl font-bold text-amber-700 block">SVIP代理</text>
|
||||||
|
<text class="text-amber-600 font-bold mt-1 text-lg block">{{ vipConfig.svipPrice }}{{ vipConfig.priceUnit
|
||||||
|
}}</text>
|
||||||
|
<text class="mt-2 text-gray-600 text-sm block">超级VIP权益</text>
|
||||||
|
<view v-if="selectedType === 'svip'"
|
||||||
|
class="absolute -top-2 -right-2 w-6 h-6 bg-gradient-to-br from-amber-500 to-amber-600 rounded-full flex items-center justify-center shadow-md">
|
||||||
|
<wd-icon name="check" color="#fff" size="14" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 六大超值权益 -->
|
||||||
|
<view class="card-container px-4 mb-8">
|
||||||
|
<view class="bg-white rounded-xl shadow-lg overflow-hidden border border-amber-100">
|
||||||
|
<view
|
||||||
|
class="bg-gradient-to-r from-amber-500 to-amber-600 text-white py-3 px-4 text-center font-bold relative overflow-hidden">
|
||||||
|
<text class="relative z-10">六大超值权益</text>
|
||||||
|
<view class="absolute inset-0 bg-amber-500 opacity-30">
|
||||||
|
<view
|
||||||
|
class="absolute top-0 left-0 w-full h-full bg-gradient-to-r from-transparent via-white to-transparent opacity-20 transform -skew-x-30 translate-x-full animate-shimmer">
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="grid grid-cols-2 gap-4 p-4">
|
||||||
|
<!-- 权益1 -->
|
||||||
|
<view
|
||||||
|
class="bg-gradient-to-br from-amber-50 to-amber-100 rounded-lg p-3 border border-amber-200 transition-all duration-300 hover:shadow-md hover:border-amber-300">
|
||||||
|
<view class="text-amber-800 font-bold mb-2 flex items-center">
|
||||||
|
<text
|
||||||
|
class="w-6 h-6 bg-gradient-to-br from-amber-500 to-amber-600 rounded-full flex items-center justify-center text-white text-xs mr-2">1</text>
|
||||||
|
下级贡献收益
|
||||||
|
</view>
|
||||||
|
<text class="text-sm text-gray-600 block">
|
||||||
|
下级完全收益您来定,涨多少赚多少,一单最高收益<text class="text-red-500 font-bold">10元</text>
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 权益2 -->
|
||||||
|
<view
|
||||||
|
class="bg-gradient-to-br from-amber-50 to-amber-100 rounded-lg p-3 border border-amber-200 transition-all duration-300 hover:shadow-md hover:border-amber-300">
|
||||||
|
<view class="text-amber-800 font-bold mb-2 flex items-center">
|
||||||
|
<text
|
||||||
|
class="w-6 h-6 bg-gradient-to-br from-amber-500 to-amber-600 rounded-full flex items-center justify-center text-white text-xs mr-2">2</text>
|
||||||
|
下级提现收益
|
||||||
|
</view>
|
||||||
|
<text class="text-sm text-gray-600 block">
|
||||||
|
下级定价标准由您定,超过标准部分收益更丰厚,一单最高多赚<text class="text-red-500 font-bold">10元</text>
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 权益3 -->
|
||||||
|
<view
|
||||||
|
class="bg-gradient-to-br from-amber-50 to-amber-100 rounded-lg p-3 border border-amber-200 transition-all duration-300 hover:shadow-md hover:border-amber-300">
|
||||||
|
<view class="text-amber-800 font-bold mb-2 flex items-center">
|
||||||
|
<text
|
||||||
|
class="w-6 h-6 bg-gradient-to-br from-amber-500 to-amber-600 rounded-full flex items-center justify-center text-white text-xs mr-2">3</text>
|
||||||
|
转换高额奖励
|
||||||
|
</view>
|
||||||
|
<text class="text-sm text-gray-600 block">
|
||||||
|
下级成为VIP、SVIP,高额奖励立马发放,<text class="text-red-500 font-bold">399元</text>
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 权益4 -->
|
||||||
|
<view
|
||||||
|
class="bg-gradient-to-br from-amber-50 to-amber-100 rounded-lg p-3 border border-amber-200 transition-all duration-300 hover:shadow-md hover:border-amber-300">
|
||||||
|
<view class="text-amber-800 font-bold mb-2 flex items-center">
|
||||||
|
<text
|
||||||
|
class="w-6 h-6 bg-gradient-to-br from-amber-500 to-amber-600 rounded-full flex items-center justify-center text-white text-xs mr-2">4</text>
|
||||||
|
下级提现奖励
|
||||||
|
</view>
|
||||||
|
<text class="text-sm text-gray-600 block">下级成为SVIP,每次提现都奖励1%,坐享被动收入</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 权益5 -->
|
||||||
|
<view
|
||||||
|
class="bg-gradient-to-br from-amber-50 to-amber-100 rounded-lg p-3 border border-amber-200 transition-all duration-300 hover:shadow-md hover:border-amber-300">
|
||||||
|
<view class="text-amber-800 font-bold mb-2 flex items-center">
|
||||||
|
<text
|
||||||
|
class="w-6 h-6 bg-gradient-to-br from-amber-500 to-amber-600 rounded-full flex items-center justify-center text-white text-xs mr-2">5</text>
|
||||||
|
月度现金奖励
|
||||||
|
</view>
|
||||||
|
<text class="text-sm text-gray-600 block">
|
||||||
|
下级每月活跃达100名额外奖励<text class="text-red-500 font-bold">50元</text>,新增15名活跃下级再得<text
|
||||||
|
class="text-red-500 font-bold">50元</text>
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 权益6 -->
|
||||||
|
<view
|
||||||
|
class="bg-gradient-to-br from-amber-50 to-amber-100 rounded-lg p-3 border border-amber-200 transition-all duration-300 hover:shadow-md hover:border-amber-300">
|
||||||
|
<view class="text-amber-800 font-bold mb-2 flex items-center">
|
||||||
|
<text
|
||||||
|
class="w-6 h-6 bg-gradient-to-br from-amber-500 to-amber-600 rounded-full flex items-center justify-center text-white text-xs mr-2">6</text>
|
||||||
|
平台专项扶持
|
||||||
|
</view>
|
||||||
|
<text class="text-sm text-gray-600 block">一对一专属客服服务,为合作伙伴提供全方位成长赋能</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 权益对比表 -->
|
||||||
|
<view class="card-container px-4 mb-8" v-if="selectedType">
|
||||||
|
<view class="bg-white rounded-xl shadow-lg overflow-hidden border border-amber-100">
|
||||||
|
<view
|
||||||
|
class="bg-gradient-to-r from-amber-500 to-amber-600 text-white py-3 px-4 text-center font-bold relative overflow-hidden">
|
||||||
|
<text class="relative z-10">{{ selectedType === 'vip' ? 'VIP' : 'SVIP' }}代理权益对比</text>
|
||||||
|
<view class="absolute inset-0 bg-amber-500 opacity-30">
|
||||||
|
<view
|
||||||
|
class="absolute top-0 left-0 w-full h-full bg-gradient-to-r from-transparent via-white to-transparent opacity-20 transform -skew-x-30 translate-x-full animate-shimmer">
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="p-4">
|
||||||
|
<!-- 权益对比表格 -->
|
||||||
|
<view class="w-full border border-amber-200 rounded-lg overflow-hidden">
|
||||||
|
<!-- 表头 -->
|
||||||
|
<view class="flex bg-gradient-to-r from-amber-100 to-amber-200">
|
||||||
|
<view class="w-16 flex-shrink-0 p-3 border-r border-amber-200 text-left font-bold text-amber-800">权益项目
|
||||||
|
</view>
|
||||||
|
<view class="flex-1 flex-shrink-0 p-3 border-r border-amber-200 text-center font-bold text-amber-800">普通代理
|
||||||
|
</view>
|
||||||
|
<view class="flex-1 flex-shrink-0 p-3 border-r border-amber-200 text-center font-bold text-amber-800"
|
||||||
|
:class="{ 'bg-amber-200': selectedType === 'vip' }">VIP代理</view>
|
||||||
|
<view class="flex-1 flex-shrink-0 p-3 text-center font-bold text-amber-800"
|
||||||
|
:class="{ 'bg-amber-200': selectedType === 'svip' }">SVIP代理</view>
|
||||||
|
</view>
|
||||||
|
<!-- 表格内容 -->
|
||||||
|
<view v-for="(item, index) in benefitsComparisonData" :key="index"
|
||||||
|
class="flex border-b border-amber-200 last:border-b-0" :class="{ 'bg-amber-50': index % 2 === 1 }">
|
||||||
|
<view class="w-16 flex-shrink-0 p-3 border-r border-amber-200 text-left font-medium">{{ item.benefit }}
|
||||||
|
</view>
|
||||||
|
<view class="flex-1 flex-shrink-0 p-3 border-r border-amber-200 text-center text-sm">{{ item.normal }}
|
||||||
|
</view>
|
||||||
|
<view class="flex-1 flex-shrink-0 p-3 border-r border-amber-200 text-center text-sm" :class="{
|
||||||
|
'text-amber-700 font-bold bg-amber-50': selectedType === 'vip',
|
||||||
|
}">{{ item.vip }}</view>
|
||||||
|
<view class="flex-1 flex-shrink-0 p-3 text-center text-sm" :class="{
|
||||||
|
'text-amber-700 font-bold bg-amber-50': selectedType === 'svip',
|
||||||
|
}">{{ item.svip }}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 收益预估 -->
|
||||||
|
<view class="card-container px-4 mb-8" v-if="selectedType">
|
||||||
|
<view class="bg-white rounded-xl shadow-lg overflow-hidden border border-amber-100">
|
||||||
|
<view
|
||||||
|
class="bg-gradient-to-r from-amber-500 to-amber-600 text-white py-3 px-4 text-center font-bold relative overflow-hidden">
|
||||||
|
<text class="relative z-10">收益预估对比</text>
|
||||||
|
<view class="absolute inset-0 bg-amber-500 opacity-30">
|
||||||
|
<view
|
||||||
|
class="absolute top-0 left-0 w-full h-full bg-gradient-to-r from-transparent via-white to-transparent opacity-20 transform -skew-x-30 translate-x-full animate-shimmer">
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="p-4">
|
||||||
|
<!-- 顶部收益概览 -->
|
||||||
|
<view class="mb-6 rounded-lg overflow-hidden border border-amber-200">
|
||||||
|
<view class="bg-gradient-to-r from-amber-100 to-amber-200 py-2 px-4 text-center font-bold text-amber-800">
|
||||||
|
VIP与SVIP代理收益对比
|
||||||
|
</view>
|
||||||
|
<view class="grid grid-cols-2 divide-x divide-amber-200">
|
||||||
|
<view class="p-4 text-center" :class="{ 'bg-amber-50': selectedType === 'vip' }">
|
||||||
|
<text class="text-sm text-gray-600 mb-1 block">VIP月预计收益</text>
|
||||||
|
<text class="text-amber-600 font-bold text-xl block">{{ revenueData.vipMonthly }}元</text>
|
||||||
|
<text class="text-xs text-gray-500 mt-1 block">年收益:{{ revenueData.vipYearly }}元</text>
|
||||||
|
</view>
|
||||||
|
<view class="p-4 text-center" :class="{ 'bg-amber-50': selectedType === 'svip' }">
|
||||||
|
<text class="text-sm text-gray-600 mb-1 block">SVIP月预计收益</text>
|
||||||
|
<text class="text-red-500 font-bold text-xl block">{{ revenueData.svipMonthly }}元</text>
|
||||||
|
<text class="text-xs text-gray-500 mt-1 block">年收益:{{ revenueData.svipYearly }}元</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="bg-gradient-to-r from-red-50 to-red-100 py-2 px-4 text-center text-red-600 font-medium">
|
||||||
|
选择SVIP,相比VIP月增收益:<text class="font-bold">{{ revenueData.monthlyDifference }}元</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 详细收益表格 -->
|
||||||
|
<view>
|
||||||
|
<view class="w-full border border-amber-200 rounded-lg overflow-hidden">
|
||||||
|
<!-- 表头 -->
|
||||||
|
<view class="flex bg-gradient-to-r from-amber-100 to-amber-200">
|
||||||
|
<view class="w-24 flex-shrink-0 p-3 border-r border-amber-200 text-left font-bold text-amber-800">收益来源
|
||||||
|
</view>
|
||||||
|
<view class="flex-1 flex-shrink-0 p-3 border-r border-amber-200 text-center font-bold text-amber-800"
|
||||||
|
:class="{ 'bg-amber-200': selectedType === 'vip' }">VIP代理</view>
|
||||||
|
<view class="flex-1 flex-shrink-0 p-3 text-center font-bold text-amber-800"
|
||||||
|
:class="{ 'bg-amber-200': selectedType === 'svip' }">SVIP代理</view>
|
||||||
|
</view>
|
||||||
|
<!-- 表格内容 -->
|
||||||
|
<view v-for="(item, index) in revenueComparisonData" :key="index"
|
||||||
|
class="flex border-b border-amber-200 last:border-b-0" :class="{
|
||||||
|
'bg-amber-50': index % 2 === 1,
|
||||||
|
'bg-gradient-to-r from-amber-50 to-amber-100 font-bold': item.source.includes('收益')
|
||||||
|
}">
|
||||||
|
<view class="w-24 flex-shrink-0 p-3 border-r border-amber-200 text-left font-medium">{{ item.source }}
|
||||||
|
</view>
|
||||||
|
<view class="flex-1 flex-shrink-0 p-3 border-r border-amber-200 text-center text-sm" :class="{
|
||||||
|
'text-amber-700 font-medium bg-amber-50': selectedType === 'vip',
|
||||||
|
'text-amber-700': item.source.includes('收益'),
|
||||||
|
'border-amber-300': item.source.includes('收益') && selectedType === 'vip'
|
||||||
|
}">{{ item.vip }}</view>
|
||||||
|
<view class="flex-1 flex-shrink-0 p-3 text-center text-sm" :class="{
|
||||||
|
'text-amber-700 font-medium bg-amber-50': selectedType === 'svip',
|
||||||
|
'text-red-500': item.source.includes('收益'),
|
||||||
|
'border-amber-300': item.source.includes('收益') && selectedType === 'svip'
|
||||||
|
}">{{ item.svip }}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 投资回报率 -->
|
||||||
|
<view class="mt-6 p-4 bg-gradient-to-r from-amber-50 to-amber-100 rounded-lg border border-amber-200">
|
||||||
|
<text class="text-center mb-3 font-bold text-amber-800 block">投资收益分析</text>
|
||||||
|
<view class="grid grid-cols-1 gap-4">
|
||||||
|
<view class="p-3 bg-white rounded-lg shadow-sm">
|
||||||
|
<view class="flex items-center justify-between">
|
||||||
|
<view class="flex-1 border-r border-amber-100 pr-3">
|
||||||
|
<text class="text-amber-700 font-medium text-center mb-1 block">VIP方案</text>
|
||||||
|
<view class="text-center">
|
||||||
|
<text class="text-amber-600 text-sm block">投资{{ vipConfig.price }}元</text>
|
||||||
|
<text class="text-gray-600 text-sm block">月收益{{ revenueData.vipMonthly }}元</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="flex-1 pl-3">
|
||||||
|
<text class="text-red-500 font-medium text-center mb-1 block">SVIP方案</text>
|
||||||
|
<view class="text-center">
|
||||||
|
<text class="text-red-500 text-sm block">投资{{ vipConfig.svipPrice }}元</text>
|
||||||
|
<text class="text-gray-600 text-sm block">月收益{{ revenueData.svipMonthly }}元</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 升级收益对比 -->
|
||||||
|
<view class="p-3 bg-gradient-to-r from-red-50 to-amber-50 rounded-lg shadow-sm">
|
||||||
|
<text class="text-center font-medium text-red-700 mb-2 block">SVIP升级优势分析</text>
|
||||||
|
<view class="flex items-center justify-center gap-3">
|
||||||
|
<view class="text-center">
|
||||||
|
<text class="text-sm text-gray-600 block">额外投资</text>
|
||||||
|
<text class="text-red-600 font-bold block">{{ revenueData.priceDifference }}元</text>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="bg-red-500 flex-shrink-0 text-white rounded-full w-6 h-6 flex items-center justify-center">
|
||||||
|
<text class="transform -translate-y-px">→</text>
|
||||||
|
</view>
|
||||||
|
<view class="text-center">
|
||||||
|
<text class="text-sm text-gray-600 block">每月额外收益</text>
|
||||||
|
<text class="text-red-600 font-bold block">{{ revenueData.monthlyDifference }}元</text>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="bg-red-500 flex-shrink-0 text-white rounded-full w-6 h-6 flex items-center justify-center">
|
||||||
|
<text class="transform -translate-y-px">→</text>
|
||||||
|
</view>
|
||||||
|
<view class="text-center">
|
||||||
|
<text class="text-sm text-gray-600 block">投资回收时间</text>
|
||||||
|
<text class="text-red-600 font-bold block">{{ revenueData.recoverDays }}天</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="text-center text-red-500 font-medium mt-3">
|
||||||
|
额外投资{{ revenueData.priceDifference }}元,<text class="text-red-600 font-bold">年多赚{{
|
||||||
|
revenueData.yearlyDifference }}元</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 申请按钮(固定在底部) -->
|
||||||
|
<view
|
||||||
|
class="fixed bottom-0 left-0 right-0 px-4 py-3 bg-gradient-to-t from-amber-100 to-transparent backdrop-blur-sm z-30">
|
||||||
|
<view class="flex flex-col gap-2">
|
||||||
|
<button :class="buttonClass" @click="applyVip" :disabled="!canPerformAction">
|
||||||
|
<text class="relative z-10">{{ buttonText }}</text>
|
||||||
|
<view
|
||||||
|
class="absolute top-0 left-0 w-full h-full bg-gradient-to-r from-transparent via-white to-transparent opacity-20 transform -skew-x-30 translate-x-full animate-shimmer">
|
||||||
|
</view>
|
||||||
|
</button>
|
||||||
|
<!-- 最终解释权声明 -->
|
||||||
|
<text class="text-center text-xs text-gray-400 py-1 block">最终解释权归海南天远大数据科技有限公司所有</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<Payment v-model="showPayment" :data="payData" :id="payID" type="agent_vip" @close="showPayment = false" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted, reactive, computed } from 'vue'
|
||||||
|
import { useToast } from 'wot-design-uni'
|
||||||
|
import { activateAgentMembership } from '@/apis/agent'
|
||||||
|
|
||||||
|
// 使用 wot-design-ui 的 toast
|
||||||
|
const toast = useToast()
|
||||||
|
|
||||||
|
// 代理相关数据
|
||||||
|
const level = ref('normal')
|
||||||
|
const ExpiryTime = ref('')
|
||||||
|
const isAgent = ref(false)
|
||||||
|
const agentStatus = ref(0)
|
||||||
|
const agentID = ref('')
|
||||||
|
const mobile = ref('')
|
||||||
|
const isRealName = ref(false)
|
||||||
|
|
||||||
|
// 计算是否已经是VIP或SVIP
|
||||||
|
const isVipOrSvip = computed(() => ['VIP', 'SVIP'].includes(level.value))
|
||||||
|
const isVip = computed(() => level.value === 'VIP')
|
||||||
|
const isSvip = computed(() => level.value === 'SVIP')
|
||||||
|
|
||||||
|
// 计算续费后的到期时间
|
||||||
|
const renewalExpiryTime = computed(() => {
|
||||||
|
if (!ExpiryTime.value) return '未知'
|
||||||
|
|
||||||
|
// 从格式化字符串中提取日期部分
|
||||||
|
const dateStr = ExpiryTime.value.split(' ')[0] // 假设格式是 "YYYY-MM-DD HH:MM:SS"
|
||||||
|
const [year, month, day] = dateStr.split('-').map(num => parseInt(num))
|
||||||
|
|
||||||
|
// 创建日期对象并加一年
|
||||||
|
const expiryDate = new Date(year, month - 1, day) // 月份从0开始,所以要-1
|
||||||
|
expiryDate.setFullYear(expiryDate.getFullYear() + 1)
|
||||||
|
|
||||||
|
// 返回格式化的日期字符串
|
||||||
|
return `${expiryDate.getFullYear()}-${String(expiryDate.getMonth() + 1).padStart(2, '0')}-${String(expiryDate.getDate()).padStart(2, '0')}`
|
||||||
|
})
|
||||||
|
|
||||||
|
// 按钮文字 - 根据当前状态显示不同文案
|
||||||
|
const buttonText = computed(() => {
|
||||||
|
if (!isVipOrSvip.value) return '立即开通' // 非会员状态
|
||||||
|
|
||||||
|
if (selectedType.value === 'vip') {
|
||||||
|
if (isVip.value) return '续费VIP代理' // VIP续费VIP
|
||||||
|
return '降级不可用' // SVIP不能降级到VIP
|
||||||
|
} else {
|
||||||
|
if (isSvip.value) return '续费SVIP代理' // SVIP续费SVIP
|
||||||
|
return '升级SVIP代理' // VIP升级SVIP
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 是否可以操作按钮
|
||||||
|
const canPerformAction = computed(() => {
|
||||||
|
// 非会员可以开通任何会员
|
||||||
|
if (!isVipOrSvip.value) return true
|
||||||
|
|
||||||
|
// VIP不能降级到普通会员
|
||||||
|
if (isVip.value && selectedType.value === '') return false
|
||||||
|
|
||||||
|
// SVIP不能降级到VIP
|
||||||
|
if (isSvip.value && selectedType.value === 'vip') return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
// 计算按钮类名
|
||||||
|
const buttonClass = computed(() => {
|
||||||
|
const baseClass =
|
||||||
|
'w-full py-4 rounded-lg font-bold text-lg shadow-lg transform transition-transform scale-btn relative overflow-hidden'
|
||||||
|
|
||||||
|
if (!canPerformAction.value) {
|
||||||
|
return `${baseClass} bg-gray-400 text-white cursor-not-allowed`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isVip.value && selectedType.value === 'svip') {
|
||||||
|
return `${baseClass} bg-gradient-to-r from-purple-500 to-indigo-600 text-white`
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${baseClass} bg-gradient-to-r from-amber-500 to-amber-600 active:from-amber-600 active:to-amber-700 text-white`
|
||||||
|
})
|
||||||
|
|
||||||
|
// VIP价格配置
|
||||||
|
const vipConfig = reactive({
|
||||||
|
price: 399, // VIP价格
|
||||||
|
svipPrice: 599, // SVIP价格
|
||||||
|
priceUnit: '元/年',
|
||||||
|
vipCommission: 1.2, // VIP下级贡献收益(元/单)
|
||||||
|
svipCommission: 1.5, // SVIP下级贡献收益(元/单)
|
||||||
|
vipFloatingRate: 5, // VIP下级价格浮动收益率(%)
|
||||||
|
svipFloatingRate: 10, // SVIP下级价格浮动收益率(%)
|
||||||
|
vipSingleOrderProfit: 5, // VIP单个订单最高利润(元)
|
||||||
|
svipSingleOrderProfit: 10, // SVIP单个订单最高利润(元)
|
||||||
|
withdrawRatio: 1, // 下级提现奖励比率(%)
|
||||||
|
monthlyRewardForTeam: 50, // 月度团队奖励(元)
|
||||||
|
monthlyRewardForNewTeam: 50, // 月度新增团队奖励(元)
|
||||||
|
vipConversionBonus: 299, // VIP下级转化奖励(元)
|
||||||
|
svipConversionBonus: 399, // SVIP下级转化奖励(元)
|
||||||
|
vipWithdrawalLimit: 1500, // VIP提现额度(元)
|
||||||
|
svipWithdrawalLimit: 3000, // SVIP提现额度(元)
|
||||||
|
vipWithdrawalTimes: 1, // VIP日提现次数
|
||||||
|
svipWithdrawalTimes: 2, // SVIP日提现次数
|
||||||
|
})
|
||||||
|
|
||||||
|
// 权益对比表数据
|
||||||
|
const benefitsComparisonData = computed(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
benefit: '会员权益',
|
||||||
|
normal: '普通代理 免费',
|
||||||
|
vip: `${vipConfig.price}${vipConfig.priceUnit}`,
|
||||||
|
svip: `${vipConfig.svipPrice}${vipConfig.priceUnit}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
benefit: '下级贡献收益',
|
||||||
|
normal: '1元/单',
|
||||||
|
vip: `${vipConfig.vipCommission}元/单`,
|
||||||
|
svip: `${vipConfig.svipCommission}元/单`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
benefit: '自定义设置下级成本',
|
||||||
|
normal: '❌',
|
||||||
|
vip: '✓',
|
||||||
|
svip: '✓'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
benefit: '下级价格浮动收益',
|
||||||
|
normal: '❌',
|
||||||
|
vip: `最高${vipConfig.vipFloatingRate}%`,
|
||||||
|
svip: `最高${vipConfig.svipFloatingRate}%`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
benefit: '下级提现奖励',
|
||||||
|
normal: '❌',
|
||||||
|
vip: '❌',
|
||||||
|
svip: `${vipConfig.withdrawRatio}%`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
benefit: '下级活跃奖励',
|
||||||
|
normal: '❌',
|
||||||
|
vip: `${vipConfig.monthlyRewardForTeam}元/月`,
|
||||||
|
svip: `${vipConfig.monthlyRewardForTeam}元/月`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
benefit: '新增活跃奖励',
|
||||||
|
normal: '❌',
|
||||||
|
vip: `${vipConfig.monthlyRewardForNewTeam}元/月`,
|
||||||
|
svip: `${vipConfig.monthlyRewardForNewTeam}元/月`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
benefit: '下级转化奖励',
|
||||||
|
normal: '❌',
|
||||||
|
vip: `${vipConfig.vipConversionBonus}元*10个`,
|
||||||
|
svip: `${vipConfig.svipConversionBonus}元*10个`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
benefit: '提现次数额度',
|
||||||
|
normal: '800元/次',
|
||||||
|
vip: `${vipConfig.vipWithdrawalLimit}元/次`,
|
||||||
|
svip: `${vipConfig.svipWithdrawalLimit}元/次`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
benefit: '提现次数',
|
||||||
|
normal: '1次/日',
|
||||||
|
vip: '1次/日',
|
||||||
|
svip: '2次/日'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
// 收益对比表数据
|
||||||
|
const revenueComparisonData = computed(() => {
|
||||||
|
const revenue = revenueData.value
|
||||||
|
const withdrawReward = 20000 * (vipConfig.withdrawRatio / 100)
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
source: '推广收益(月)',
|
||||||
|
vip: '300单×50元=15,000元',
|
||||||
|
svip: '300单×50元=15,000元'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: '下级贡献收益(月)',
|
||||||
|
vip: `300单×${vipConfig.vipCommission}元=360元`,
|
||||||
|
svip: `300单×${vipConfig.svipCommission}元=450元`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: '下级价格浮动收益(月)',
|
||||||
|
vip: `100单×100元×${vipConfig.vipFloatingRate}%=500元`,
|
||||||
|
svip: `200单×100元×${vipConfig.svipFloatingRate}%=2,000元`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: '下级提现奖励(月)',
|
||||||
|
vip: '-',
|
||||||
|
svip: `${withdrawReward}元`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: '下级活跃奖励(月)',
|
||||||
|
vip: `${vipConfig.monthlyRewardForTeam}元`,
|
||||||
|
svip: `${vipConfig.monthlyRewardForTeam}元`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: '新增活跃奖励(月)',
|
||||||
|
vip: `${vipConfig.monthlyRewardForNewTeam}元`,
|
||||||
|
svip: `${vipConfig.monthlyRewardForNewTeam}元`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: '下级转化奖励(月)',
|
||||||
|
vip: `${vipConfig.vipConversionBonus}元×2个=598元`,
|
||||||
|
svip: `${vipConfig.svipConversionBonus}元×2个=798元`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: '额外业务收益(月)',
|
||||||
|
vip: '约3,000元',
|
||||||
|
svip: '约6,000元'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: '月计收益',
|
||||||
|
vip: `${revenue.vipMonthly}元`,
|
||||||
|
svip: `${revenue.svipMonthly}元`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: '年计收益',
|
||||||
|
vip: `${revenue.vipYearly}元`,
|
||||||
|
svip: `${revenue.svipYearly}元`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
// 计算得出的收益数据
|
||||||
|
const revenueData = computed(() => {
|
||||||
|
const baseOrders = 300 // 基础订单数
|
||||||
|
const pricePerOrder = 50 // 每单价格
|
||||||
|
const baseRevenue = baseOrders * pricePerOrder // 基础推广收益
|
||||||
|
|
||||||
|
const vipCommissionRevenue = baseOrders * vipConfig.vipCommission // VIP下级贡献收益
|
||||||
|
const svipCommissionRevenue = baseOrders * vipConfig.svipCommission // SVIP下级贡献收益
|
||||||
|
|
||||||
|
const vipFloatingRevenue = 100 * 100 * (vipConfig.vipFloatingRate / 100) // VIP浮动收益
|
||||||
|
const svipFloatingRevenue = 200 * 100 * (vipConfig.svipFloatingRate / 100) // SVIP浮动收益
|
||||||
|
|
||||||
|
const vipConversionRevenue = vipConfig.vipConversionBonus * 2 // VIP转化奖励
|
||||||
|
const svipConversionRevenue = vipConfig.svipConversionBonus * 2 // SVIP转化奖励
|
||||||
|
|
||||||
|
const vipExtraRevenue = 3000 // VIP额外收益估计
|
||||||
|
const svipExtraRevenue = 6000 // SVIP额外收益估计
|
||||||
|
|
||||||
|
// 平级提现奖励(只有SVIP才有)
|
||||||
|
const withdrawReward = 20000 * (vipConfig.withdrawRatio / 100)
|
||||||
|
|
||||||
|
// 计算月总收益
|
||||||
|
const vipMonthlyTotal =
|
||||||
|
baseRevenue +
|
||||||
|
vipCommissionRevenue +
|
||||||
|
vipFloatingRevenue +
|
||||||
|
vipConfig.monthlyRewardForTeam +
|
||||||
|
vipConfig.monthlyRewardForNewTeam +
|
||||||
|
vipConversionRevenue +
|
||||||
|
vipExtraRevenue
|
||||||
|
|
||||||
|
const svipMonthlyTotal =
|
||||||
|
baseRevenue +
|
||||||
|
svipCommissionRevenue +
|
||||||
|
svipFloatingRevenue +
|
||||||
|
withdrawReward +
|
||||||
|
vipConfig.monthlyRewardForTeam +
|
||||||
|
vipConfig.monthlyRewardForNewTeam +
|
||||||
|
svipConversionRevenue +
|
||||||
|
svipExtraRevenue
|
||||||
|
|
||||||
|
// 计算VIP和SVIP之间的差额
|
||||||
|
const monthlyDifference = svipMonthlyTotal - vipMonthlyTotal
|
||||||
|
const priceDifference = vipConfig.svipPrice - vipConfig.price
|
||||||
|
|
||||||
|
return {
|
||||||
|
vipMonthly: Math.round(vipMonthlyTotal),
|
||||||
|
svipMonthly: Math.round(svipMonthlyTotal),
|
||||||
|
vipYearly: Math.round(vipMonthlyTotal * 12),
|
||||||
|
svipYearly: Math.round(svipMonthlyTotal * 12),
|
||||||
|
monthlyDifference: Math.round(monthlyDifference),
|
||||||
|
yearlyDifference: Math.round(monthlyDifference * 12),
|
||||||
|
vipRate: Math.round(vipMonthlyTotal / vipConfig.price),
|
||||||
|
svipRate: Math.round(svipMonthlyTotal / vipConfig.svipPrice),
|
||||||
|
priceDifference,
|
||||||
|
recoverDays: Math.ceil(priceDifference / (monthlyDifference / 30)),
|
||||||
|
withdrawReward,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 初始化代理数据和价格配置
|
||||||
|
onMounted(async () => {
|
||||||
|
// 从本地缓存获取代理信息
|
||||||
|
loadAgentInfo()
|
||||||
|
|
||||||
|
// 模拟API请求获取价格
|
||||||
|
try {
|
||||||
|
// 未来可以在这里添加实际的API调用
|
||||||
|
// const response = await uni.request({...});
|
||||||
|
// 使用API返回的数据更新vipConfig
|
||||||
|
|
||||||
|
console.log('价格配置已初始化,未来将从API加载')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载价格配置失败', error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 从本地缓存加载代理信息
|
||||||
|
const loadAgentInfo = () => {
|
||||||
|
try {
|
||||||
|
const agentInfo = uni.getStorageSync('agentInfo')
|
||||||
|
if (agentInfo) {
|
||||||
|
level.value = agentInfo.level || 'normal'
|
||||||
|
ExpiryTime.value = agentInfo.expiryTime || ''
|
||||||
|
isAgent.value = agentInfo.isAgent || false
|
||||||
|
agentStatus.value = agentInfo.status || 0
|
||||||
|
agentID.value = agentInfo.agentID || ''
|
||||||
|
mobile.value = agentInfo.mobile || ''
|
||||||
|
isRealName.value = agentInfo.isRealName || false
|
||||||
|
|
||||||
|
console.log('代理信息已加载:', agentInfo)
|
||||||
|
} else {
|
||||||
|
console.log('未找到代理信息缓存')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载代理信息失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedType = ref('vip') // 默认选择VIP
|
||||||
|
const showPayment = ref(false)
|
||||||
|
const payData = ref({
|
||||||
|
product_name: `${selectedType.value.toUpperCase()}代理`,
|
||||||
|
sell_price: vipConfig.price,
|
||||||
|
})
|
||||||
|
const payID = ref('')
|
||||||
|
|
||||||
|
// 选择代理类型
|
||||||
|
function selectType(type) {
|
||||||
|
selectedType.value = type
|
||||||
|
// 更新payData中的价格和产品名称
|
||||||
|
payData.value = {
|
||||||
|
product_name: `${type === 'vip' ? 'VIP' : 'SVIP'}代理`,
|
||||||
|
sell_price: type === 'vip' ? vipConfig.price : vipConfig.svipPrice,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 申请VIP或SVIP
|
||||||
|
async function applyVip() {
|
||||||
|
// 如果是VIP想升级到SVIP,提示联系客服
|
||||||
|
if (isVip.value && selectedType.value === 'svip') {
|
||||||
|
contactService()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是SVIP要降级到VIP,提示不能降级
|
||||||
|
if (isSvip.value && selectedType.value === 'vip') {
|
||||||
|
toast.error('SVIP会员不能降级到VIP会员')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await activateAgentMembership({
|
||||||
|
type: selectedType.value.toUpperCase(),
|
||||||
|
})
|
||||||
|
|
||||||
|
if (res.code === 200) {
|
||||||
|
if (res.data.id) {
|
||||||
|
payID.value = res.data.id
|
||||||
|
showPayment.value = true
|
||||||
|
uni.requestPayment({
|
||||||
|
"provider": "wxpay",
|
||||||
|
"orderInfo": {
|
||||||
|
"appid": "wx499********7c70e", // 微信开放平台 - 应用 - AppId,注意和微信小程序、公众号 AppId 可能不一致
|
||||||
|
"noncestr": "c5sEwbaNPiXAF3iv", // 随机字符串
|
||||||
|
"package": "Sign=WXPay", // 固定值
|
||||||
|
"partnerid": "148*****52", // 微信支付商户号
|
||||||
|
"prepayid": "wx202254********************fbe90000", // 统一下单订单号
|
||||||
|
"timestamp": 1597935292, // 时间戳(单位:秒)
|
||||||
|
"sign": "A842B45937F6EFF60DEC7A2EAA52D5A0" // 签名,这里用的 MD5/RSA 签名
|
||||||
|
},
|
||||||
|
success(res) { },
|
||||||
|
fail(e) { }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
toast.error(res.msg || '申请失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('申请VIP失败:', error)
|
||||||
|
toast.error('网络请求失败,请稍后重试')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatExpiryTime(expiryTimeStr) {
|
||||||
|
if (!expiryTimeStr) return '未知'
|
||||||
|
// 从格式化字符串中提取日期部分
|
||||||
|
return expiryTimeStr.split(' ')[0] // 假设格式是 "YYYY-MM-DD HH:MM:SS"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.agent-VIP-apply {
|
||||||
|
font-family:
|
||||||
|
system-ui,
|
||||||
|
-apple-system,
|
||||||
|
BlinkMacSystemFont,
|
||||||
|
'Segoe UI',
|
||||||
|
Roboto,
|
||||||
|
sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shimmer {
|
||||||
|
0% {
|
||||||
|
transform: translateX(-100%) skewX(-30deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translateX(200%) skewX(-30deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-shimmer {
|
||||||
|
animation: shimmer 3s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scale-btn:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<route type="page" lang="json">{
|
||||||
|
"layout": "page",
|
||||||
|
"title": "开通代理会员",
|
||||||
|
"agent": true,
|
||||||
|
"auth": true
|
||||||
|
}</route>
|
||||||
513
src/pages/agentVipConfig.vue
Normal file
@@ -0,0 +1,513 @@
|
|||||||
|
<template>
|
||||||
|
<view class="p-4 mx-auto min-h-screen">
|
||||||
|
<!-- 标题部分 -->
|
||||||
|
<view class="card mb-4 p-4 bg-gradient-to-r from-blue-500 to-blue-600 rounded-lg shadow-lg text-white">
|
||||||
|
<text class="text-2xl font-extrabold mb-2 block">专业报告定价配置</text>
|
||||||
|
<text class="opacity-90 block">请选择报告类型并设置定价策略,助您实现精准定价</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="mb-4 bg-white rounded-lg overflow-hidden px-4 flex items-center justify-between">
|
||||||
|
<span class="text-blue-600 font-medium text-sm">📝 选择报告</span>
|
||||||
|
<wd-picker custom-class="flex-1" v-model="selectedReportText" :columns="reportOptions" title="选择报告类型"
|
||||||
|
@confirm="onConfirm" />
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-if="selectedReportText" class="space-y-6">
|
||||||
|
<!-- 配置卡片 -->
|
||||||
|
<view class="card">
|
||||||
|
<!-- 当前报告标题 -->
|
||||||
|
<view class="flex items-center mb-6">
|
||||||
|
<text class="text-xl font-semibold text-gray-800">
|
||||||
|
{{ selectedReportText }}配置
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 显示当前产品的基础成本信息 -->
|
||||||
|
<view v-if="productConfigData && productConfigData.cost_price"
|
||||||
|
class="px-4 py-2 mb-4 bg-gray-50 border border-gray-200 rounded-lg shadow-sm">
|
||||||
|
<text class="text-lg font-semibold text-gray-700 block">报告基础配置信息</text>
|
||||||
|
<view class="mt-1 text-sm text-gray-600">
|
||||||
|
<text class="block">基础成本价:<text class="font-medium">{{ productConfigData.cost_price }}</text> 元</text>
|
||||||
|
<text class="block">最高设定金额上限:<text class="font-medium">{{ productConfigData.price_range_max }}</text>
|
||||||
|
元</text>
|
||||||
|
<text class="block">最高设定比例上限:<text class="font-medium">{{ priceRatioMax }}</text> %</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 分隔线 -->
|
||||||
|
<view class="my-6 flex items-center justify-center">
|
||||||
|
<view class="bg-gray-200 h-px flex-1"></view>
|
||||||
|
<text class="mx-2 text-gray-400 text-sm">成本策略配置</text>
|
||||||
|
<view class="bg-gray-200 h-px flex-1"></view>
|
||||||
|
</view>
|
||||||
|
<!-- 表单部分 -->
|
||||||
|
<wd-form>
|
||||||
|
<!-- 加价金额 -->
|
||||||
|
|
||||||
|
<wd-form-item label="加价金额" prop="price_increase_amount">
|
||||||
|
<wd-input v-model="configData.price_increase_amount" type="number" placeholder="0"
|
||||||
|
@blur="validateDecimal('price_increase_amount')" />
|
||||||
|
</wd-form-item>
|
||||||
|
<view class="text-xs text-gray-400 mt-1">
|
||||||
|
<text class="block">提示:最大加价金额为{{ priceIncreaseAmountMax }}元</text>
|
||||||
|
<text class="block">说明:加价金额是在基础成本价上增加的额外费用,决定下级报告的最低定价,您将获得所有输入的金额利润。</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 分隔线 -->
|
||||||
|
<view class="my-6 flex items-center justify-center">
|
||||||
|
<view class="bg-gray-200 h-px flex-1"></view>
|
||||||
|
<text class="mx-2 text-gray-400 text-sm">定价策略配置</text>
|
||||||
|
<view class="bg-gray-200 h-px flex-1"></view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 定价区间最低 -->
|
||||||
|
<wd-form-item label="定价区间最低" prop="price_range_from">
|
||||||
|
<wd-input v-model="configData.price_range_from" type="number" placeholder="0"
|
||||||
|
@blur="() => { validateDecimal('price_range_from'); validateRange(); }" />
|
||||||
|
</wd-form-item>
|
||||||
|
<view class="text-xs text-gray-400 mt-1">
|
||||||
|
<text class="block">提示:定价区间最低金额不能低于(基础最低 {{ productConfigData?.price_range_min || 0 }}元 + 加价金额)</text>
|
||||||
|
<text class="block">说明:设定的定价区间最低金额为定价区间的起始值,若下级设定的报告金额在区间内,则区间内部分将按比例获得收益。</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 定价区间最高 -->
|
||||||
|
<wd-form-item label="定价区间最高" prop="price_range_to">
|
||||||
|
<wd-input v-model="configData.price_range_to" type="number" placeholder="0"
|
||||||
|
@blur="() => { validateDecimal('price_range_to'); validateRange(); }" />
|
||||||
|
</wd-form-item>
|
||||||
|
<view class="text-xs text-gray-400 mt-1">
|
||||||
|
<text class="block">提示:定价区间最高金额不能超过上限({{ productConfigData?.price_range_max || 0 }}元)和大于定价区间最低金额({{
|
||||||
|
priceIncreaseMax
|
||||||
|
}}元)</text>
|
||||||
|
<text class="block">说明:设定的定价区间最高金额为定价区间的结束值,若下级设定的报告金额在区间内,则区间内部分将按比例获得收益。</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 收取比例 -->
|
||||||
|
<wd-form-item label="收取比例" prop="price_ratio">
|
||||||
|
<wd-input v-model="configData.price_ratio" type="number" placeholder="0" @blur="validateRatio" />
|
||||||
|
</wd-form-item>
|
||||||
|
<view class="text-xs text-gray-400 mt-1">
|
||||||
|
<text class="block">提示:最大收取比例为{{ priceRatioMax }}%</text>
|
||||||
|
<text class="block">说明:收取比例表示对定价区间内(即报告金额超过最低金额,小于最高金额的部分)的金额,按此比例进行利润分成。</text>
|
||||||
|
</view>
|
||||||
|
</wd-form>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 保存按钮 -->
|
||||||
|
<button type="primary" class="bg-blue-500 text-white py-1 rounded-xl w-full" @click="handleSubmit">
|
||||||
|
保存当前报告配置
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 未选择提示 -->
|
||||||
|
<view v-else class="text-center py-12">
|
||||||
|
<text class="text-gray-400 text-4xl block mb-4">⚠️</text>
|
||||||
|
<text class="text-gray-500 block">请先选择需要配置的报告类型</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, computed, onMounted, watch } from 'vue'
|
||||||
|
import { getAgentMembershipUserConfig, saveAgentMembershipUserConfig } from '@/apis/agent'
|
||||||
|
|
||||||
|
// 报告类型选项
|
||||||
|
const reportOptions = [
|
||||||
|
{ label: '人事背调', value: 1 },
|
||||||
|
{ label: '老板企业报告', value: 2 },
|
||||||
|
{ label: '家政风险', value: 3 },
|
||||||
|
{ label: '婚恋风险', value: 4 },
|
||||||
|
{ label: '贷前背调', value: 5 },
|
||||||
|
{ label: '租赁风险', value: 6 },
|
||||||
|
{ label: '个人风险', value: 7 },
|
||||||
|
]
|
||||||
|
|
||||||
|
// 状态管理
|
||||||
|
const showPicker = ref(false)
|
||||||
|
const selectedReportId = ref(1)
|
||||||
|
const selectedReportText = ref('人事背调')
|
||||||
|
|
||||||
|
const configData = ref({})
|
||||||
|
const productConfigData = ref({})
|
||||||
|
const priceIncreaseMax = ref(null)
|
||||||
|
const priceIncreaseAmountMax = ref(null)
|
||||||
|
const priceRatioMax = ref(null)
|
||||||
|
const rangeError = ref(false)
|
||||||
|
const ratioError = ref(false)
|
||||||
|
const increaseError = ref(false)
|
||||||
|
|
||||||
|
// 金额输入格式验证:确保最多两位小数
|
||||||
|
const validateDecimal = (field) => {
|
||||||
|
console.log(`validateDecimal开始: field=${field}, 值=${configData.value[field]}`)
|
||||||
|
const value = configData.value[field]
|
||||||
|
if (value === null || value === undefined) {
|
||||||
|
console.log(`validateDecimal: ${field}为空,退出验证`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const numValue = Number(value)
|
||||||
|
if (isNaN(numValue)) {
|
||||||
|
console.log(`validateDecimal: ${field}无法转换为数字,设置为null`)
|
||||||
|
configData.value[field] = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const fixedValue = parseFloat(numValue.toFixed(2))
|
||||||
|
console.log(`validateDecimal: ${field}原值=${numValue},处理后=${fixedValue}`)
|
||||||
|
configData.value[field] = fixedValue
|
||||||
|
|
||||||
|
if (field === 'price_increase_amount') {
|
||||||
|
console.log(`validateDecimal: 检查加价金额上限 ${fixedValue} vs ${priceIncreaseAmountMax.value}`)
|
||||||
|
if (fixedValue > priceIncreaseAmountMax.value) {
|
||||||
|
configData.value[field] = priceIncreaseAmountMax.value
|
||||||
|
console.log(`validateDecimal: 加价金额超过上限,已修正为${priceIncreaseAmountMax.value}`)
|
||||||
|
uni.showToast({
|
||||||
|
title: `加价金额最大为${priceIncreaseAmountMax.value}元`,
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
increaseError.value = true
|
||||||
|
setTimeout(() => {
|
||||||
|
increaseError.value = false
|
||||||
|
}, 2000)
|
||||||
|
} else {
|
||||||
|
increaseError.value = false
|
||||||
|
}
|
||||||
|
// 当加价金额改变后,重新验证价格区间
|
||||||
|
validateRange()
|
||||||
|
}
|
||||||
|
console.log(`validateDecimal结束: ${field}最终值=${configData.value[field]}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 价格区间验证
|
||||||
|
const validateRange = () => {
|
||||||
|
console.log('validateRange开始:',
|
||||||
|
`最低=${configData.value.price_range_from}`,
|
||||||
|
`最高=${configData.value.price_range_to}`)
|
||||||
|
|
||||||
|
// if (configData.value.price_range_from === null || configData.value.price_range_to === null) {
|
||||||
|
// console.log('validateRange: 价格区间值为null,退出验证')
|
||||||
|
// rangeError.value = false
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (isNaN(configData.value.price_range_from) || isNaN(configData.value.price_range_to)) {
|
||||||
|
console.log('validateRange: 价格区间值非数字,退出验证')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const additional = configData.value.price_increase_amount || 0
|
||||||
|
console.log(`validateRange: 加价金额=${additional}`)
|
||||||
|
|
||||||
|
const minAllowed = parseFloat(
|
||||||
|
(Number(productConfigData.value.cost_price) + Number(additional)).toFixed(2)
|
||||||
|
) // 使用成本价作为最小值
|
||||||
|
const maxAllowed = productConfigData.value.price_range_max // 使用产品配置中的最大价格作为最大值
|
||||||
|
|
||||||
|
console.log(`validateRange: 最低允许=${minAllowed}, 最高允许=${maxAllowed}`)
|
||||||
|
|
||||||
|
// 检查最低金额
|
||||||
|
if (configData.value.price_range_from < minAllowed) {
|
||||||
|
console.log(`validateRange: 定价区间最低金额(${configData.value.price_range_from})小于允许最低值(${minAllowed}),进行修正`)
|
||||||
|
configData.value.price_range_from = minAllowed
|
||||||
|
uni.showToast({
|
||||||
|
title: `定价区间最低金额不能低于成本价 ${minAllowed}元`,
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
rangeError.value = true
|
||||||
|
closeRangeError()
|
||||||
|
|
||||||
|
configData.value.price_range_to = parseFloat(
|
||||||
|
(Number(configData.value.price_range_from) + Number(priceIncreaseMax.value)).toFixed(2)
|
||||||
|
)
|
||||||
|
console.log(`validateRange: 已调整最高金额为 ${configData.value.price_range_to}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查最高金额是否小于最低金额
|
||||||
|
if (configData.value.price_range_to < configData.value.price_range_from) {
|
||||||
|
console.log(`validateRange: 定价区间最高金额(${configData.value.price_range_to})小于最低金额(${configData.value.price_range_from}),进行修正`)
|
||||||
|
uni.showToast({
|
||||||
|
title: '定价区间最高金额不能低于定价区间最低金额',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
if (configData.value.price_range_from + priceIncreaseMax.value > maxAllowed) {
|
||||||
|
configData.value.price_range_to = maxAllowed
|
||||||
|
console.log(`validateRange: 最高值已修正为最大允许值 ${maxAllowed}`)
|
||||||
|
} else {
|
||||||
|
configData.value.price_range_to = configData.value.price_range_from + priceIncreaseMax.value
|
||||||
|
console.log(`validateRange: 最高值已修正为最低金额+最大增加值 ${configData.value.price_range_to}`)
|
||||||
|
}
|
||||||
|
rangeError.value = true
|
||||||
|
closeRangeError()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查差值
|
||||||
|
const diff = parseFloat(
|
||||||
|
(configData.value.price_range_to - configData.value.price_range_from).toFixed(2)
|
||||||
|
)
|
||||||
|
console.log(`validateRange: 价格区间差值=${diff}, 最大允许差值=${priceIncreaseMax.value}`)
|
||||||
|
|
||||||
|
if (diff > priceIncreaseMax.value) {
|
||||||
|
console.log(`validateRange: 价格区间差值超过最大允许值,进行修正`)
|
||||||
|
uni.showToast({
|
||||||
|
title: `价格区间最大差值为${priceIncreaseMax.value}元`,
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
configData.value.price_range_to = parseFloat(
|
||||||
|
(Number(configData.value.price_range_from) + Number(priceIncreaseMax.value)).toFixed(2)
|
||||||
|
)
|
||||||
|
console.log(`validateRange: 已调整最高金额为 ${configData.value.price_range_to}`)
|
||||||
|
closeRangeError()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查最高金额是否超过上限
|
||||||
|
if (configData.value.price_range_to > maxAllowed) {
|
||||||
|
console.log(`validateRange: 定价区间最高金额(${configData.value.price_range_to})超过上限(${maxAllowed}),进行修正`)
|
||||||
|
configData.value.price_range_to = maxAllowed
|
||||||
|
uni.showToast({
|
||||||
|
title: `定价区间最高金额不能超过 ${maxAllowed}元`,
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
closeRangeError()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rangeError.value) {
|
||||||
|
rangeError.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('validateRange结束:',
|
||||||
|
`最终最低=${configData.value.price_range_from}`,
|
||||||
|
`最终最高=${configData.value.price_range_to}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 收取比例验证(修改为保留两位小数,不再四舍五入取整)
|
||||||
|
const validateRatio = () => {
|
||||||
|
console.log(`validateRatio开始: 值=${configData.value.price_ratio}`)
|
||||||
|
let value = configData.value.price_ratio
|
||||||
|
if (value === null || value === undefined) {
|
||||||
|
console.log('validateRatio: 值为空,退出验证')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const numValue = Number(value)
|
||||||
|
if (isNaN(numValue)) {
|
||||||
|
console.log('validateRatio: 值无法转换为数字,设置为null')
|
||||||
|
configData.value.price_ratio = null
|
||||||
|
ratioError.value = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`validateRatio: 检查比例范围 ${numValue} vs 最大值${priceRatioMax.value}`)
|
||||||
|
if (numValue > priceRatioMax.value) {
|
||||||
|
console.log(`validateRatio: 比例超过最大值,已修正为${priceRatioMax.value}`)
|
||||||
|
configData.value.price_ratio = priceRatioMax.value
|
||||||
|
uni.showToast({
|
||||||
|
title: `收取比例最大为${priceRatioMax.value}%`,
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
ratioError.value = true
|
||||||
|
setTimeout(() => {
|
||||||
|
ratioError.value = false
|
||||||
|
}, 1000)
|
||||||
|
} else if (numValue < 0) {
|
||||||
|
console.log('validateRatio: 比例小于0,已修正为0')
|
||||||
|
configData.value.price_ratio = 0
|
||||||
|
ratioError.value = true
|
||||||
|
} else {
|
||||||
|
configData.value.price_ratio = parseFloat(numValue.toFixed(2))
|
||||||
|
ratioError.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`validateRatio结束: 最终值=${configData.value.price_ratio}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取配置
|
||||||
|
const getConfig = async () => {
|
||||||
|
try {
|
||||||
|
console.log(`getConfig开始: 获取产品ID=${selectedReportId.value}的配置`)
|
||||||
|
const res = await getAgentMembershipUserConfig({ product_id: selectedReportId.value })
|
||||||
|
if (res.code === 200) {
|
||||||
|
const respConfigData = res.data.agent_membership_user_config
|
||||||
|
console.log("respConfigData", respConfigData)
|
||||||
|
configData.value = {
|
||||||
|
id: respConfigData.product_id,
|
||||||
|
price_range_from: respConfigData.price_range_from || null,
|
||||||
|
price_range_to: respConfigData.price_range_to || null,
|
||||||
|
price_ratio: respConfigData.price_ratio * 100 || null, // 转换为百分比
|
||||||
|
price_increase_amount: respConfigData.price_increase_amount || null,
|
||||||
|
}
|
||||||
|
|
||||||
|
productConfigData.value = res.data.product_config
|
||||||
|
// 设置动态限制值
|
||||||
|
priceIncreaseMax.value = res.data.price_increase_max
|
||||||
|
priceIncreaseAmountMax.value = res.data.price_increase_amount
|
||||||
|
priceRatioMax.value = res.data.price_ratio * 100
|
||||||
|
|
||||||
|
console.log('getConfig: 配置加载成功',
|
||||||
|
`最大差值=${priceIncreaseMax.value}`,
|
||||||
|
`最大加价=${priceIncreaseAmountMax.value}`,
|
||||||
|
`最大比例=${priceRatioMax.value}%`)
|
||||||
|
console.log('getConfig: 当前配置', configData.value)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("getConfig错误:", error)
|
||||||
|
uni.showToast({
|
||||||
|
title: '配置加载失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交处理
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
try {
|
||||||
|
if (!finalValidation()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 前端数据转换
|
||||||
|
const submitData = {
|
||||||
|
product_id: configData.value.id,
|
||||||
|
price_range_from: configData.value.price_range_from || 0,
|
||||||
|
price_range_to: configData.value.price_range_to || 0,
|
||||||
|
price_ratio: (configData.value.price_ratio || 0) / 100, // 转换为小数
|
||||||
|
price_increase_amount: configData.value.price_increase_amount || 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await saveAgentMembershipUserConfig(submitData)
|
||||||
|
if (res.code === 200) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '保存成功',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
getConfig()
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("handleSubmit错误:", error)
|
||||||
|
uni.showToast({
|
||||||
|
title: '保存失败,请稍后重试',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最终验证函数
|
||||||
|
const finalValidation = () => {
|
||||||
|
// 校验最低金额:不能为空且大于0
|
||||||
|
if (!configData.value.price_range_from || configData.value.price_range_from <= 0) {
|
||||||
|
uni.showToast({
|
||||||
|
title: "定价区间最低金额不能为空",
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// 校验最高金额:不能为空且大于0
|
||||||
|
if (!configData.value.price_range_to || configData.value.price_range_to <= 0) {
|
||||||
|
uni.showToast({
|
||||||
|
title: "定价区间最高金额不能为空",
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// 校验收取比例:不能为空且大于0
|
||||||
|
if (!configData.value.price_ratio || configData.value.price_ratio <= 0) {
|
||||||
|
uni.showToast({
|
||||||
|
title: "收取比例不能为空",
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// 验证最低金额必须小于最高金额
|
||||||
|
if (configData.value.price_range_from >= configData.value.price_range_to) {
|
||||||
|
uni.showToast({
|
||||||
|
title: "定价区间最低金额必须小于定价区间最高金额",
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// 验证价格区间差值不能超过最大允许差值
|
||||||
|
const finalDiff = parseFloat(
|
||||||
|
(configData.value.price_range_to - configData.value.price_range_from).toFixed(2)
|
||||||
|
)
|
||||||
|
if (finalDiff > priceIncreaseMax.value) {
|
||||||
|
uni.showToast({
|
||||||
|
title: `价格区间最大差值为${priceIncreaseMax.value}元`,
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// 验证最高金额不能超过产品配置中设定的上限
|
||||||
|
if (configData.value.price_range_to > productConfigData.value.price_range_max) {
|
||||||
|
uni.showToast({
|
||||||
|
title: `定价区间最高金额不能超过${productConfigData.value.price_range_max}元`,
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// 验证最低金额不能低于成本价+加价金额(加价金额允许为空)
|
||||||
|
const additional = configData.value.price_increase_amount || 0
|
||||||
|
if (configData.value.price_range_from < productConfigData.value.cost_price + additional) {
|
||||||
|
uni.showToast({
|
||||||
|
title: `定价区间最低金额不能低于成本价${productConfigData.value.cost_price + additional}元`,
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择器确认
|
||||||
|
const onConfirm = (e) => {
|
||||||
|
const { selectedItems } = e
|
||||||
|
selectedReportId.value = selectedItems.value
|
||||||
|
selectedReportText.value = selectedItems.label
|
||||||
|
showPicker.value = false
|
||||||
|
|
||||||
|
// 重置错误状态
|
||||||
|
rangeError.value = false
|
||||||
|
ratioError.value = false
|
||||||
|
increaseError.value = false
|
||||||
|
|
||||||
|
getConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeRangeError = () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
rangeError.value = false
|
||||||
|
}, 2000)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getConfig()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.card {
|
||||||
|
border-radius: 24rpx;
|
||||||
|
background-color: white;
|
||||||
|
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.05);
|
||||||
|
padding: 32rpx;
|
||||||
|
margin-bottom: 32rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.space-y-6 {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.space-y-6 > view {
|
||||||
|
margin-bottom: 48rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.space-y-6 > view:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<route type="page" lang="json">{
|
||||||
|
"layout": "page",
|
||||||
|
"title": "会员代理报告配置",
|
||||||
|
"agent": true,
|
||||||
|
"auth": true
|
||||||
|
}</route>
|
||||||
72
src/pages/agreement.vue
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
<script setup>
|
||||||
|
const webviewStyles = ref({
|
||||||
|
top: `${uni.getSystemInfoSync().statusBarHeight + 44}px`, // 距离顶部的距离
|
||||||
|
height: `${uni.getSystemInfoSync().windowHeight - uni.getSystemInfoSync().statusBarHeight - 44}px`, // 高度
|
||||||
|
position: 'absolute', // 绝对定位
|
||||||
|
dock: 'bottom', // 停靠在底部
|
||||||
|
bounce: 'vertical', // 垂直方向的回弹效果
|
||||||
|
})
|
||||||
|
|
||||||
|
// 从环境变量获取基础URL
|
||||||
|
const BASE_URL = import.meta.env.VITE_APP_BASE_URL || 'https://www.tianyuandb.com'
|
||||||
|
|
||||||
|
// 协议路径和标题的映射
|
||||||
|
const agreementMap = {
|
||||||
|
user: {
|
||||||
|
path: '/app/userAgreement',
|
||||||
|
title: '用户协议'
|
||||||
|
},
|
||||||
|
privacy: {
|
||||||
|
path: '/app/privacyPolicy',
|
||||||
|
title: '隐私政策'
|
||||||
|
},
|
||||||
|
authorization: {
|
||||||
|
path: '/app/authorization',
|
||||||
|
title: '授权书'
|
||||||
|
},
|
||||||
|
service: {
|
||||||
|
path: '/app/agentSerivceAgreement',
|
||||||
|
title: '信息技术服务合同'
|
||||||
|
},
|
||||||
|
manage: {
|
||||||
|
path: '/app/agentManageAgreement',
|
||||||
|
title: '推广方管理制度协议'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const agreementUrl = ref('')
|
||||||
|
const pageTitle = ref('协议')
|
||||||
|
|
||||||
|
// 使用 uniapp 的 onLoad 生命周期钩子获取页面参数
|
||||||
|
onLoad((option) => {
|
||||||
|
const type = option.type || 'user'
|
||||||
|
if (agreementMap[type]) {
|
||||||
|
agreementUrl.value = `${BASE_URL}${agreementMap[type].path}`
|
||||||
|
pageTitle.value = agreementMap[type].title
|
||||||
|
|
||||||
|
// 设置标题 - 此标题会传递给 page 布局中的 wd-navbar
|
||||||
|
uni.setNavigationBarTitle({
|
||||||
|
title: pageTitle.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
function handleClickLeft() {
|
||||||
|
uni.navigateBack()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view>
|
||||||
|
<wd-navbar :title="pageTitle" left-text="返回" placeholder left-arrow safe-area-inset-top fixed @click-left="handleClickLeft" />
|
||||||
|
<web-view :webview-styles="webviewStyles" :src="agreementUrl" />
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
<route type="page" lang="json">{
|
||||||
|
"layout": "page",
|
||||||
|
"title": "协议",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTextStyle": "black",
|
||||||
|
"navigationStyle": "custom",
|
||||||
|
"navigationBarBackgroundColor": "#e3f0ff"
|
||||||
|
}
|
||||||
|
}</route>
|
||||||
123
src/pages/index.vue
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref, onBeforeMount } from 'vue'
|
||||||
|
|
||||||
|
// 引入icon图片
|
||||||
|
import iconCard1 from '/static/image/icon_2.png'
|
||||||
|
import iconCard2 from '/static/image/icon_1.png'
|
||||||
|
|
||||||
|
// 从缓存中获取代理状态
|
||||||
|
const isAgent = ref(false)
|
||||||
|
onBeforeMount(() => {
|
||||||
|
// 从缓存获取代理信息
|
||||||
|
const agentInfo = uni.getStorageSync('agentInfo')
|
||||||
|
if (agentInfo?.isAgent) {
|
||||||
|
isAgent.value = agentInfo.isAgent
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function toInvitation() {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/invitation',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function toPromote() {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/promote',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跳转到帮助中心页面
|
||||||
|
function toHelpCenter() {
|
||||||
|
uni.navigateTo({ url: '/pages/help' })
|
||||||
|
}
|
||||||
|
// 跳转到帮助详情
|
||||||
|
function toHelpDetail(title) {
|
||||||
|
uni.navigateTo({ url: `/pages/helpDetail?title=${encodeURIComponent(title)}` })
|
||||||
|
}
|
||||||
|
function getPhoneNumber(e) {
|
||||||
|
console.log("e", e)
|
||||||
|
console.log(e.detail.code) // 动态令牌
|
||||||
|
console.log(e.detail.errMsg) // 回调信息(成功失败都会返回)
|
||||||
|
console.log(e.detail.errno) // 错误码(失败时返回)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<!-- <button open-type="getPhoneNumber" @getphonenumber="getPhoneNumber">test</button> -->
|
||||||
|
<view class="box-border min-h-screen bg-[#f5faff]">
|
||||||
|
<!-- 顶部Banner,复用原有Banner部分 -->
|
||||||
|
<view class="relative h-[150px] rounded-2xl overflow-hidden m-4">
|
||||||
|
<image class="h-full w-full" src="/static/image/banner.png" />
|
||||||
|
</view>
|
||||||
|
<view class="mt-4 px-4">
|
||||||
|
<view class="text-base font-bold mb-2 text-gray-800">即刻赚佣金</view>
|
||||||
|
<!-- 卡片1:直推报告 -->
|
||||||
|
<view class="rounded-2xl shadow-xl p-0 mb-4 flex items-center card-gradient-1" @click="toPromote">
|
||||||
|
<view class="flex-1 flex flex-col justify-center items-start pl-4 py-4">
|
||||||
|
<view class="text-[20px] font-bold text-blue-700 mb-1">直推报告</view>
|
||||||
|
<view class="text-gray-700 text-xs mb-4">选择所需报告类型,灵活定价,一键分享客户,客户下单即结算,佣金实时到账</view>
|
||||||
|
<wd-button plain>立即推广</wd-button>
|
||||||
|
</view>
|
||||||
|
<image class="w-20 h-20 mr-4 ml-4 my-4" :src="iconCard1" mode="aspectFit" />
|
||||||
|
</view>
|
||||||
|
<!-- 卡片2:邀请下级代理 -->
|
||||||
|
<view class="rounded-2xl shadow-xl p-0 flex items-center card-gradient-2" @click="toInvitation">
|
||||||
|
<view class="flex-1 flex flex-col justify-center items-start pl-4 py-4">
|
||||||
|
<view class="text-[20px] font-bold text-teal-700 mb-1 ">邀请下级代理</view>
|
||||||
|
<view class="text-gray-700 text-xs mb-4">邀请好友成为代理,好友推广获客,客户支付即返佣,团队收益轻松到手</view>
|
||||||
|
<wd-button plain>立即邀请</wd-button>
|
||||||
|
</view>
|
||||||
|
<image class="w-20 h-20 mr-4 ml-4 my-4" :src="iconCard2" mode="aspectFit" />
|
||||||
|
</view>
|
||||||
|
<!-- 帮助中心模块 -->
|
||||||
|
<!-- <view class="mt-6">
|
||||||
|
<view class="flex items-center justify-between mb-2">
|
||||||
|
<view class="font-bold text-base text-gray-800">帮助中心</view>
|
||||||
|
<view class="text-xs text-blue-500 px-2 py-1 rounded cursor-pointer" @click="toHelpCenter">更多</view>
|
||||||
|
</view>
|
||||||
|
<view class="bg-white rounded-2xl shadow p-2 divide-y divide-gray-100">
|
||||||
|
<view class="flex items-center py-3 px-2 justify-between" @click="toHelpDetail('直推报告页面引导')">
|
||||||
|
<view class="text-gray-800 text-sm">直推报告页面引导</view>
|
||||||
|
<view class="text-xs bg-blue-100 text-blue-600 px-2 py-0.5 rounded">引导指南</view>
|
||||||
|
</view>
|
||||||
|
<view class="flex items-center py-3 px-2 justify-between" @click="toHelpDetail('邀请下级页面引导')">
|
||||||
|
<view class="text-gray-800 text-sm">邀请下级页面引导</view>
|
||||||
|
<view class="text-xs bg-blue-100 text-blue-600 px-2 py-0.5 rounded">引导指南</view>
|
||||||
|
</view>
|
||||||
|
<view class="flex items-center py-3 px-2 justify-between" @click="toHelpDetail('如何成为天远数据代理')">
|
||||||
|
<view class="text-gray-800 text-sm">如何成为天远数据代理</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view> -->
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.clip-left {
|
||||||
|
clip-path: polygon(0 0, 0 100%, 90% 100%, 0 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.clip-right {
|
||||||
|
clip-path: polygon(0 0, 0 0, 90% 100%, 0 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-gradient-1 {
|
||||||
|
background: linear-gradient(135deg, #e3f0ff 0%, #fafdff 100%);
|
||||||
|
box-shadow: 0 6px 24px 0 rgba(60, 120, 255, 0.10), 0 1.5px 4px 0 rgba(60, 120, 255, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-gradient-2 {
|
||||||
|
background: linear-gradient(135deg, #e6f7fa 0%, #fafdff 100%);
|
||||||
|
box-shadow: 0 6px 24px 0 rgba(0, 200, 180, 0.10), 0 1.5px 4px 0 rgba(0, 200, 180, 0.08);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<route type="home" lang="json">{
|
||||||
|
"layout": "home",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTextStyle": "black",
|
||||||
|
"navigationStyle": "default",
|
||||||
|
"navigationBarBackgroundColor": "#e3f0ff"
|
||||||
|
}
|
||||||
|
}</route>
|
||||||
52
src/pages/invitation.vue
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<template>
|
||||||
|
<view>
|
||||||
|
<image src="/static/image/invitation.png" alt="邀请下级" mode="widthFix" class="w-full" />
|
||||||
|
<view @click="showQRcode = true"
|
||||||
|
class="bg-gradient-to-t from-orange-500 to-orange-300 fixed bottom-0 h-12 w-full shadow-xl text-white rounded-t-xl flex items-center justify-center font-bold">
|
||||||
|
立即邀请好友
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<QRcode v-model:show="showQRcode" mode="invitation" :linkIdentifier="linkIdentifier" />
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onBeforeMount } from 'vue'
|
||||||
|
import { aesEncrypt } from "@/utils/crypto"
|
||||||
|
import QRcode from '@/components/QRcode.vue'
|
||||||
|
|
||||||
|
|
||||||
|
const showQRcode = ref(false)
|
||||||
|
const linkIdentifier = ref("")
|
||||||
|
const mobile = ref("")
|
||||||
|
const agentID = ref("")
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
// 从UniApp缓存获取用户信息
|
||||||
|
const userInfo = uni.getStorageSync('userInfo') || {}
|
||||||
|
mobile.value = userInfo.mobile || ''
|
||||||
|
agentID.value = userInfo.agentID || ''
|
||||||
|
|
||||||
|
encryptIdentifire(agentID.value, mobile.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
const encryptIdentifire = (agentID, mobile) => {
|
||||||
|
const linkIdentifierJSON = {
|
||||||
|
agentID,
|
||||||
|
mobile
|
||||||
|
}
|
||||||
|
const linkIdentifierStr = JSON.stringify(linkIdentifierJSON)
|
||||||
|
const encodeData = aesEncrypt(linkIdentifierStr, "8e3e7a2f60edb49221e953b9c029ed10")
|
||||||
|
linkIdentifier.value = encodeURIComponent(encodeData)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* 自定义样式 */
|
||||||
|
</style>
|
||||||
|
<route type="page" lang="json">{
|
||||||
|
"layout": "page",
|
||||||
|
"title": "邀请下级",
|
||||||
|
"auth": true,
|
||||||
|
"agent": true
|
||||||
|
}</route>
|
||||||
201
src/pages/invitationAgentApply.vue
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
<template>
|
||||||
|
<view class="min-h-screen bg-[#D1D6FF]">
|
||||||
|
<image src="/static/image/invitation_agent_apply.png" alt="邀请代理申请" mode="widthFix" class="w-full" />
|
||||||
|
|
||||||
|
<!-- 统一状态处理容器 -->
|
||||||
|
<view class="flex flex-col items-center justify-center">
|
||||||
|
<!-- 审核中状态 -->
|
||||||
|
<view v-if="displayStatus === 0" class="text-center">
|
||||||
|
<text class="text-xs text-gray-500 block">您的申请正在审核中</text>
|
||||||
|
<view class="bg-gray-200 p-1 rounded-3xl shadow-xl mt-1">
|
||||||
|
<view class="text-xl font-bold px-8 py-2 bg-gray-400 text-white rounded-3xl shadow-lg cursor-not-allowed">
|
||||||
|
审核进行中
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 审核通过状态 -->
|
||||||
|
<view v-if="displayStatus === 1" class="text-center">
|
||||||
|
<text class="text-xs text-gray-500 block">您已成为认证代理方</text>
|
||||||
|
<view class="bg-green-100 p-1 rounded-3xl shadow-xl mt-1" @click="goToHome">
|
||||||
|
<view
|
||||||
|
class="text-xl font-bold px-8 py-2 bg-gradient-to-t from-green-500 to-green-300 text-white rounded-3xl shadow-lg cursor-pointer">
|
||||||
|
进入应用首页
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 审核未通过状态 -->
|
||||||
|
<view v-if="displayStatus === 2" class="text-center">
|
||||||
|
<text class="text-xs text-red-500 block">审核未通过,请重新提交</text>
|
||||||
|
<view class="bg-red-100 p-1 rounded-3xl shadow-xl mt-1" @click="agentApply">
|
||||||
|
<view
|
||||||
|
class="text-xl font-bold px-8 py-2 bg-gradient-to-t from-red-500 to-red-300 text-white rounded-3xl shadow-lg cursor-pointer">
|
||||||
|
重新提交申请
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 未申请状态(包含邀请状态) -->
|
||||||
|
<view v-if="displayStatus === 3" class="text-center">
|
||||||
|
<text class="text-xs text-gray-500 block">{{ isSelf ? '立即申请成为代理人' : '邀您注册代理人' }}</text>
|
||||||
|
<view class="bg-gray-100 p-1 rounded-3xl shadow-xl mt-1" @click="agentApply">
|
||||||
|
<view
|
||||||
|
class="text-xl font-bold px-8 py-2 bg-gradient-to-t from-blue-500 to-blue-300 text-white rounded-3xl shadow-lg cursor-pointer">
|
||||||
|
立即成为代理方
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<AgentApplicationForm v-model:show="showApplyPopup" @submit="submitApplication" @close="showApplyPopup = false"
|
||||||
|
:ancestor="ancestor" />
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, onBeforeMount } from 'vue'
|
||||||
|
import { getAgentInfo, applyAgent } from '@/apis/agent'
|
||||||
|
import AgentApplicationForm from '@/components/AgentApplicationForm.vue'
|
||||||
|
|
||||||
|
const showApplyPopup = ref(false)
|
||||||
|
const status = ref(3) // 默认为未申请状态
|
||||||
|
const ancestor = ref("")
|
||||||
|
const isSelf = ref(true)
|
||||||
|
let intervalId = null // 保存定时器 ID
|
||||||
|
|
||||||
|
// 计算显示状态(当isSelf为false时强制显示为3)
|
||||||
|
const displayStatus = computed(() => {
|
||||||
|
return !isSelf.value ? 3 : status.value
|
||||||
|
})
|
||||||
|
|
||||||
|
// 打开申请表单
|
||||||
|
const agentApply = () => {
|
||||||
|
showApplyPopup.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跳转到首页
|
||||||
|
const goToHome = () => {
|
||||||
|
clearInterval(intervalId)
|
||||||
|
uni.switchTab({
|
||||||
|
url: '/pages/index'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAgentInformation = async () => {
|
||||||
|
const token = uni.getStorageSync("token")
|
||||||
|
if (!token) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await getAgentInfo()
|
||||||
|
if (res.code === 200 && res.data) {
|
||||||
|
// 将代理信息存入缓存
|
||||||
|
uni.setStorageSync("agentInfo", {
|
||||||
|
level: res.data.level,
|
||||||
|
isAgent: res.data.is_agent, // 判断是否是代理
|
||||||
|
status: res.data.status, // 获取代理状态 0=待审核,1=审核通过,2=审核未通过,3=未申请
|
||||||
|
agentID: res.data.agent_id,
|
||||||
|
mobile: res.data.mobile,
|
||||||
|
isRealName: res.data.is_real_name,
|
||||||
|
expiryTime: res.data.expiry_time
|
||||||
|
})
|
||||||
|
status.value = res.data.status
|
||||||
|
console.log('代理信息已获取并存入缓存')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取代理信息失败', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 提交代理申请
|
||||||
|
const submitApplication = async (formData) => {
|
||||||
|
try {
|
||||||
|
const { region, mobile, wechat_id, code } = formData
|
||||||
|
let postData = {
|
||||||
|
region,
|
||||||
|
mobile,
|
||||||
|
wechat_id,
|
||||||
|
code,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isSelf.value) {
|
||||||
|
postData.ancestor = ancestor.value
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await applyAgent(postData)
|
||||||
|
|
||||||
|
if (res.code === 200) {
|
||||||
|
showApplyPopup.value = false
|
||||||
|
uni.showToast({
|
||||||
|
title: "已提交申请",
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
|
||||||
|
if (res.data.accessToken) {
|
||||||
|
uni.setStorageSync('token', res.data.accessToken)
|
||||||
|
uni.setStorageSync('refreshAfter', res.data.refreshAfter)
|
||||||
|
uni.setStorageSync('accessExpire', res.data.accessExpire)
|
||||||
|
refreshAgentStatus()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: res.msg || '申请失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '网络错误',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定时刷新代理状态
|
||||||
|
const refreshAgentStatus = () => {
|
||||||
|
if (status.value === 3) {
|
||||||
|
if (intervalId) clearInterval(intervalId)
|
||||||
|
|
||||||
|
intervalId = setInterval(() => {
|
||||||
|
if (status.value !== 3) {
|
||||||
|
clearInterval(intervalId)
|
||||||
|
intervalId = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
getAgentInformation()
|
||||||
|
}, 2000)
|
||||||
|
} else {
|
||||||
|
if (intervalId) {
|
||||||
|
clearInterval(intervalId)
|
||||||
|
intervalId = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onLoad(() => {
|
||||||
|
const token = uni.getStorageSync('token')
|
||||||
|
if (token) {
|
||||||
|
// 从缓存中获取代理信息
|
||||||
|
const agentInfo = uni.getStorageSync('agentInfo')
|
||||||
|
if (agentInfo) {
|
||||||
|
status.value = agentInfo.status || 3
|
||||||
|
}
|
||||||
|
getAgentInformation()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 页面卸载时清除定时器
|
||||||
|
onUnload(() => {
|
||||||
|
if (intervalId) {
|
||||||
|
clearInterval(intervalId)
|
||||||
|
intervalId = null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<route type="page" lang="json">{
|
||||||
|
"layout": "page",
|
||||||
|
"title": "代理申请",
|
||||||
|
"auth": true
|
||||||
|
}</route>
|
||||||
223
src/pages/login.vue
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
<script setup>
|
||||||
|
import { getCode, bindMobile, getUserInfo } from '@/api/apis'
|
||||||
|
import { getAgentInfo } from '@/apis/agent'
|
||||||
|
const phoneNumber = ref('')
|
||||||
|
const verificationCode = ref('')
|
||||||
|
const password = ref('')
|
||||||
|
const isPasswordLogin = ref(false)
|
||||||
|
const isAgreed = ref(false)
|
||||||
|
const isCountingDown = ref(false)
|
||||||
|
const countdown = ref(60)
|
||||||
|
let timer = null
|
||||||
|
|
||||||
|
// 聚焦状态变量
|
||||||
|
const phoneFocused = ref(false)
|
||||||
|
const codeFocused = ref(false)
|
||||||
|
const passwordFocused = ref(false)
|
||||||
|
|
||||||
|
const isPhoneNumberValid = computed(() => {
|
||||||
|
return /^1[3-9]\d{9}$/.test(phoneNumber.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
const canLogin = computed(() => {
|
||||||
|
if (!isPhoneNumberValid.value)
|
||||||
|
return false
|
||||||
|
if (isPasswordLogin.value) {
|
||||||
|
return password.value.length >= 6
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return verificationCode.value.length === 6
|
||||||
|
}
|
||||||
|
})
|
||||||
|
function sendVerificationCode() {
|
||||||
|
if (isCountingDown.value || !isPhoneNumberValid.value)
|
||||||
|
return
|
||||||
|
if (!isPhoneNumberValid.value) {
|
||||||
|
uni.showToast({ title: '请输入有效的手机号', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
getCode({
|
||||||
|
mobile: phoneNumber.value,
|
||||||
|
actionType: 'bindMobile',
|
||||||
|
}).then((res) => {
|
||||||
|
if (res.code === 200) {
|
||||||
|
uni.showToast({ title: '获取成功', icon: 'none' })
|
||||||
|
|
||||||
|
startCountdown()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function startCountdown() {
|
||||||
|
isCountingDown.value = true
|
||||||
|
countdown.value = 60
|
||||||
|
timer = setInterval(() => {
|
||||||
|
if (countdown.value > 0) {
|
||||||
|
countdown.value--
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
clearInterval(timer)
|
||||||
|
isCountingDown.value = false
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleLogin() {
|
||||||
|
if (!canLogin.value) {
|
||||||
|
uni.showToast({ title: '请完善信息', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!isAgreed.value) {
|
||||||
|
uni.showToast({ title: '请先同意用户协议', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
bindMobile({ mobile: phoneNumber.value, code: verificationCode.value }).then((res) => {
|
||||||
|
if (res.code === 200) {
|
||||||
|
uni.setStorageSync('token', res.data.accessToken)
|
||||||
|
uni.setStorageSync('refreshAfter', res.data.refreshAfter)
|
||||||
|
uni.setStorageSync('accessExpire', res.data.accessExpire)
|
||||||
|
uni.showToast({ title: '绑定成功', icon: 'none' })
|
||||||
|
getUser()
|
||||||
|
getAgentInformation()
|
||||||
|
uni.reLaunch({
|
||||||
|
url: '/pages/index',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
uni.showToast({ title: res.msg, icon: 'none' })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function toUserAgreement() {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/agreement?type=user',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function toPrivacyPolicy() {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/agreement?type=privacy',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const getAgentInformation = async () => {
|
||||||
|
const token = uni.getStorageSync("token")
|
||||||
|
if (!token) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await getAgentInfo()
|
||||||
|
if (res.code === 200 && res.data) {
|
||||||
|
// 将代理信息存入缓存
|
||||||
|
uni.setStorageSync("agentInfo", {
|
||||||
|
level: res.data.level,
|
||||||
|
isAgent: res.data.is_agent, // 判断是否是代理
|
||||||
|
status: res.data.status, // 获取代理状态 0=待审核,1=审核通过,2=审核未通过,3=未申请
|
||||||
|
agentID: res.data.agent_id,
|
||||||
|
mobile: res.data.mobile
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('代理信息已获取并存入缓存')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取代理信息失败', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getUser = async () => {
|
||||||
|
const token = uni.getStorageSync("token")
|
||||||
|
if (!token) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const res = await getUserInfo()
|
||||||
|
if (res.code === 200) {
|
||||||
|
console.log(res.data)
|
||||||
|
uni.setStorageSync("userInfo", res.data.userInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (timer) {
|
||||||
|
clearInterval(timer)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<view class="login px-8">
|
||||||
|
<view class="mb-8 pt-8 text-left">
|
||||||
|
<view class="flex flex-col items-center">
|
||||||
|
<image class="h-18 w-18 rounded-full shadow" src="/static/image/logo.jpg" mode="scaleToFill" />
|
||||||
|
<view class="mt-4 text-3xl font-bold text-gray-800">天远数据</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="space-y-5">
|
||||||
|
<view class="input-container bg-blue-300/20" :class="[phoneFocused ? 'focused' : '']">
|
||||||
|
<input v-model="phoneNumber" class="input-field" type="number" placeholder="请输入手机号" maxlength="11"
|
||||||
|
@focus="phoneFocused = true" @blur="phoneFocused = false">
|
||||||
|
</view>
|
||||||
|
<view v-if="!isPasswordLogin">
|
||||||
|
<view class="flex items-center justify-between">
|
||||||
|
<view class="input-container bg-blue-300/20" :class="[codeFocused ? 'focused' : '']">
|
||||||
|
<input v-model="verificationCode" class="input-field" type="number" placeholder="请输入验证码" maxlength="6"
|
||||||
|
@focus="codeFocused = true" @blur="codeFocused = false">
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="ml-2 flex-shrink-0 rounded-lg px-4 py-2 text-sm font-bold transition duration-300 focus:outline-none"
|
||||||
|
:class="isCountingDown || !isPhoneNumberValid ? 'cursor-not-allowed bg-gray-300 text-gray-500' : 'bg-blue-500 text-white hover:bg-blue-600'"
|
||||||
|
@click="sendVerificationCode">
|
||||||
|
{{ isCountingDown ? `${countdown}s重新获取` : '获取验证码' }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view v-if="isPasswordLogin" class="input-container" :class="[passwordFocused ? 'focused' : '']">
|
||||||
|
<input v-model="password" class="input-field" type="password" placeholder="请输入密码"
|
||||||
|
@focus="passwordFocused = true" @blur="passwordFocused = false">
|
||||||
|
</view>
|
||||||
|
<view class="flex items-start space-x-2">
|
||||||
|
<wd-checkbox v-model="isAgreed" class="mt-1" />
|
||||||
|
<text class="text-xs text-gray-400 leading-tight">
|
||||||
|
绑定手机号后将自动成为代理,并且代表您已阅读并同意
|
||||||
|
<text class="cursor-pointer text-blue-400" @click="toUserAgreement">
|
||||||
|
《用户协议》
|
||||||
|
</text>
|
||||||
|
和
|
||||||
|
<text class="cursor-pointer text-blue-400" @click="toPrivacyPolicy">
|
||||||
|
《隐私政策》
|
||||||
|
</text>
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<button
|
||||||
|
class="mt-20 block w-full flex-shrink-0 rounded-full bg-blue-500 py-3 text-lg text-white font-bold transition duration-300"
|
||||||
|
@click="handleLogin">
|
||||||
|
绑定手机号
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.login {}
|
||||||
|
|
||||||
|
.input-container {
|
||||||
|
border: 2px solid rgba(125, 211, 252, 0.0);
|
||||||
|
border-radius: 1rem;
|
||||||
|
@apply transition duration-200
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-container.focused {
|
||||||
|
border: 2px solid #3b82f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-field {
|
||||||
|
width: 100%;
|
||||||
|
padding: 1rem;
|
||||||
|
transition: border-color 0.3s ease;
|
||||||
|
outline: none;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<route lang="json">{
|
||||||
|
"layout": "login",
|
||||||
|
"title": "绑定手机号"
|
||||||
|
}</route>
|
||||||
296
src/pages/me.vue
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
<template>
|
||||||
|
<view class="safe-area-top box-border min-h-screen">
|
||||||
|
<view class="flex flex-col p-4 space-y-6">
|
||||||
|
<!-- 用户信息卡片 -->
|
||||||
|
<view
|
||||||
|
class="mb-4 profile-section group relative flex items-center gap-4 rounded-xl bg-white p-6 shadow-lg transition-all hover:shadow-xl"
|
||||||
|
@click="!isLoggedIn ? redirectToLogin() : null">
|
||||||
|
<view class="relative">
|
||||||
|
<!-- 头像容器添加overflow-hidden解决边框问题 -->
|
||||||
|
<view class="flex items-center justify-center overflow-hidden rounded-full p-0.5"
|
||||||
|
:class="levelGradient.border">
|
||||||
|
<image :src="userAvatar || getDefaultAvatar()" alt="User Avatar"
|
||||||
|
class="h-24 w-24 rounded-full border-4 border-white">
|
||||||
|
</image>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 代理标识 -->
|
||||||
|
<view v-if="isAgent" class="absolute -bottom-2 -right-2">
|
||||||
|
<view class="flex items-center justify-center rounded-full px-3 py-1 text-xs font-bold text-white shadow-sm"
|
||||||
|
:class="levelGradient.badge">
|
||||||
|
{{ levelNames[level] }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="space-y-1">
|
||||||
|
<!-- @click.stop="handleVersionClickForTest" -->
|
||||||
|
<view class="text-lg font-bold text-gray-800" @click="userType === 0 ? toBindPhone() : null"
|
||||||
|
:class="userType === 0 ? 'cursor-pointer text-blue-600' : ''">
|
||||||
|
{{ !isLoggedIn ? '点击登录' : (userType === 0 ? '绑定手机号' : maskName(userName)) }}
|
||||||
|
</view>
|
||||||
|
<view v-if="isAgent" class="text-sm font-medium" :class="levelGradient.text">
|
||||||
|
🎖️ {{ levelText[level] }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<VipBanner v-if="isAgent && level === 'normal'" />
|
||||||
|
<!-- 功能菜单 -->
|
||||||
|
<view class="features-section space-y-3">
|
||||||
|
<template v-if="isAgent && ['VIP', 'SVIP'].includes(level)">
|
||||||
|
<button
|
||||||
|
class=" flex items-center p-3 rounded-xl bg-gradient-to-r from-purple-200/80 to-pink-200/80 text-purple-700 font-medium shadow-sm transition-all active:shadow-md"
|
||||||
|
hover-class="opacity-80 scale-98" @click="toVipConfig">
|
||||||
|
<text class="mr-2">⚙️</text> 代理报告配置
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
<button
|
||||||
|
class=" flex items-center text-gray-600 p-3 rounded-xl bg-white font-medium shadow-sm transition-all active:shadow-md"
|
||||||
|
hover-class="opacity-80 bg-blue-50" @click="toUserAgreement">
|
||||||
|
<text class="mr-2">📜</text> 用户协议
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class=" flex items-center text-gray-600 p-3 rounded-xl bg-white font-medium shadow-sm transition-all active:shadow-md"
|
||||||
|
hover-class="opacity-80 bg-blue-50" @click="toPrivacyPolicy">
|
||||||
|
<text class="mr-2">🔒</text> 隐私政策
|
||||||
|
</button>
|
||||||
|
<button open-type="contact"
|
||||||
|
class=" flex items-center text-gray-600 p-3 rounded-xl bg-white font-medium shadow-sm transition-all active:shadow-md"
|
||||||
|
hover-class="opacity-80 bg-blue-50" @click="toAi">
|
||||||
|
<text class="mr-2">💬</text> 联系客服
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 更新进度弹窗 -->
|
||||||
|
<view v-if="showUpdateProgress" class="update-progress-mask">
|
||||||
|
<view class="update-progress-dialog">
|
||||||
|
<view class="update-progress-title">应用更新中</view>
|
||||||
|
<view class="update-progress-bar-container">
|
||||||
|
<view class="update-progress-bar" :style="{ width: downloadProgress + '%' }"></view>
|
||||||
|
</view>
|
||||||
|
<view class="update-progress-text">{{ downloadProgress }}%</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, onBeforeMount } from 'vue'
|
||||||
|
|
||||||
|
// 用户数据
|
||||||
|
const userName = ref('')
|
||||||
|
const userAvatar = ref('')
|
||||||
|
const isLoggedIn = ref(false)
|
||||||
|
const userType = ref(null)
|
||||||
|
|
||||||
|
// 代理数据
|
||||||
|
const isAgent = ref(false)
|
||||||
|
const level = ref('normal')
|
||||||
|
|
||||||
|
|
||||||
|
const showUpdateProgress = ref(false)
|
||||||
|
|
||||||
|
// 检查平台环境
|
||||||
|
onBeforeMount(() => {
|
||||||
|
// 从缓存获取用户信息
|
||||||
|
const token = uni.getStorageSync('token')
|
||||||
|
if (token) {
|
||||||
|
isLoggedIn.value = true
|
||||||
|
// 从缓存获取用户信息
|
||||||
|
const userInfo = uni.getStorageSync('userInfo')
|
||||||
|
console.log("userInfo", userInfo)
|
||||||
|
if (userInfo) {
|
||||||
|
userName.value = userInfo.nickName || userInfo.mobile || '微信用户'
|
||||||
|
userAvatar.value = userInfo.avatar || ''
|
||||||
|
userType.value = userInfo.userType
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从缓存获取代理信息
|
||||||
|
const agentInfo = uni.getStorageSync('agentInfo')
|
||||||
|
if (agentInfo?.isAgent) {
|
||||||
|
isAgent.value = agentInfo.isAgent
|
||||||
|
level.value = agentInfo.level || 'normal'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
const levelNames = {
|
||||||
|
normal: '普通代理',
|
||||||
|
'': '普通代理',
|
||||||
|
VIP: 'VIP代理',
|
||||||
|
SVIP: 'SVIP代理',
|
||||||
|
}
|
||||||
|
|
||||||
|
const levelText = {
|
||||||
|
normal: '基础代理特权',
|
||||||
|
'': '基础代理特权',
|
||||||
|
VIP: '高级代理特权',
|
||||||
|
SVIP: '尊享代理特权',
|
||||||
|
}
|
||||||
|
|
||||||
|
const levelGradient = computed(() => ({
|
||||||
|
border: {
|
||||||
|
normal: 'bg-green-300',
|
||||||
|
'': 'bg-green-300',
|
||||||
|
VIP: 'bg-gradient-to-r from-yellow-400 to-amber-500',
|
||||||
|
SVIP: 'bg-gradient-to-r from-purple-400 to-pink-400 shadow-[0_0_15px_rgba(163,51,200,0.2)]',
|
||||||
|
}[level.value],
|
||||||
|
|
||||||
|
badge: {
|
||||||
|
normal: 'bg-green-500',
|
||||||
|
'': 'bg-green-500',
|
||||||
|
VIP: 'bg-gradient-to-r from-yellow-500 to-amber-600',
|
||||||
|
SVIP: 'bg-gradient-to-r from-purple-500 to-pink-500',
|
||||||
|
}[level.value],
|
||||||
|
|
||||||
|
text: {
|
||||||
|
normal: 'text-green-600',
|
||||||
|
'': 'text-green-600',
|
||||||
|
VIP: 'text-amber-600',
|
||||||
|
SVIP: 'text-purple-600',
|
||||||
|
}[level.value],
|
||||||
|
}))
|
||||||
|
|
||||||
|
function maskName(name) {
|
||||||
|
if (!name || name.length < 11) return name
|
||||||
|
return name.substring(0, 3) + "****" + name.substring(7)
|
||||||
|
}
|
||||||
|
|
||||||
|
function toHistory() {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/queryHistory'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function toUserAgreement() {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/agreement?type=user'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function redirectToLogin() {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/login'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const toPrivacyPolicy = () => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/agreement?type=privacy'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const toAi = () => {
|
||||||
|
uni.switchTab({
|
||||||
|
url: '/pages/ai'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function toVipConfig() {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/agentVipConfig'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function toBindPhone() {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages/login'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const getDefaultAvatar = () => {
|
||||||
|
if (!isAgent.value) return '/static/image/head_shot.webp'
|
||||||
|
|
||||||
|
switch (level.value) {
|
||||||
|
case 'normal':
|
||||||
|
case '':
|
||||||
|
return '/static/image/shot_nonal.png'
|
||||||
|
case 'VIP':
|
||||||
|
return '/static/image/shot_vip.png'
|
||||||
|
case 'SVIP':
|
||||||
|
return '/static/image/shot_svip.png'
|
||||||
|
default:
|
||||||
|
return '/static/image/head_shot.webp'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.profile-section {
|
||||||
|
background: linear-gradient(135deg, #ffffff 50%, rgba(236, 253, 245, 0.3));
|
||||||
|
border: 1px solid rgba(209, 213, 219, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-section .relative>view:first-child {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-gradient-to-r {
|
||||||
|
border-image: linear-gradient(to right, var(--tw-gradient-from), var(--tw-gradient-to)) 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadow-glow {
|
||||||
|
box-shadow: 0 0 8px rgba(163, 51, 200, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 更新进度样式 */
|
||||||
|
.update-progress-mask {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-progress-dialog {
|
||||||
|
width: 80%;
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-progress-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-progress-bar-container {
|
||||||
|
height: 10px;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
border-radius: 5px;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-progress-bar {
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(to right, #4299e1, #667eea);
|
||||||
|
border-radius: 5px;
|
||||||
|
transition: width 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-progress-text {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<route lang="json">{
|
||||||
|
"layout": "home",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTextStyle": "black",
|
||||||
|
"navigationStyle": "default",
|
||||||
|
"navigationBarBackgroundColor": "#e3f0ff"
|
||||||
|
}
|
||||||
|
}</route>
|
||||||
265
src/pages/promote.vue
Normal file
@@ -0,0 +1,265 @@
|
|||||||
|
<template>
|
||||||
|
<view class="min-h-screen p-4 promote">
|
||||||
|
<view class="mb-4 card !bg-gradient-to-b from-orange-200 to-orange-200/80">
|
||||||
|
<view>
|
||||||
|
<text class="text-lg font-bold text-orange-500 block">直推用户查询</text>
|
||||||
|
<text class="font-bold text-orange-400 mt-1 block">自定义价格,赚取差价</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="mt-6">
|
||||||
|
<view class="mt-2 text-gray-600 bg-orange-100 rounded-xl px-4 py-2">
|
||||||
|
在下方 "自定义价格" 处选择报告类型,设置客户查询价(元)即可立即推广。
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<VipBanner />
|
||||||
|
|
||||||
|
<!-- 查看示例报告提示 -->
|
||||||
|
<view class="card mb-4 !bg-gradient-to-r from-blue-50 to-blue-100 border-l-4 border-blue-400">
|
||||||
|
<view class="flex items-center justify-between">
|
||||||
|
<view>
|
||||||
|
<text class="text-sm text-blue-700 font-medium">不知道如何推广?</text>
|
||||||
|
<text class="text-xs text-blue-600 mt-1 block">查看示例报告了解产品效果</text>
|
||||||
|
</view>
|
||||||
|
<text class="text-blue-500 text-sm underline" @click="showGzhQrcodeModal">查看示例</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 推广内容 -->
|
||||||
|
<view>
|
||||||
|
<view class="card mb-4">
|
||||||
|
<view>
|
||||||
|
<text class="text-xl font-semibold mb-2 block">生成推广码</text>
|
||||||
|
<wd-form :model="formData" ref="promotionForm">
|
||||||
|
<wd-cell-group border>
|
||||||
|
<!-- 报告类型 -->
|
||||||
|
<wd-picker label="报告类型" label-width="100px" v-model="formData.productType" :columns="[reportTypes]"
|
||||||
|
title="选择报告类型" prop="productType" placeholder="请选择报告类型" @confirm="onConfirmType"
|
||||||
|
:rules="[{ required: true, message: '请选择报告类型' }]" />
|
||||||
|
|
||||||
|
<!-- 定价 -->
|
||||||
|
<wd-input label="客户查询价" label-width="100px" v-model="formData.clientPrice" placeholder="请输入价格" readonly
|
||||||
|
clickable @click="showPricePicker = true" prop="clientPrice" suffix-icon="arrow-right"
|
||||||
|
:rules="[{ required: true, message: '请输入客户查询价' }]" />
|
||||||
|
</wd-cell-group>
|
||||||
|
|
||||||
|
<view class="flex items-center justify-between my-2">
|
||||||
|
<text class="text-sm text-gray-500">推广收益为 <text class="text-orange-500">{{ promotionRevenue }}</text>
|
||||||
|
元</text>
|
||||||
|
<text class="text-sm text-gray-500">我的成本为 <text class="text-orange-500">{{ costPrice }}</text> 元</text>
|
||||||
|
</view>
|
||||||
|
</wd-form>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="mt-6">
|
||||||
|
<button type="primary" block @click="generatePromotionCode">点击立即推广</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<PriceInputPopup v-model:show="showPricePicker" :default-price="formData.clientPrice"
|
||||||
|
:product-config="pickerProductConfig" @change="onPriceChange" />
|
||||||
|
|
||||||
|
<QRcode v-model:show="showQRcode" :linkIdentifier="linkIdentifier" />
|
||||||
|
|
||||||
|
<GzhQrcode :visible="showGzhQrcode" @close="showGzhQrcode = false" />
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { getProductConfig, generatePromotionLink } from '@/apis/agent'
|
||||||
|
import PriceInputPopup from '@/components/PriceInputPopup.vue'
|
||||||
|
import VipBanner from '@/components/VipBanner.vue'
|
||||||
|
import QRcode from '@/components/QRcode.vue'
|
||||||
|
import GzhQrcode from '@/components/GzhQrcode.vue'
|
||||||
|
|
||||||
|
// 报告类型
|
||||||
|
const reportTypes = [
|
||||||
|
{ label: '人事背调', value: 'backgroundcheck', id: 1 },
|
||||||
|
{ label: '老板企业报告', value: 'companyinfo', id: 2 },
|
||||||
|
{ label: '家政风险', value: 'homeservice', id: 3 },
|
||||||
|
{ label: '婚恋风险', value: 'marriage', id: 4 },
|
||||||
|
{ label: '贷前背调', value: 'preloanbackgroundcheck', id: 5 },
|
||||||
|
{ label: '租赁风险', value: 'rentalrisk', id: 6 },
|
||||||
|
{ label: '个人风险', value: 'riskassessment', id: 7 }
|
||||||
|
]
|
||||||
|
|
||||||
|
// 状态管理
|
||||||
|
const promotionForm = ref(null)
|
||||||
|
const showPricePicker = ref(false)
|
||||||
|
const pickerProductConfig = ref(null)
|
||||||
|
const productConfig = ref(null)
|
||||||
|
const linkIdentifier = ref("")
|
||||||
|
const showQRcode = ref(false)
|
||||||
|
const showGzhQrcode = ref(false)
|
||||||
|
|
||||||
|
// 表单数据对象
|
||||||
|
const formData = ref({
|
||||||
|
productType: '',
|
||||||
|
clientPrice: null
|
||||||
|
})
|
||||||
|
|
||||||
|
// 计算成本价格
|
||||||
|
const costPrice = computed(() => {
|
||||||
|
if (!pickerProductConfig.value) return '0.00'
|
||||||
|
// 平台定价成本
|
||||||
|
let platformPricing = 0
|
||||||
|
platformPricing += pickerProductConfig.value.cost_price
|
||||||
|
if (formData.value.clientPrice > pickerProductConfig.value.p_pricing_standard) {
|
||||||
|
platformPricing += (formData.value.clientPrice - pickerProductConfig.value.p_pricing_standard) * pickerProductConfig.value.p_overpricing_ratio
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pickerProductConfig.value.a_pricing_standard > platformPricing &&
|
||||||
|
pickerProductConfig.value.a_pricing_end > platformPricing &&
|
||||||
|
pickerProductConfig.value.a_overpricing_ratio > 0) {
|
||||||
|
if (formData.value.clientPrice > pickerProductConfig.value.a_pricing_standard) {
|
||||||
|
if (formData.value.clientPrice > pickerProductConfig.value.a_pricing_end) {
|
||||||
|
platformPricing += (pickerProductConfig.value.a_pricing_end - pickerProductConfig.value.a_pricing_standard) * pickerProductConfig.value.a_overpricing_ratio
|
||||||
|
} else {
|
||||||
|
platformPricing += (formData.value.clientPrice - pickerProductConfig.value.a_pricing_standard) * pickerProductConfig.value.a_overpricing_ratio
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return safeTruncate(platformPricing)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 计算推广收益
|
||||||
|
const promotionRevenue = computed(() => {
|
||||||
|
return safeTruncate(formData.value.clientPrice - costPrice.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 安全截断数字(保留2位小数)
|
||||||
|
function safeTruncate(num, decimals = 2) {
|
||||||
|
if (isNaN(num) || !isFinite(num)) return "0.00"
|
||||||
|
|
||||||
|
const factor = 10 ** decimals
|
||||||
|
const scaled = Math.trunc(num * factor)
|
||||||
|
const truncated = scaled / factor
|
||||||
|
|
||||||
|
return truncated.toFixed(decimals)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成推广码
|
||||||
|
const generatePromotionCode = async () => {
|
||||||
|
// 表单验证
|
||||||
|
try {
|
||||||
|
await promotionForm.value.validate()
|
||||||
|
} catch (e) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取选中产品的完整信息
|
||||||
|
const reportType = reportTypes.find(item => item.value === formData.value.productType)
|
||||||
|
if (!reportType) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '请选择有效的报告类型',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await generatePromotionLink({
|
||||||
|
product: formData.value.productType,
|
||||||
|
price: formData.value.clientPrice
|
||||||
|
})
|
||||||
|
|
||||||
|
if (res.code === 200) {
|
||||||
|
linkIdentifier.value = res.data.link_identifier
|
||||||
|
showQRcode.value = true
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: res.msg || '生成推广码失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '网络错误',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择类型
|
||||||
|
const selectProductType = (reportTypeValue) => {
|
||||||
|
const reportType = reportTypes.find(item => item.id === reportTypeValue || item.value === reportTypeValue)
|
||||||
|
|
||||||
|
if (!reportType) return
|
||||||
|
|
||||||
|
formData.value.productType = reportType.value
|
||||||
|
|
||||||
|
if (productConfig.value) {
|
||||||
|
for (let i of productConfig.value) {
|
||||||
|
if (i.product_id === reportType.id) {
|
||||||
|
pickerProductConfig.value = i
|
||||||
|
formData.value.clientPrice = i.p_pricing_standard.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取产品配置
|
||||||
|
const getPromoteConfig = async () => {
|
||||||
|
try {
|
||||||
|
const res = await getProductConfig()
|
||||||
|
|
||||||
|
if (res.code === 200) {
|
||||||
|
productConfig.value = res.data.AgentProductConfig
|
||||||
|
// 选择第一个报告类型
|
||||||
|
selectProductType(1) // 使用ID 1选择第一个报告类型
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: res.msg || '获取配置失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
uni.showToast({
|
||||||
|
title: '网络错误',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 价格变更
|
||||||
|
const onPriceChange = (price) => {
|
||||||
|
formData.value.clientPrice = price
|
||||||
|
}
|
||||||
|
|
||||||
|
// 类型选择确认
|
||||||
|
const onConfirmType = (e) => {
|
||||||
|
// picker在单列模式下返回的是选中项的值
|
||||||
|
if (e && e.value && e.value.length > 0) {
|
||||||
|
const selectedValue = e.value[0]
|
||||||
|
selectProductType(selectedValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示公众号二维码
|
||||||
|
const showGzhQrcodeModal = () => {
|
||||||
|
showGzhQrcode.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 页面加载
|
||||||
|
onMounted(() => {
|
||||||
|
getPromoteConfig()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.card {
|
||||||
|
border-radius: 12px;
|
||||||
|
background-color: white;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||||
|
padding: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<route type="page" lang="json">{
|
||||||
|
"layout": "page",
|
||||||
|
"title": "推广",
|
||||||
|
"agent": true,
|
||||||
|
"auth": true
|
||||||
|
}</route>
|
||||||
144
src/pages/promoteDetails.vue
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
<template>
|
||||||
|
<view class="min-h-screen bg-gray-50">
|
||||||
|
<!-- 收益列表 -->
|
||||||
|
<uni-list :loading="loading" :loadmore="loadMoreStatus" @loadmore="onLoadMore">
|
||||||
|
<!-- 空状态提示 -->
|
||||||
|
<view v-if="!loading && list.length === 0" class="flex flex-col items-center justify-center py-16">
|
||||||
|
<image src="/static/image/empty.svg" mode="aspectFit" class="w-48 h-48 mb-4" />
|
||||||
|
<text class="text-gray-400 text-base">暂无直推报告</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-for="(item, index) in list" :key="index" class="mx-4 my-2 bg-white rounded-lg p-4 shadow-sm">
|
||||||
|
<view class="flex justify-between items-center mb-2">
|
||||||
|
<text class="text-gray-500 text-sm">{{ item.create_time || '-' }}</text>
|
||||||
|
<text class="text-green-500 font-bold">+{{ item.amount.toFixed(2) }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="flex items-center">
|
||||||
|
<text class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium"
|
||||||
|
:class="getReportTypeStyle(item.product_name)">
|
||||||
|
<text class="w-2 h-2 rounded-full mr-1 inline-block" :class="getDotColor(item.product_name)"></text>
|
||||||
|
{{ item.product_name }}
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 加载更多/加载完成提示 -->
|
||||||
|
<uni-load-more :status="loadMoreStatus" />
|
||||||
|
</uni-list>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
|
import { getAgentCommission } from '@/apis/agent'
|
||||||
|
|
||||||
|
// 颜色配置(根据产品名称映射)
|
||||||
|
const typeColors = {
|
||||||
|
'老板企业报告': { bg: 'bg-blue-100', text: 'text-blue-800', dot: 'bg-blue-500' },
|
||||||
|
'人事背调': { bg: 'bg-green-100', text: 'text-green-800', dot: 'bg-green-500' },
|
||||||
|
'家政风险': { bg: 'bg-purple-100', text: 'text-purple-800', dot: 'bg-purple-500' },
|
||||||
|
'婚恋风险': { bg: 'bg-pink-100', text: 'text-pink-800', dot: 'bg-pink-500' },
|
||||||
|
'贷前背调': { bg: 'bg-orange-100', text: 'text-orange-800', dot: 'bg-orange-500' },
|
||||||
|
'租赁风险': { bg: 'bg-indigo-100', text: 'text-indigo-800', dot: 'bg-indigo-500' },
|
||||||
|
'个人风险': { bg: 'bg-red-100', text: 'text-red-800', dot: 'bg-red-500' },
|
||||||
|
// 默认类型
|
||||||
|
'default': { bg: 'bg-gray-100', text: 'text-gray-800', dot: 'bg-gray-500' }
|
||||||
|
}
|
||||||
|
|
||||||
|
const page = ref(1)
|
||||||
|
const pageSize = ref(10)
|
||||||
|
const total = ref(0)
|
||||||
|
const list = ref([])
|
||||||
|
const loading = ref(false)
|
||||||
|
const loadMoreStatus = ref('more') // 'more'|'loading'|'noMore'
|
||||||
|
|
||||||
|
// 获取颜色样式
|
||||||
|
const getReportTypeStyle = (name) => {
|
||||||
|
const color = typeColors[name] || typeColors.default
|
||||||
|
return `${color.bg} ${color.text}`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取小圆点颜色
|
||||||
|
const getDotColor = (name) => {
|
||||||
|
return (typeColors[name] || typeColors.default).dot
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载更多数据
|
||||||
|
const onLoadMore = async () => {
|
||||||
|
if (loadMoreStatus.value === 'noMore') return
|
||||||
|
|
||||||
|
page.value++
|
||||||
|
await getData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取数据
|
||||||
|
const getData = async () => {
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
loadMoreStatus.value = 'loading'
|
||||||
|
|
||||||
|
const res = await getAgentCommission({
|
||||||
|
page: page.value,
|
||||||
|
page_size: pageSize.value
|
||||||
|
})
|
||||||
|
|
||||||
|
if (res.code === 200) {
|
||||||
|
// 首次加载
|
||||||
|
if (page.value === 1) {
|
||||||
|
list.value = res.data.list
|
||||||
|
total.value = res.data.total
|
||||||
|
} else {
|
||||||
|
// 分页加载
|
||||||
|
list.value.push(...res.data.list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否加载完成
|
||||||
|
if (list.value.length >= res.data.total || res.data.list.length < pageSize.value) {
|
||||||
|
loadMoreStatus.value = 'noMore'
|
||||||
|
} else {
|
||||||
|
loadMoreStatus.value = 'more'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: res.msg || '加载失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '网络错误',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 页面加载
|
||||||
|
onMounted(() => {
|
||||||
|
getData()
|
||||||
|
})
|
||||||
|
|
||||||
|
// 页面下拉刷新
|
||||||
|
const onPullDownRefresh = () => {
|
||||||
|
page.value = 1
|
||||||
|
loadMoreStatus.value = 'more'
|
||||||
|
getData().then(() => {
|
||||||
|
uni.stopPullDownRefresh()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出页面生命周期方法
|
||||||
|
defineExpose({
|
||||||
|
onPullDownRefresh
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<route type="page" lang="json">
|
||||||
|
{
|
||||||
|
"layout": "page",
|
||||||
|
"title": "直推报告",
|
||||||
|
"agent": true,
|
||||||
|
"auth": true
|
||||||
|
}
|
||||||
|
</route>
|
||||||
165
src/pages/rewardsDetails.vue
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
<template>
|
||||||
|
<view class="min-h-screen bg-gray-50">
|
||||||
|
<!-- 收益列表 -->
|
||||||
|
<uni-list :loading="loading" :loadmore="loadMoreStatus" @loadmore="onLoadMore">
|
||||||
|
<!-- 空状态提示 -->
|
||||||
|
<view v-if="!loading && list.length === 0" class="flex flex-col items-center justify-center py-16">
|
||||||
|
<image src="/static/image/empty.svg" mode="aspectFit" class="w-48 h-48 mb-4" />
|
||||||
|
<text class="text-gray-400 text-base">暂无收益记录</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-for="(item, index) in list" :key="index" class="mx-4 my-2 bg-white rounded-lg p-4 shadow-sm">
|
||||||
|
<view class="flex justify-between items-center mb-2">
|
||||||
|
<text class="text-gray-500 text-sm">{{ item.create_time || '-' }}</text>
|
||||||
|
<text class="text-green-500 font-bold">+{{ item.amount.toFixed(2) }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="flex items-center">
|
||||||
|
<text class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium"
|
||||||
|
:class="getReportTypeStyle(item.type)">
|
||||||
|
<text class="w-2 h-2 rounded-full mr-1 inline-block" :class="getDotColor(item.type)"></text>
|
||||||
|
{{ typeToChinese(item.type) }}
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 加载更多/加载完成提示 -->
|
||||||
|
<uni-load-more :status="loadMoreStatus" />
|
||||||
|
</uni-list>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
|
import { getAgentRewards } from '@/apis/agent'
|
||||||
|
|
||||||
|
// 类型映射配置
|
||||||
|
const typeConfig = {
|
||||||
|
descendant_promotion: {
|
||||||
|
chinese: '下级推广奖励',
|
||||||
|
color: { bg: 'bg-blue-100', text: 'text-blue-800', dot: 'bg-blue-500' }
|
||||||
|
},
|
||||||
|
descendant_upgrade_vip: {
|
||||||
|
chinese: '下级升级VIP奖励',
|
||||||
|
color: { bg: 'bg-green-100', text: 'text-green-800', dot: 'bg-green-500' }
|
||||||
|
},
|
||||||
|
descendant_upgrade_svip: {
|
||||||
|
chinese: '下级升级SVIP奖励',
|
||||||
|
color: { bg: 'bg-purple-100', text: 'text-purple-800', dot: 'bg-purple-500' }
|
||||||
|
},
|
||||||
|
descendant_stay_activedescendant: {
|
||||||
|
chinese: '下级活跃奖励',
|
||||||
|
color: { bg: 'bg-pink-100', text: 'text-pink-800', dot: 'bg-pink-500' }
|
||||||
|
},
|
||||||
|
new_active: {
|
||||||
|
chinese: '新增活跃奖励',
|
||||||
|
color: { bg: 'bg-orange-100', text: 'text-orange-800', dot: 'bg-orange-500' }
|
||||||
|
},
|
||||||
|
descendant_withdraw: {
|
||||||
|
chinese: '下级提现奖励',
|
||||||
|
color: { bg: 'bg-indigo-100', text: 'text-indigo-800', dot: 'bg-indigo-500' }
|
||||||
|
},
|
||||||
|
default: {
|
||||||
|
chinese: '其他奖励',
|
||||||
|
color: { bg: 'bg-gray-100', text: 'text-gray-800', dot: 'bg-gray-500' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const page = ref(1)
|
||||||
|
const pageSize = ref(10)
|
||||||
|
const total = ref(0)
|
||||||
|
const list = ref([])
|
||||||
|
const loading = ref(false)
|
||||||
|
const loadMoreStatus = ref('more') // 'more'|'loading'|'noMore'
|
||||||
|
|
||||||
|
// 类型转中文
|
||||||
|
const typeToChinese = (type) => {
|
||||||
|
return typeConfig[type]?.chinese || typeConfig.default.chinese
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取颜色样式
|
||||||
|
const getReportTypeStyle = (type) => {
|
||||||
|
const config = typeConfig[type] || typeConfig.default
|
||||||
|
return `${config.color.bg} ${config.color.text}`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取小圆点颜色
|
||||||
|
const getDotColor = (type) => {
|
||||||
|
return typeConfig[type]?.color.dot || typeConfig.default.color.dot
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载更多数据
|
||||||
|
const onLoadMore = async () => {
|
||||||
|
if (loadMoreStatus.value === 'noMore') return
|
||||||
|
|
||||||
|
page.value++
|
||||||
|
await getData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取数据
|
||||||
|
const getData = async () => {
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
loadMoreStatus.value = 'loading'
|
||||||
|
|
||||||
|
const res = await getAgentRewards({
|
||||||
|
page: page.value,
|
||||||
|
page_size: pageSize.value
|
||||||
|
})
|
||||||
|
|
||||||
|
if (res.code === 200) {
|
||||||
|
if (page.value === 1) {
|
||||||
|
list.value = res.data.list
|
||||||
|
total.value = res.data.total
|
||||||
|
} else {
|
||||||
|
list.value.push(...res.data.list)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list.value.length >= res.data.total || res.data.list.length < pageSize.value) {
|
||||||
|
loadMoreStatus.value = 'noMore'
|
||||||
|
} else {
|
||||||
|
loadMoreStatus.value = 'more'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: res.msg || '加载失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '网络错误',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 页面加载
|
||||||
|
onMounted(() => {
|
||||||
|
getData()
|
||||||
|
})
|
||||||
|
|
||||||
|
// 页面下拉刷新
|
||||||
|
const onPullDownRefresh = () => {
|
||||||
|
page.value = 1
|
||||||
|
loadMoreStatus.value = 'more'
|
||||||
|
getData().then(() => {
|
||||||
|
uni.stopPullDownRefresh()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出页面生命周期方法
|
||||||
|
defineExpose({
|
||||||
|
onPullDownRefresh
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<route type="page" lang="json">
|
||||||
|
{
|
||||||
|
"layout": "page",
|
||||||
|
"title": "收益明细",
|
||||||
|
"agent": true,
|
||||||
|
"auth": true
|
||||||
|
}
|
||||||
|
</route>
|
||||||
26
src/pages/vip.vue
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<template>
|
||||||
|
<view class="relative">
|
||||||
|
<image class="w-full" src="/static/images/vip_bg.png" mode="widthFix" />
|
||||||
|
<view @click="toService"
|
||||||
|
class="absolute left-[50%] translate-x-[-50%] bottom-80 bg-gradient-to-r from-gray-900 via-black to-gray-900 py-2 px-4 rounded-lg text-white text-[24px] font-bold shadow-[0_0_15px_rgba(255,255,255,0.3)]"
|
||||||
|
hover-class="scale-105">
|
||||||
|
点击马上报名
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
function toService() {
|
||||||
|
// 跳转到客服页面
|
||||||
|
uni.navigateTo({
|
||||||
|
url: 'https://work.weixin.qq.com/kfid/kfc5c19b2b93a5e73b9'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.scale-105 {
|
||||||
|
transform: scale(1.05);
|
||||||
|
transition: transform 0.3s;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
181
src/pages/withdrawDetails.vue
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
<template>
|
||||||
|
<view class="min-h-screen bg-gray-50">
|
||||||
|
<!-- 提现记录列表 -->
|
||||||
|
<uni-list :loading="loading" :loadmore="loadMoreStatus" @loadmore="onLoadMore">
|
||||||
|
<!-- 空状态提示 -->
|
||||||
|
<view v-if="!loading && list.length === 0" class="flex flex-col items-center justify-center py-16">
|
||||||
|
<image src="/static/image/empty.svg" mode="aspectFit" class="w-48 h-48 mb-4" />
|
||||||
|
<text class="text-gray-400 text-base">暂无提现记录</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view v-for="(item, index) in list" :key="index" class="mx-4 my-2 bg-white rounded-lg p-4 shadow-sm">
|
||||||
|
<view class="flex justify-between items-center mb-2">
|
||||||
|
<text class="text-gray-500 text-sm">{{ item.create_time || '-' }}</text>
|
||||||
|
<text class="font-bold" :class="getAmountColor(item.status)">{{ item.amount.toFixed(2) }}元</text>
|
||||||
|
</view>
|
||||||
|
<view class="flex items-center mb-2">
|
||||||
|
<text class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium"
|
||||||
|
:class="getStatusStyle(item.status)">
|
||||||
|
<text class="w-2 h-2 rounded-full mr-1 inline-block" :class="getDotColor(item.status)"></text>
|
||||||
|
{{ statusToChinese(item.status) }}
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
<view class="text-xs text-gray-500">
|
||||||
|
<text v-if="item.payee_account" class="block">收款账户:{{ maskName(item.payee_account) }}</text>
|
||||||
|
<text v-if="item.remark" class="block">备注:{{ item.remark }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 加载更多/加载完成提示 -->
|
||||||
|
<uni-load-more :status="loadMoreStatus" />
|
||||||
|
</uni-list>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
|
import { getWithdrawalRecords } from '@/apis/agent'
|
||||||
|
|
||||||
|
// 状态映射配置
|
||||||
|
const statusConfig = {
|
||||||
|
1: {
|
||||||
|
chinese: '处理中',
|
||||||
|
color: {
|
||||||
|
bg: 'bg-yellow-100',
|
||||||
|
text: 'text-yellow-800',
|
||||||
|
dot: 'bg-yellow-500',
|
||||||
|
amount: 'text-yellow-500'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
chinese: '提现成功',
|
||||||
|
color: {
|
||||||
|
bg: 'bg-green-100',
|
||||||
|
text: 'text-green-800',
|
||||||
|
dot: 'bg-green-500',
|
||||||
|
amount: 'text-green-500'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
chinese: '提现失败',
|
||||||
|
color: {
|
||||||
|
bg: 'bg-red-100',
|
||||||
|
text: 'text-red-800',
|
||||||
|
dot: 'bg-red-500',
|
||||||
|
amount: 'text-red-500'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const page = ref(1)
|
||||||
|
const pageSize = ref(10)
|
||||||
|
const total = ref(0)
|
||||||
|
const list = ref([])
|
||||||
|
const loading = ref(false)
|
||||||
|
const loadMoreStatus = ref('more') // 'more'|'loading'|'noMore'
|
||||||
|
|
||||||
|
// 账户脱敏处理
|
||||||
|
const maskName = (name) => {
|
||||||
|
if (!name || typeof name !== 'string') return ''
|
||||||
|
if (name.length <= 7) return name
|
||||||
|
return name.substring(0, 3) + '****' + name.substring(7)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 状态转中文
|
||||||
|
const statusToChinese = (status) => {
|
||||||
|
return statusConfig[status]?.chinese || '未知状态'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取状态样式
|
||||||
|
const getStatusStyle = (status) => {
|
||||||
|
const config = statusConfig[status] || {}
|
||||||
|
return `${config.color?.bg || 'bg-gray-100'} ${config.color?.text || 'text-gray-800'}`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取小圆点颜色
|
||||||
|
const getDotColor = (status) => {
|
||||||
|
return statusConfig[status]?.color.dot || 'bg-gray-500'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取金额颜色
|
||||||
|
const getAmountColor = (status) => {
|
||||||
|
return statusConfig[status]?.color.amount || 'text-gray-500'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载更多数据
|
||||||
|
const onLoadMore = async () => {
|
||||||
|
if (loadMoreStatus.value === 'noMore') return
|
||||||
|
|
||||||
|
page.value++
|
||||||
|
await getData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取数据
|
||||||
|
const getData = async () => {
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
loadMoreStatus.value = 'loading'
|
||||||
|
|
||||||
|
const res = await getWithdrawalRecords({
|
||||||
|
page: page.value,
|
||||||
|
page_size: pageSize.value
|
||||||
|
})
|
||||||
|
|
||||||
|
if (res.code === 200) {
|
||||||
|
if (page.value === 1) {
|
||||||
|
list.value = res.data.list
|
||||||
|
total.value = res.data.total
|
||||||
|
} else {
|
||||||
|
list.value.push(...res.data.list)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list.value.length >= res.data.total || res.data.list.length < pageSize.value) {
|
||||||
|
loadMoreStatus.value = 'noMore'
|
||||||
|
} else {
|
||||||
|
loadMoreStatus.value = 'more'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uni.showToast({
|
||||||
|
title: res.msg || '加载失败',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '网络错误',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 页面加载
|
||||||
|
onMounted(() => {
|
||||||
|
getData()
|
||||||
|
})
|
||||||
|
|
||||||
|
// 页面下拉刷新
|
||||||
|
const onPullDownRefresh = () => {
|
||||||
|
page.value = 1
|
||||||
|
loadMoreStatus.value = 'more'
|
||||||
|
getData().then(() => {
|
||||||
|
uni.stopPullDownRefresh()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出页面生命周期方法
|
||||||
|
defineExpose({
|
||||||
|
onPullDownRefresh
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* 自定义样式 */
|
||||||
|
</style>
|
||||||
|
<route type="page" lang="json">{
|
||||||
|
"layout": "page",
|
||||||
|
"title": "提现记录",
|
||||||
|
"auth": true,
|
||||||
|
"agent": true
|
||||||
|
}</route>
|
||||||
6
src/shims.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export {}
|
||||||
|
|
||||||
|
declare module 'vue' {
|
||||||
|
type Hooks = App.AppInstance & Page.PageInstance
|
||||||
|
interface ComponentCustomOptions extends Hooks {}
|
||||||
|
}
|
||||||
BIN
src/static/image/ai_qinggan.png
Normal file
|
After Width: | Height: | Size: 59 KiB |
1
src/static/image/alipay_icon.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1732336609573" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5954" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"><path d="M860.16 0C950.272 0 1024 73.889684 1024 164.163368v531.509895s-32.768-4.122947-180.224-53.355789c-40.96-14.362947-96.256-34.896842-157.696-57.478737 36.864-63.595789 65.536-137.485474 86.016-215.444211h-202.752v-71.841684h247.808V256.512h-247.808V135.437474h-100.352c-18.432 0-18.432 18.458947-18.432 18.458947v104.663579H200.704v41.040842h249.856v69.793684H243.712v41.013895H645.12c-14.336 51.307789-34.816 98.519579-57.344 141.608421-129.024-43.115789-268.288-77.985684-356.352-55.403789-55.296 14.362947-92.16 38.992842-112.64 63.595789-96.256 116.978526-26.624 295.504842 176.128 295.504842 120.832 0 237.568-67.718737 327.68-178.526316C757.76 742.858105 1024 853.692632 1024 853.692632v6.144C1024 950.110316 950.272 1024 860.16 1024H163.84C73.728 1024 0 950.137263 0 859.836632V164.163368C0 73.889684 73.728 0 163.84 0h696.32zM268.126316 553.121684c93.049263-10.374737 180.062316 26.974316 283.270737 78.874948-74.886737 95.501474-165.941895 155.701895-256.970106 155.701894-157.830737 0-204.368842-126.652632-125.466947-197.200842 26.300632-22.851368 72.838737-35.301053 99.166316-37.376z" fill="#00A0EA" p-id="5955"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
1
src/static/image/apple.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1732615487801" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3146" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"><path d="M905.714286 694.285714q-22.285714 71.428571-70.285714 142.857143-73.714286 112-146.857143 112-28 0-80-18.285714-49.142857-18.285714-86.285714-18.285714-34.857143 0-81.142857 18.857143-46.285714 19.428571-75.428571 19.428571-86.857143 0-172-148-84-149.142857-84-287.428571 0-130.285714 64.571429-213.714286 64-82.285714 162.285714-82.285714 41.142857 0 101.142857 17.142857 59.428571 17.142857 78.857143 17.142857 25.714286 0 81.714286-19.428571 58.285714-19.428571 98.857143-19.428571 68 0 121.714286 37.142857 29.714286 20.571429 59.428571 57.142857-45.142857 38.285714-65.142857 67.428571-37.142857 53.714286-37.142857 118.285714 0 70.857143 39.428571 127.428571t90.285714 72zm-214.857143-670.285714q0 34.857143-16.571429 77.714286-17.142857 42.857143-53.142857 78.857143-30.857143 30.857143-61.714286 41.142857-21.142857 6.285714-59.428571 9.714286 1.714286-85.142857 44.571429-146.857143 42.285714-61.142857 142.857143-84.571429 0.571429 1.714286 1.428571 6.285714t1.428571 6.285714q0 2.285714 2.857143 5.714286t2.857143 5.714286z" p-id="3147"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
BIN
src/static/image/banner.png
Normal file
|
After Width: | Height: | Size: 142 KiB |
BIN
src/static/image/bg_2.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
src/static/image/bg_icon.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
75
src/static/image/empty.svg
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 1024 1024" height="1024px" width="1024px">
|
||||||
|
<title>空空如也</title>
|
||||||
|
<defs>
|
||||||
|
<rect rx="22.1405405" height="1024" width="1024" y="0" x="0" id="path-1"></rect>
|
||||||
|
<linearGradient id="linearGradient-3" y2="64.8840762%" x2="50%" y1="-33.7184979%" x1="115.913479%">
|
||||||
|
<stop offset="0%" stop-color="#6CADFF"></stop>
|
||||||
|
<stop offset="100%" stop-opacity="0" stop-color="#FFFFFF"></stop>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="linearGradient-4" y2="100%" x2="70.4980572%" y1="-20.569195%" x1="10.5031837%">
|
||||||
|
<stop offset="0%" stop-color="#6CADFF"></stop>
|
||||||
|
<stop offset="100%" stop-opacity="0" stop-color="#FFFFFF"></stop>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="linearGradient-5" y2="104.73608%" x2="38.801584%" y1="-97.78046%" x1="100.191761%">
|
||||||
|
<stop offset="0%" stop-color="#6CADFF"></stop>
|
||||||
|
<stop offset="100%" stop-color="#FFFFFF"></stop>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="linearGradient-6" y2="100%" x2="50%" y1="-27.9013949%" x1="50%">
|
||||||
|
<stop offset="0%" stop-color="#6CADFF"></stop>
|
||||||
|
<stop offset="100%" stop-opacity="0" stop-color="#FFFFFF"></stop>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="linearGradient-7" y2="100%" x2="50%" y1="-27.9013949%" x1="50%">
|
||||||
|
<stop offset="0%" stop-color="#6CADFF"></stop>
|
||||||
|
<stop offset="100%" stop-opacity="0" stop-color="#FFFFFF"></stop>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="linearGradient-8" y2="100%" x2="50%" y1="-221.1569%" x1="50%">
|
||||||
|
<stop offset="0%" stop-color="#D2D2D2"></stop>
|
||||||
|
<stop offset="100%" stop-opacity="0" stop-color="#D2D2D2"></stop>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="linearGradient-9" y2="53.7335012%" x2="73.0360423%" y1="48.1527472%" x1="67.5652976%">
|
||||||
|
<stop offset="0%" stop-opacity="0" stop-color="#858585"></stop>
|
||||||
|
<stop offset="100%" stop-opacity="0.5" stop-color="#616161"></stop>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<g fill-rule="evenodd" fill="none" stroke-width="1" stroke="none" id="空空如也">
|
||||||
|
<g>
|
||||||
|
<mask fill="white" id="mask-2">
|
||||||
|
<use xlink:href="#path-1"></use>
|
||||||
|
</mask>
|
||||||
|
<g id="蒙版"></g>
|
||||||
|
<g mask="url(#mask-2)" id="编组-3">
|
||||||
|
<g transform="translate(0, 238.0108)">
|
||||||
|
<g fill-rule="evenodd" fill="none" stroke-width="1" stroke="none" id="编组-2">
|
||||||
|
<g fill-rule="nonzero" transform="translate(162.5599, 0)" id="编组">
|
||||||
|
<polygon points="592.450826 498.100707 589.3555 489.432325 587.255101 479.632083 586.398359 469.500567 586.868185 459.341443 588.001295 452.660716 589.852963 446.421689 592.367915 440.541544 595.711972 435.158313 599.857498 430.520452 604.887401 426.517537 606.932527 428.091097 608.342006 430.133964 609.060564 432.508107 609.032927 435.075494 608.286732 439.989418 607.070711 451.308006 606.794343 463.813666 607.540538 469.25211 608.894743 472.371623 610.248947 473.448269 612.072979 473.393057 614.864299 471.791891 619.037461 467.319668 626.084854 456.884481 638.963619 434.136879 651.870021 411.527309 659.000324 400.705634 667.015006 390.325661 673.758394 383.451689 680.225413 378.42734 686.471338 374.921338 693.159452 372.491982 699.156645 371.470549 704.628738 371.691399 709.8521 373.126928 714.191083 375.639102 717.783871 379.283135 720.32646 383.755358 721.818849 389.331833 722.150491 396.343837 721.155565 403.328234 718.530066 411.610128 713.942351 421.493188 708.884811 429.471413 700.400302 443.19175 696.006046 451.031943 692.219799 458.706498 689.345568 466.270628 688.212458 472.09556 688.46119 475.601562 689.760121 477.920492 692.109252 479.438839 695.232214 479.714902 698.299903 479.052351 705.070927 476.540176 711.924862 473.393057 718.778797 470.383969 725.384001 468.258282 729.805894 467.595731 734.144877 467.8994 738.511497 469.224503 741.966102 471.377796 744.702148 474.055608 746.857821 477.368366 748.985858 483.000054 750.008421 489.349506 750.036057 495.864596 749.206952 502.158835 746.526179 511.40695 742.104286 520.737884 736.21764 529.406266 728.866242 537.16364 723.283601 541.608256 717.203498 545.25229 710.570657 548.123346 703.606175 550.000575 696.199503 550.745946 688.212458 550.359458 675.554788 548.09574 661.238907 544.396494 646.923027 539.537783 632.855878 533.464394 623.76336 528.688502 615.582857 523.526121 608.203822 517.977252 601.543344 511.683013 596.375256 505.140317 592.533736 498.349164" fill="url(#linearGradient-3)" id="路径"></polygon>
|
||||||
|
<polygon points="39.9075893 440.458725 31.7823599 436.096928 23.6571305 430.216783 16.222822 423.287598 9.75580266 415.447406 4.58771456 406.834236 1.21602073 397.834578 0.0552736694 391.623157 0 385.411737 1.05019972 379.117498 3.59278851 378.896647 5.99719313 379.421167 8.12522941 380.635845 9.83871316 382.54068 12.6023966 386.654021 19.2905106 395.87453 27.4986505 405.315889 31.6994494 408.849497 34.7947749 410.257419 36.5082587 410.146993 37.8348267 408.877103 38.8297528 405.840409 38.9403001 399.711807 37.1439059 387.26136 31.3401706 361.863552 25.7022563 336.465744 23.7676779 323.601202 22.8003886 310.488203 23.242578 300.881206 24.6520566 292.820163 26.9459139 286.056616 30.2899709 279.789983 34.0762172 275.014091 38.2770161 271.508089 43.1134622 269.078734 48.0051819 268.0573 53.1179963 268.333363 57.9820792 269.934529 62.8185253 273.081649 67.7655187 278.050785 71.6899493 283.903324 75.3103746 291.798729 78.5162474 302.206309 80.1191838 311.509637 83.0210515 327.383267 85.0385404 336.134468 87.4153082 344.361149 90.3448127 351.870067 93.4125013 356.977234 95.9550901 359.378984 98.4700421 360.234779 101.206089 359.765472 103.748678 357.888243 105.572709 355.320856 108.916766 348.916191 111.873907 342.014613 114.913959 335.195853 118.368563 329.23289 121.215157 325.782101 124.642125 323.159501 128.78765 321.254665 132.794991 320.509295 136.636511 320.674933 140.450394 321.696366 145.81194 324.429391 150.841844 328.459913 155.236101 333.291018 158.856526 338.508611 162.670409 345.575827 166.788298 354.823942 170.381086 364.762215 173.448775 375.639102 175.604448 386.764446 176.5441 397.641334 176.378279 404.874188 175.41099 411.499703 173.697506 417.628304 171.04437 423.315205 167.396308 428.173916 162.642772 432.314863 157.032495 435.62762 150.344381 438.691921 142.440246 441.424946 130.058944 444.572066 116.2958 446.835784 102.173378 448.160887 87.9956817 448.547375 74.0390802 447.967642 61.3537731 446.504508 49.4422973 443.964727 40.1563208 440.707182" fill="url(#linearGradient-4)" id="路径"></polygon>
|
||||||
|
<path fill="url(#linearGradient-5)" id="形状结合" d="M648.498327,284.510663 L644.71208,289.9215 L641.589118,295.939676 L639.350534,302.344341 L638.217424,308.914643 L638.355608,315.567765 L640.013818,322.165674 L642.224765,326.058164 L644.988449,328.92922 L647.94559,331.579426 L650.571089,334.505696 L652.284573,338.039304 L652.83731,343.588173 L651.648926,349.689168 L649.576163,355.458887 L646.619022,360.952544 L640.428371,370.780391 L633.408615,379.945687 L625.615028,388.53125 L630.092195,393.086292 L633.159883,398.524736 L634.735183,404.570518 L634.707546,410.947577 L633.215157,417.186603 L630.755479,423.066748 L627.411422,428.477585 L623.210623,433.336296 L620.115297,436.234959 L616.384325,438.25022 L612.459894,438.691921 L608.535464,438.25022 L595.960704,435.517195 L583.77286,431.624705 L571.944295,426.57275 L566.555112,423.729299 L561.608118,420.250904 L557.545504,416.027138 L554.864731,410.91997 L554.035626,406.723811 L554.25672,403.769935 L555.251646,401.699462 L557.877146,399.049256 L561.41466,396.895963 L562.685955,396.178199 L566.30638,393.886875 L569.180611,390.822574 L571.253374,386.129501 L572.524668,380.939514 L574.127604,370.55954 L575.150167,359.075314 L575.896362,352.781075 L577.444025,347.121781 L579.212782,343.864236 L581.783008,341.352061 L585.292886,339.557651 L592.533736,338.260154 L599.74695,336.935051 L604.389938,334.754152 L608.176185,331.827883 L611.243873,328.128637 L614.974846,321.696366 L618.180719,314.518725 L618.899277,312.751921 L622.630249,304.663271 L624.785922,300.908813 L627.273238,297.568449 L630.313289,294.86303 L634.292994,292.461281 L642.611681,288.430759 L646.28738,286.387892 L648.498327,284.510663 Z M619.009824,341.73855 L615.195941,346.514442 L606.158696,359.323771 L600.935334,367.854122 L595.463241,378.013245 L590.626795,388.807313 L586.702364,400.346752 L584.795423,408.269764 L583.717586,416.192776 L583.468855,424.115788 L595.325057,424.115788 L596.319983,416.109957 L599.912771,395.929742 L602.925186,383.313657 L607.153622,369.234437 L612.432257,355.238037 L619.009824,341.73855 Z"></path>
|
||||||
|
<polygon points="125.250135 60.7614951 125.250135 60.7614951 124.559214 54.3016178 122.431178 48.4214731 119.059484 43.2314863 114.55468 38.9249014 109.165497 35.7777818 102.947209 33.9005525 96.4525532 33.5416704 90.3171759 34.7011355 84.6239879 37.21331 79.6769945 40.9677686 75.6972903 45.7712671 72.8783332 51.6238055 66.8258663 52.3415696 61.3537731 54.5500746 56.6278743 58.111289 52.9245385 62.9147875 50.658318 68.5464754 49.9673972 74.3990138 50.8517759 80.2515521 53.3114542 85.8004211 57.1529742 90.4934943 61.9894203 93.8890708 67.5444241 95.931938 73.569254 96.4564579 123.011551 96.4564579 127.626903 95.7663001 131.634244 94.1375276 135.171759 91.5149279 137.963079 88.1193514 139.78711 84.1992549 140.699126 79.6442132 140.367484 74.9787463 139.013279 70.8654057 136.664148 67.1661597 133.513549 64.1294653 129.727302 62.0037792" fill="url(#linearGradient-6)" id="路径"></polygon>
|
||||||
|
<polygon points="329.569254 33.7073083 329.569254 33.5416704 329.127065 28.130833 327.911044 23.0788777 325.921192 18.3305919 321.665119 11.9259273 316.054842 6.65312145 311.715859 3.89249014 306.934686 1.84962298 301.656051 0.496913635 296.266868 0 291.071143 0.35888207 286.041239 1.49074091 278.993846 4.61025428 272.858469 9.22050857 269.376228 13.0301798 266.557271 17.3643709 264.318687 22.305901 259.205873 22.8856335 254.507611 24.2383429 250.196265 26.364029 246.299471 29.2074792 243.010688 32.6030557 240.302278 36.5783648 238.284789 40.9677686 237.096405 45.6608418 236.681853 50.7956161 237.234589 55.902784 238.588794 60.5682509 240.744467 64.8748357 243.591061 68.7673259 246.990392 72.0524771 250.970096 74.7855021 255.364353 76.7731567 260.062615 77.9878345 265.203066 78.3743228 326.612113 78.3743228 332.360574 77.6565587 337.501026 75.5860852 341.950556 72.3285403 345.488071 68.1047744 347.892475 63.1080317 348.997949 57.4211312 348.611033 51.6514118 346.842276 46.378606 343.885134 41.7683517 339.877793 37.9862868 335.041347 35.2808681 329.403433 33.8453398" fill="url(#linearGradient-7)" id="路径"></polygon>
|
||||||
|
</g>
|
||||||
|
<polygon points="1024 550.359458 999.596675 534.403009 973.839145 519.164324 946.672136 504.615797 918.040376 490.785034 889.159883 478.224161 859.118644 466.491478 827.833747 455.669804 795.305193 445.703925 762.693728 437.007936 729.114974 429.360987 694.541293 422.735472 658.917413 417.186603 623.348807 412.880018 587.034006 409.760505 549.945374 407.883276 512 407.193118 474.054626 407.883276 436.965994 409.760505 400.623556 412.880018 365.082587 417.186603 329.458707 422.735472 294.885026 429.360987 261.306272 437.007936 228.694807 445.703925 196.166253 455.669804 164.881356 466.491478 134.840117 478.224161 105.931987 490.785034 77.3278635 504.615797 50.160855 519.164324 24.4033251 534.403009 0 550.359458" fill-rule="nonzero" fill="url(#linearGradient-8)" id="路径"></polygon>
|
||||||
|
</g>
|
||||||
|
<polygon points="168.585366 389.215532 314.612039 389.215532 461.536985 469.266954 317.214646 465.594981" fill-rule="nonzero" fill="url(#linearGradient-9)" stroke="none" id="矩形"></polygon>
|
||||||
|
<polygon points="481.155803 208.25638 479.722777 299.451613 688.569371 236.303345" fill-rule="nonzero" fill="#B8D6FF" stroke="none" id="路径"></polygon>
|
||||||
|
<polygon points="314.788219 244.959395 481.155803 208.631248 481.155803 264.00952" fill-rule="nonzero" fill="#9CC6FF" stroke="none" id="路径"></polygon>
|
||||||
|
<polygon points="314.788219 244.959395 511.147006 264.384388 511.147006 512.547202 314.788219 465.075243" fill-rule="nonzero" fill="#64ADFF" stroke="none" id="路径"></polygon>
|
||||||
|
<polygon points="314.788219 244.959395 511.283486 263.617612 489.889892 383.428742 314.788219 346.994453" fill-rule="nonzero" fill="#429BFF" stroke="none" id="矩形"></polygon>
|
||||||
|
<polygon points="511.147006 264.384388 688.569371 236.303345 688.569371 458.600245 511.147006 512.547202" opacity="0.99" fill-rule="nonzero" fill="#9CC5FF" stroke="none" id="路径"></polygon>
|
||||||
|
<polygon points="511.283486 264.997809 671.897967 239.34913 688.569371 344.292902 535.025228 383.428742" fill-rule="nonzero" fill="#64ADFF" stroke="none" id="矩形"></polygon>
|
||||||
|
<polygon points="314.788219 244.959395 267.566573 324.806343 465.801946 362.565804 511.147006 264.384388" fill-rule="nonzero" fill="#9CC6FF" stroke="none" id="路径"></polygon>
|
||||||
|
<polygon points="511.147006 264.384388 566.898574 362.565804 745.583366 317.786082 688.569371 236.303345" opacity="0.99" fill-rule="nonzero" fill="#9DC6FF" stroke="none" id="路径"></polygon>
|
||||||
|
<path stroke-dasharray="11.05477807439905,8.29108355579929" fill="none" stroke-width="5.52738904" stroke="#9DC6FF" id="路径-19" d="M583.139543,151.843183 C532.695151,184.875351 501.824507,214.257045 511.001904,232.450753 C523.475834,257.179661 544.659409,246.913618 547.874,236.537816 C551.088588,226.162013 542.242035,205.908265 523.475834,216.933951 C504.709635,227.959637 484.261479,247.732311 479.722777,267.098145"></path>
|
||||||
|
<g transform="translate(555.0939, 41.4059)" fill-rule="evenodd" fill="none" stroke-width="1" stroke="none" id="飞机">
|
||||||
|
<polygon points="163.057977 9.09494702e-13 0 30.854292 41.4554178 58.3378535" fill-rule="nonzero" fill="#9DC6FF" id="路径-16备份"></polygon>
|
||||||
|
<polygon points="163.057977 0 41.4554178 58.3378535 41.4554178 104.894966" fill-rule="nonzero" fill="#64ADFF" id="路径-16备份-2"></polygon>
|
||||||
|
<polygon points="163.057977 0 41.4554178 58.3378535 65.4910753 84.1769753" fill-rule="nonzero" fill="#429BFF" id="路径-16备份-2"></polygon>
|
||||||
|
<polygon points="163.057977 0 58.9237102 70.0692202 108.951745 102.134572" fill-rule="nonzero" fill="#9DC6FF" id="路径-16备份"></polygon>
|
||||||
|
<line stroke-linecap="round" stroke-width="2.76369452" stroke="#429BFF" id="路径-16" y2="32.1173295" x2="0.501635492" y1="58.3378535" x1="41.4554178"></line>
|
||||||
|
<line stroke-linecap="round" stroke-width="2.76369452" stroke="#429BFF" id="路径-17" y2="101.744072" x2="107.648858" y1="71.0666294" x1="59.7211085"></line>
|
||||||
|
<line stroke-linecap="round" stroke-width="2.76369452" stroke="#429BFF" id="路径-18" y2="103.502333" x2="41.4554178" y1="58.3378535" x1="41.4554178"></line>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 15 KiB |
1
src/static/image/gdrqgx_icon.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1733324501181" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6340" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"><path d="M339.162366 1023.729653c-95.973119 0-127.972351-435.012601-127.97235-518.254842 0-63.900156 121.557758-115.167743 121.557758-115.167743h127.972351s6.390016 51.193856 31.950078 63.900156C466.962679 479.865595 441.476347 742.224889 460.670971 799.759606a214.974871 214.974871 0 0 0 51.193856 95.973119 227.681171 227.681171 0 0 0 63.900156-95.973119c6.414593-51.193856 0-307.212288-25.58464-345.503228a90.394144 90.394144 0 0 0 25.58464-63.900156h127.97235s115.143166 44.80384 108.753151 115.167743c0 76.778495-44.80384 505.450234-140.776959 518.254842z m511.864827-204.750846c12.780031-51.193856 19.170047-319.918589-19.194624-390.307068s-147.166975-89.558526-147.166974-102.363135c0-6.390016 25.584639-12.780031 44.80384-12.780031h211.140861a84.692284 84.692284 0 0 1 83.193088 83.168511l-25.682947 351.893244c-6.414593 51.193856-44.779263 70.38848-102.387712 70.388479z m-729.395704 0c-57.583871 0-89.583103-19.194624-102.363134-70.388479L0 396.697084a84.692284 84.692284 0 0 1 83.168511-83.168511h204.750846c19.170047 0 44.80384 0 44.80384 12.780031s-108.777727 31.950078-147.166975 102.363135-31.950078 339.162366-19.194624 390.307068z m230.261755-652.617209A169.384567 169.384567 0 0 1 518.254842 0a169.581183 169.581183 0 0 1 0 339.162366 170.785455 170.785455 0 0 1-166.361598-172.800768z m377.50246 12.780032a101.060554 101.060554 0 0 1 102.363134-102.363135 95.850234 95.850234 0 0 1 102.363135 95.973119 101.060554 101.060554 0 0 1-102.363135 102.363135 99.635089 99.635089 0 0 1-102.363134-95.973119z m-639.812601 0A101.060554 101.060554 0 0 1 191.946238 76.778495a99.659666 99.659666 0 0 1 102.363134 95.973119 101.060554 101.060554 0 0 1-102.363134 102.363135 95.850234 95.850234 0 0 1-102.363135-95.973119z" fill="#4B64FA" p-id="6341"></path></svg>
|
||||||
|
After Width: | Height: | Size: 2.0 KiB |
1
src/static/image/grbl_icon.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1733326573199" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9341" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"><path d="M390.063158 950.649263a104.286316 104.286316 0 0 0-10.644211 50.149053H26.947368v-150.042948c0-220.779789 177.259789-400.410947 395.290948-400.410947 68.985263 0 133.820632 18.081684 190.383158 49.744842l-222.558316 450.56z m32.175158-550.346105c-109.029053 0-197.658947-89.869474-197.658948-200.218947C224.579368 89.788632 313.236211 0 422.265263 0s197.632 89.734737 197.632 200.218947c0 110.322526-88.656842 200.084211-197.632 200.084211z m571.930947 583.895579c8.973474 18.270316-4.149895 39.801263-24.306526 39.801263H480.741053c-20.156632 0-33.306947-21.530947-24.306527-39.801263l244.601263-495.400421a27.028211 27.028211 0 0 1 48.613053 0l244.493474 495.400421z m-296.016842-42.738526h54.298947v-55.080422h-54.298947v55.080422z m0-110.133895h54.298947v-137.620211h-54.298947v137.620211z" fill="#4B64FA" p-id="9342"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
1
src/static/image/grss_icon.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1733326791000" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="28866" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"><path d="M425.3 245.5c80.1 0 126.4 50.8 126.4 147.4 0 55.2-19 102.2-47.9 132.4-9.1 15.2-13.9 37.5-13.9 47.4 0 5.9 4.6 13.7 13 21.9-20.1 9.4-32.7 29-32.5 50.5v22.8c0 24.7 16 45.4 38.5 52.9-8.7 19.1-6.1 41.1 6.7 57.9h-339c-12.7 0-23-10.1-23-24 0-30.9 4.6-51.4 13.7-61.4 22.5-24.7 70.7-46.4 118.3-62.9 47.6-16.4 74.5-42.8 74.5-57.6 0-10.2-5-33.6-14.6-48.8-28.2-30.2-46.6-76.7-46.6-131 0.1-96.8 46.4-147.5 126.4-147.5z m241 266.7l66.9 68.4-9.8 8.6 15.4 16.6-10.3 9 128.3 125.3c6.6 6.3 22.4 21.6 7.1 33.8-15.4 12.2-29.4-4.9-36-11.2l-125.6-124-12.2 10.8-15.4-16.6-8.9 7.6-66.4-68.4 66.9-59.9z m50.8 205.9c7.9 0 14.5 6.3 14.5 14l0.5 3.6c11.7 0 21 9 21 20.2v18h-201v-18c0-11.2 9.3-20.2 21-20.2h1.4v-3.6c0-7.7 6.6-14 14.5-14h128.1zM593.3 578.4l65.5 67.5c6.6 6.7 6.6 18-1 24.3l-10.3 9c-7 6.3-18.7 5.8-25.2-1l-65.5-67.5c-6.6-6.7-6.6-18 1-24.3l10.3-9c7-6.2 18.7-6.2 25.2 1z m116.5-104l65.5 67.5c6.6 6.7 6.6 18-1 24.3l-10.3 9c-7 6.3-18.7 6.3-25.2-0.9l-65.5-67.4c-6.6-6.7-6.6-18 1-24.3l10.3-9c6.9-6.4 18.1-5.9 25.2 0.8z" fill="#4B64FA" p-id="28867"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
BIN
src/static/image/gzh_qrcode.jpg
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
src/static/image/head_shot.webp
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
src/static/image/icon_1.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
src/static/image/icon_2.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
1
src/static/image/icon_bg copy.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1740754594473" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="36608" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M102.4 256a102.4 102.4 0 0 1 102.4-102.4h340.2752a102.4 102.4 0 0 1 95.0784 64.3584L665.6 281.6c3.3792 8.3968 5.4272 17.0496 6.2464 25.6H819.2a51.2 51.2 0 0 1 51.2 51.2v512a51.2 51.2 0 0 1-51.2 51.2H153.6a51.2 51.2 0 0 1-51.2-51.2V256z" fill="#2357DF" opacity=".5" p-id="36609"></path><path d="M238.592 409.6h681.8304a51.2 51.2 0 0 1 50.176 61.44l-83.8656 409.6a51.2 51.2 0 0 1-50.176 40.96H154.7776a51.2 51.2 0 0 1-50.176-61.44l83.8656-409.6a51.2 51.2 0 0 1 50.176-40.96z" fill="#2357DF" p-id="36610"></path><path d="M358.4 640m25.6 0l307.2 0q25.6 0 25.6 25.6l0 0q0 25.6-25.6 25.6l-307.2 0q-25.6 0-25.6-25.6l0 0q0-25.6 25.6-25.6Z" fill="#FFFFFF" p-id="36611"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1007 B |
1
src/static/image/icon_bg.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1740754594473" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="36608" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M102.4 256a102.4 102.4 0 0 1 102.4-102.4h340.2752a102.4 102.4 0 0 1 95.0784 64.3584L665.6 281.6c3.3792 8.3968 5.4272 17.0496 6.2464 25.6H819.2a51.2 51.2 0 0 1 51.2 51.2v512a51.2 51.2 0 0 1-51.2 51.2H153.6a51.2 51.2 0 0 1-51.2-51.2V256z" fill="#2357DF" opacity=".5" p-id="36609"></path><path d="M238.592 409.6h681.8304a51.2 51.2 0 0 1 50.176 61.44l-83.8656 409.6a51.2 51.2 0 0 1-50.176 40.96H154.7776a51.2 51.2 0 0 1-50.176-61.44l83.8656-409.6a51.2 51.2 0 0 1 50.176-40.96z" fill="#2357DF" p-id="36610"></path><path d="M358.4 640m25.6 0l307.2 0q25.6 0 25.6 25.6l0 0q0 25.6-25.6 25.6l-307.2 0q-25.6 0-25.6-25.6l0 0q0-25.6 25.6-25.6Z" fill="#FFFFFF" p-id="36611"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1007 B |
1
src/static/image/icon_bz.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1741855510780" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2636" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M853.64224 76.7488H155.25888c-49.03424 0-88.75008 40.14592-88.75008 89.18528l-0.41472 766.18752c-0.00512 13.45024 16.256 20.18816 25.76384 10.68032l148.16256-148.16256a15.09376 15.09376 0 0 1 10.6752-4.41856h602.94656c57.34912 0 104.26368-46.9248 104.26368-104.2688V181.02784c0-57.35424-46.91456-104.27904-104.26368-104.27904z m-299.33056 544.1792a15.09376 15.09376 0 0 1-15.08864 15.08864H472.73472a15.09376 15.09376 0 0 1-15.09376-15.08864v-60.5696a15.09376 15.09376 0 0 1 15.09376-15.08864h66.4832a15.09376 15.09376 0 0 1 15.08864 15.08864v60.5696h0.00512z m39.45984-199.94112c-35.16928 24.76032-51.65056 48.84992-49.39776 72.30464 0.05632 0.4608 0.08704 0.9216 0.08704 1.3824a15.08864 15.08864 0 0 1-15.09376 15.08864h-48.73216a15.08352 15.08352 0 0 1-15.08864-15.08864v-6.60992c-1.32608-40.61184 15.6672-72.77056 50.92352-96.41984 0.24064-0.16384 0.49152-0.33792 0.72704-0.51712 32.6144-24.86784 48.2816-48.45056 46.99136-70.74816-2.62144-27.33056-18.0992-42.40896-46.4384-45.29152a16.45568 16.45568 0 0 0-1.81248-0.08192c-31.42144 0.3584-52.42368 19.4816-62.98112 57.3696a15.13472 15.13472 0 0 1-17.4592 10.85952L374.1696 331.23328a15.11424 15.11424 0 0 1-11.70944-18.61632c20.54144-80.15872 76.60032-118.99392 168.17152-116.5312 81.30048 5.25312 126.0032 41.89696 134.08256 109.88544 0.0512 0.38912 0.08192 0.78848 0.10752 1.1776 2.3808 44.47744-21.2992 82.41152-71.05024 113.83808z" fill="#397B8B" p-id="2637"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.7 KiB |
1
src/static/image/icon_bz_2.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1740754612192" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="39491" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M887.456923 331.598922C867.952667 146.308492 707.042557 2.464607 516.876064 0.026575 307.205315-2.411457 134.105045 163.374716 134.105045 368.169401c0 134.091758 73.140959 253.555324 190.166494 319.382188l7.314096 75.578991c2.438032 14.628192 14.628192 24.38032 29.256383 24.380319h299.877932c14.628192 0 26.818352-12.19016 29.256383-24.380319l7.314096-75.578991c131.653726-70.702927 204.794685-207.232717 190.166494-355.952667zM321.833507 360.855305c0 17.066224-14.628192 29.256384-29.256384 29.256384s-29.256384-12.19016-29.256383-29.256384c0-9.752128 0-17.066224 2.438031-26.818351 2.438032-17.066224 17.066224-26.818352 34.132448-26.818352 17.066224 2.438032 29.256384 17.066224 26.818352 31.694416-2.438032 9.752128-4.876064 14.628192-4.876064 21.942287z m190.166493-185.290429c-70.702927 0-134.091758 36.570479-165.786174 97.521279-4.876064 9.752128-14.628192 14.628192-26.818351 14.628191-4.876064 0-9.752128 0-14.628192-2.438032-14.628192-7.314096-19.504256-24.38032-12.19016-39.008511 43.884575-78.017023 126.777662-126.777662 219.422877-126.777662 17.066224 0 29.256384 12.19016 29.256384 29.256383s-12.19016 26.818352-29.256384 26.818352zM343.775794 833.833507c-2.438032 9.752128-2.438032 17.066224-2.438032 26.818351 0 90.207183 75.578991 163.348142 168.224206 163.348142 92.645215 0 168.224206-73.140959 168.224206-163.348142 0-9.752128 0-19.504256-2.438032-26.818351H343.775794z" fill="#F5B53A" p-id="39492"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.7 KiB |
1
src/static/image/icon_index_apistore.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1739431145867" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1589" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"><path d="M795.442 73a38 38 0 0 1 35.204 23.693L951.218 393.37c7.042 17.326 0.43 37.2-15.589 46.854l-2.63 1.583L933 860c0 52.49-42.125 95.14-94.412 95.987L837 956H198c-52.49 0-95.14-42.125-95.987-94.412L102 860V450.436l-16.391-10.474c-15.605-9.972-21.727-29.72-14.57-46.741l0.22-0.515L201.222 95.764A38 38 0 0 1 236.034 73z m-77.406 373.306l-37.677 22.708c-41.905 25.255-94.4 24.824-135.854-1.01l-1.252-0.79-32.717-20.908-37.677 22.708c-41.905 25.255-94.4 24.824-135.854-1.01l-1.252-0.79-32.717-20.908-39.657 23.901c-30.927 18.64-67.792 22.875-101.38 12.828L162 860c0 19.683 15.797 35.677 35.405 35.995L198 896h639c19.683 0 35.677-15.797 35.995-35.405L873 860V476.714c-39.43 17.315-85.348 14.08-122.247-9.5l-32.717-20.908z m62.598-313.307H250.42L133.766 399.531l28.744 18.37a66.275 66.275 0 0 0 69.25 1.303l0.648-0.386 71.555-43.124 64.099 40.962a70 70 0 0 0 73.827 0.969l69.574-41.931 64.099 40.962a70 70 0 0 0 73.827 0.969l69.574-41.931 64.099 40.962a70 70 0 0 0 73.827 0.969l31.665-19.084-107.92-265.542z" fill="#1296db" p-id="1590"></path><path d="M335.81 565.173l54.876 0.41 82.301 190-55.056 23.85-16.649-38.434h-78.957l-17.52 38.844-54.693-24.67 85.699-190z m26.792 86.529l-13.215 29.297h25.905l-12.69-29.297zM502.395 561.66l60 0.11v0.236l0.492 0.005c45.692 0.736 82.613 37.772 82.613 83.489 0 45.717-36.92 82.753-82.613 83.489l-0.803 0.005-0.084 46.061-60-0.11 0.395-213.286z m59.888 60.352l-0.087 46.978c12.991-0.36 23.304-10.8 23.304-23.49 0-12.662-10.267-23.083-23.217-23.488zM691.697 561.66l60 0.11-0.394 213.285-60-0.11z" fill="#1296db" p-id="1591"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.9 KiB |