Compare commits

...

87 Commits

Author SHA1 Message Date
b4ed4f3264 ci(other): ci脚本换源
All checks were successful
CI Build & Deploy / build-and-deploy-dev (push) Successful in 1m55s
CI Build & Deploy / build-and-deploy-prod (push) Has been skipped
2025-11-05 11:42:48 +08:00
e0ee232f5f ci: update ci
All checks were successful
CI Build & Deploy / build-and-deploy-dev (push) Successful in 2m27s
CI Build & Deploy / build-and-deploy-prod (push) Has been skipped
2025-10-31 18:01:01 +08:00
0bb91567d9 Merge branch 'dev' into test
All checks were successful
CI Build & Deploy / build-and-deploy-dev (push) Successful in 32m55s
CI Build & Deploy / build-and-deploy-prod (push) Has been skipped
# Conflicts:
#	.gitea/workflows/ci.yaml
2025-10-31 17:08:50 +08:00
f8df513e10 ci: ci test 2025-10-31 17:06:08 +08:00
b20ae843d6 ci: test ci
Some checks failed
CI Build & Deploy / build-and-deploy-dev (push) Has been cancelled
CI Build & Deploy / build-and-deploy-prod (push) Has been cancelled
2025-10-31 16:54:11 +08:00
afbc4bac90 ci: test ci 2025-10-31 16:51:29 +08:00
90b920b9b4 ci: test ci 2025-10-31 16:44:32 +08:00
77bfbf1982 ci: test ci
All checks were successful
continuous-integration/drone/push Build is passing
2025-10-31 16:41:38 +08:00
7c198fac43 build(deps): 依赖升级
Some checks failed
continuous-integration/drone/push Build is failing
2025-10-31 15:37:57 +08:00
c3547feebd build(deps): 依赖升级
All checks were successful
continuous-integration/drone/push Build is passing
2025-10-31 14:47:49 +08:00
f231939ae9 refactor: 活动订单id显示变更嘉联
Some checks failed
continuous-integration/drone/push Build is failing
2025-10-31 14:39:18 +08:00
038f67ec2a refactor: 活动订单id显示变更嘉联
All checks were successful
continuous-integration/drone/push Build is passing
2025-10-31 14:36:56 +08:00
914c25e962 feat(custom): 多API版本
All checks were successful
continuous-integration/drone/push Build is passing
2025-09-23 18:27:19 +08:00
d754bd45c9 feat(custom): 多API版本
All checks were successful
continuous-integration/drone/push Build is passing
2025-09-23 18:24:41 +08:00
4261f8c5f1 feat(custom): 订单列表支付方式筛选增加新条件
All checks were successful
continuous-integration/drone/push Build is passing
2025-05-29 15:33:56 +08:00
e90e74ff40 mod(custom): \! 2025-05-29 15:33:56 +08:00
0ff7154101 feat(custom): 订单列表支付方式筛选增加新条件 2025-05-29 15:33:18 +08:00
e0632136d7 mod(custom): \!
All checks were successful
continuous-integration/drone/push Build is passing
2024-10-05 04:22:55 +08:00
d520e95b3b Merge branch 'dev' into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-09-19 15:16:09 +08:00
16ab65df50 feat(custom): \!
All checks were successful
continuous-integration/drone/push Build is passing
2024-09-19 15:15:36 +08:00
7b5bb9b565 Merge branch 'dev' into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-09-13 16:40:37 +08:00
edcbd2b964 feat(custom): \!
All checks were successful
continuous-integration/drone/push Build is passing
2024-09-13 16:39:52 +08:00
e7b1992932 Merge branch 'test' of https://gitea.jdt168.com/XinLingKeJi/jdt-admin into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-09-12 01:42:56 +08:00
aee9a2d9c0 Merge branch 'dev' into test 2024-09-12 01:42:52 +08:00
82211679f9 fix(custom): \!
All checks were successful
continuous-integration/drone/push Build is passing
2024-09-12 01:40:25 +08:00
f031d84745 Merge branch 'dev' into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-09-09 16:04:14 +08:00
7d379fa03c fix(custom): \!
All checks were successful
continuous-integration/drone/push Build is passing
2024-09-09 16:03:59 +08:00
94cb709702 Merge branch 'dev' into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-09-06 21:05:30 +08:00
ef0ca7ff27 feat(custom): \!
All checks were successful
continuous-integration/drone/push Build is passing
2024-09-06 21:05:05 +08:00
b2ff663fa8 Merge branch 'dev' into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-09-05 20:30:25 +08:00
50d10c1d53 feat(custom): \!
All checks were successful
continuous-integration/drone/push Build is passing
2024-09-05 20:29:20 +08:00
5c129c35af Merge branch 'dev' into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-09-05 16:42:12 +08:00
9fa1a81b8a feat(custom): \!
All checks were successful
continuous-integration/drone/push Build is passing
2024-09-05 16:38:10 +08:00
2d067aae44 Merge branch 'dev' into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-09-03 22:24:16 +08:00
a4c2674fbe feat(custom): \!
All checks were successful
continuous-integration/drone/push Build is passing
2024-09-03 22:23:48 +08:00
1c95c1097c wip:
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-10 00:11:14 +08:00
0f1fa9c932 Merge branch 'dev' into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-05 18:03:33 +08:00
99c0541e4f feat(custom): 新增导出表格
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-05 18:03:04 +08:00
a8a3bc4ee9 Merge branch 'dev' into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-05 05:33:39 +08:00
825a4ae3ea feat(custom):
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-05 05:33:20 +08:00
7fc8762bd9 Merge branch 'dev' into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-04 00:20:40 +08:00
2e3a445eb5 fix(custom):
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-04 00:20:26 +08:00
d21b06875a Merge branch 'dev' into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-01 22:56:51 +08:00
cadb47cd22 mod(custom):
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-01 22:56:24 +08:00
65ade0ef3a Merge branch 'dev' into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-29 18:48:12 +08:00
1bcd417c57 mod(custom):
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-29 18:47:57 +08:00
e051d75d21 Merge branch 'dev' into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-29 18:35:23 +08:00
698ecb5429 mod(custom):
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-29 18:35:10 +08:00
8daa1d4925 Merge branch 'dev' into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-29 18:06:12 +08:00
be1bf99fc3 feat(custom): 新增活动商品筛选
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-29 18:05:52 +08:00
3e920e2873 Merge branch 'dev' into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-28 10:45:22 +08:00
3d8f72de35 feat(custom):
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-28 10:45:08 +08:00
f75d9eef81 Merge branch 'dev' into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-19 17:03:31 +08:00
f78140d8dd mod(custom):
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-19 17:03:17 +08:00
0ea8754321 Merge branch 'dev' into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-19 11:01:23 +08:00
2f7b80f418 feat(custom):
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-19 11:00:48 +08:00
b47bc1b35b Merge branch 'dev' into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-18 19:05:04 +08:00
c157d6024e feat(custom): 新增支付管理
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-18 19:04:51 +08:00
cab4da5b28 Merge branch 'dev' into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-17 20:10:00 +08:00
587b11e1ec fix(custom):
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-17 20:09:38 +08:00
7c7d395518 Merge branch 'dev' into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-17 18:29:45 +08:00
ca4ec000f6 feat(custom): update
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-17 18:29:26 +08:00
324426ef4a Merge branch 'dev' into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-06-21 19:51:24 +08:00
c233ad8f5a feat(custom): 新增营销-\>二维码管理
All checks were successful
continuous-integration/drone/push Build is passing
2024-06-21 19:51:02 +08:00
d3a8a0425c Merge branch 'dev' into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-05-29 16:09:52 +08:00
0865529e10 feat(custom):
All checks were successful
continuous-integration/drone/push Build is passing
2024-05-29 16:09:35 +08:00
5c9b248624 Merge branch 'dev' into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-05-20 16:06:56 +08:00
3e47ee08af feat(custom):
All checks were successful
continuous-integration/drone/push Build is passing
2024-05-20 16:06:37 +08:00
a7cb1758e2 Merge branch 'dev' into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-04-09 19:20:19 +08:00
1be0710a82 feat(custom):
All checks were successful
continuous-integration/drone/push Build is passing
2024-04-09 19:20:01 +08:00
25e4185183 Merge branch 'dev' into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-28 16:39:38 +08:00
f191298dea feat(custom):
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-28 16:39:24 +08:00
233824e959 Merge branch 'dev' into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-21 18:36:08 +08:00
e5334f8977 refactor(custom):
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-21 18:35:46 +08:00
1f7669f033 Merge branch 'dev' into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-20 16:38:37 +08:00
04f0f65e94 feat(custom):
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-20 16:38:24 +08:00
18b2e7361f Merge branch 'dev' into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-15 16:03:15 +08:00
84afb82cca feat(custom): 新增协议管理
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-15 16:02:56 +08:00
2d109b22a0 Merge branch 'dev' into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-08 20:53:46 +08:00
d1b8a8fc61 ci(custom):
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-08 20:53:26 +08:00
52ef8900ff ci(custom):
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-08 19:15:03 +08:00
6256f59fe9 Merge branch 'dev' into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-08 18:51:10 +08:00
eb3c104259 ci(custom): update .drone.yml
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-08 18:50:55 +08:00
49bbe2a086 Merge branch 'dev' into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-08 18:48:50 +08:00
93bda1fa29 ci(custom): update cicd
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-08 18:48:37 +08:00
929a9e5826 Merge branch 'dev' into test
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-08 18:30:34 +08:00
1c729f1e35 ci(custom): update .drone.yml
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-08 18:30:20 +08:00
50 changed files with 9949 additions and 4938 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@@ -1,160 +0,0 @@
kind: pipeline
type: docker
name: default
platform:
os: linux
arch: amd64
steps:
- name: 测试服-依赖安装&&编译打包
image: node:20-alpine
when:
branch:
- test
commands:
- npm config set registry https://registry.npmmirror.com/
- npm install -g pnpm
- pnpm install
- pnpm build:test
- rm -rf dist.tar
- rm -rf node_modules
- tar -zcvf dist.tar ./dist ./default.conf ./Dockerfile
- name: 正式服-依赖安装&&编译打包
image: node:20-alpine
when:
branch:
- master
commands:
- npm config set registry https://registry.npmmirror.com/
- npm install -g pnpm
- pnpm install
- pnpm build:prod
- rm -rf dist.tar
- rm -rf node_modules
- tar -zcvf dist.tar ./dist ./default.conf ./Dockerfile
- name: 测试服-产物上传
image: appleboy/drone-scp
when:
branch:
- test
settings:
host:
from_secret: HOST_DEV
username:
from_secret: USER_DEV
password:
from_secret: PWD_DEV
port: 22
strip_components: 1
target: /www/builder
source:
- ./dist.tar
- name: 测试服-部署
image: appleboy/drone-ssh
when:
branch:
- test
settings:
host:
from_secret: HOST_DEV
username:
from_secret: USER_DEV
password:
from_secret: PWD_DEV
port: 22
script:
- cd /www/builder
- mkdir jdt-admin-dev
- tar -xzvf dist.tar -C /www/builder/jdt-admin-dev
- rm -rf dist.tar
- cd jdt-admin-dev
- docker build -t jdt-admin-dev .
- target_container="jdt-admin-dev"
- if docker ps -a --format '{{.Names}}' | grep -q "^$target_container$"; then
- echo "容器存在,执行操作..."
- docker stop jdt-admin-dev
- docker rm jdt-admin-dev
- else
- echo "容器不存在"
- fi
- docker run -d -p 8082:80 --restart=always --name jdt-admin-dev jdt-admin-dev
- cd ..
- rm -rf jdt-admin-dev
- name: 正式服-产物上传
image: appleboy/drone-scp
when:
branch:
- main
settings:
host:
from_secret: HOST_PROD
username:
from_secret: USER_PROD
password:
from_secret: PWD_PROD
port: 22
strip_components: 1
target: /www/builder
source:
- ./dist.tar
- name: 正式服-部署
image: appleboy/drone-ssh
when:
branch:
- main
settings:
host:
from_secret: HOST_PROD
username:
from_secret: USER_PROD
password:
from_secret: PWD_PROD
port: 22
script:
- cd /www/builder
- mkdir jdt-admin-prod
- tar -xzvf dist.tar -C /www/builder/jdt-admin-prod
- rm -rf dist.tar
- cd jdt-admin-prod
- cd docker build -t jdt-admin-prod .
- target_container="jdt-admin-prod"
- if docker ps -a --format '{{.Names}}' | grep -q "^$target_container$"; then
- echo "容器存在,执行操作..."
- docker stop jdt-admin-prod
- docker rm jdt-admin-prod
- else
- echo "容器不存在"
- fi
- docker run -d -p 8085:80 --restart=always --name jdt-admin-prod jdt-admin-prod
- cd ..
- rm -rf jdt-admin-prod
- name: 微信通知
image: plugins/webhook
# 等待构建完成再发送通知
depends_on:
- 测试服-部署
- 正式服-部署
settings:
urls: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=a2065e21-4f92-4f5b-a432-2c0cd1d965b5
content_type: application/json
template: |
{
"msgtype": "markdown",
"markdown": {
"content": "{{#success build.status}}✅{{else}}❌{{/success}}**{{ repo.owner }}/{{ repo.name }}** (Build #{{build.number}})\n
>**构建结果**: {{ build.status }}
>**构建详情**: [点击查看]({{ build.link }})
>**代码分支**: {{ build.branch }}
>**提交标识**: {{ build.commit }}
>**提交发起**: {{ build.author }}
>**提交信息**: {{ build.message }}
"
}
}

View File

@@ -8,7 +8,12 @@ VITE_USE_MOCK=false
VITE_USE_PROXY=true VITE_USE_PROXY=true
# base api # base api
VITE_BASE_API='https://test.wanzhuanyongcheng.cn/admin' VITE_BASE_API='/api'
VITE_BASE_API_1='/api1'
# VITE_BASE_API='https://test.wanzhuanyongcheng.cn/admin'
# VITE_BASE_API_1='https://api.gxwzwh.com/admin'
VITE_WS1_URL='game.wanzhuanyongcheng.cn/dice/home' VITE_WS1_URL='game.wanzhuanyongcheng.cn/dice/home'

View File

@@ -6,6 +6,7 @@ VITE_USE_MOCK=false
# base api # base api
VITE_BASE_API='//www.wanzhuanyongcheng.cn/admin' VITE_BASE_API='//www.wanzhuanyongcheng.cn/admin'
VITE_BASE_API_1='//api.gxwzwh.com/admin'
# 是否启用压缩 # 是否启用压缩
VITE_USE_COMPRESS=true VITE_USE_COMPRESS=true

162
.gitea/workflows/ci.yaml Normal file
View File

@@ -0,0 +1,162 @@
name: CI Build & Deploy
on:
push:
branches:
- test
- main
jobs:
build-and-deploy-dev:
if: gitea.ref_name == 'test'
runs-on: gitea_act_runner
container:
image: node:24-alpine
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 8
- name: Install deps
run: |
npm config set registry https://registry.npmmirror.com/
pnpm install
- name: Build (test)
run: pnpm build:test
- name: Pack artifacts
run: |
rm -rf dist.tar
tar -zcvf dist.tar ./dist ./default.conf ./Dockerfile
- name: Upload artifacts to server
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.HOST_DEV }}
username: ${{ secrets.USER_DEV }}
password: ${{ secrets.PWD_DEV }}
port: 22
source: 'dist.tar'
target: '/www/builder'
strip_components: 0
- name: Deploy over SSH
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.HOST_DEV }}
username: ${{ secrets.USER_DEV }}
password: ${{ secrets.PWD_DEV }}
port: 22
script: |
set -e
cd /www/builder
rm -rf jdt-admin-dev
mkdir -p jdt-admin-dev
tar -xzvf dist.tar -C /www/builder/jdt-admin-dev
rm -rf dist.tar
cd jdt-admin-dev
docker build -t jdt-admin-dev .
docker stop jdt-admin-dev || true
docker rm jdt-admin-dev || true
docker run -d -p 8085:80 --restart=always --name jdt-admin-dev jdt-admin-dev
cd ..
rm -rf jdt-admin-dev
- name: Notify WeCom (Dev)
if: always()
env:
WEBHOOK_KEY: ${{ secrets.QYWX_WEBHOOK_KEY }}
STATUS: ${{ job.status }}
REPO: ${{ gitea.repository }}
RUN_URL: ${{ gitea.server_url }}/${{ gitea.repository }}/actions/runs/${{ gitea.run_id }}
BRANCH: ${{ gitea.ref_name }}
COMMIT: ${{ gitea.sha }}
ACTOR: ${{ gitea.actor }}
run: |
sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories || true
apk add --no-cache curl jq
EMOJI=$( [ "$STATUS" = "success" ] && echo "✅" || echo "❌" )
MSG="$(printf "%s**%s** (Run #%s)\n>**构建结果**: %s\n>**构建详情**: [点击查看](%s)\n>**代码分支**: %s\n>**提交标识**: %s\n>**提交发起**: %s\n" "$EMOJI" "$REPO" "${{ gitea.run_number }}" "$STATUS" "$RUN_URL" "$BRANCH" "$COMMIT" "$ACTOR")"
curl -sS -H 'Content-Type: application/json' -d "{\"msgtype\":\"markdown\",\"markdown\":{\"content\":\"$MSG\"}}" "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=${WEBHOOK_KEY}"
build-and-deploy-prod:
if: gitea.ref_name == 'main'
runs-on: gitea_act_runner
container:
image: node:24-alpine
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 8
- name: Install deps
run: |
npm config set registry https://registry.npmmirror.com/
pnpm install
- name: Build (prod)
run: pnpm build:prod
- name: Pack artifacts
run: |
rm -rf dist.tar
tar -zcvf dist.tar ./dist ./default.conf ./Dockerfile
- name: Upload artifacts to server
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.HOST_PROD }}
username: ${{ secrets.USER_PROD }}
password: ${{ secrets.PWD_PROD }}
port: 22
source: 'dist.tar'
target: '/www/builder'
strip_components: 0
- name: Deploy over SSH
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.HOST_PROD }}
username: ${{ secrets.USER_PROD }}
password: ${{ secrets.PWD_PROD }}
port: 22
script: |
set -e
cd /www/builder
rm -rf jdt-admin-prod
mkdir -p jdt-admin-prod
tar -xzvf dist.tar -C /www/builder/jdt-admin-prod
rm -rf dist.tar
cd jdt-admin-prod
docker build -t jdt-admin-prod .
docker stop jdt-admin-prod || true
docker rm jdt-admin-prod || true
docker run -d -p 8085:80 --restart=always --name jdt-admin-prod jdt-admin-prod
cd ..
rm -rf jdt-admin-prod
- name: Notify WeCom (Prod)
if: always()
env:
WEBHOOK_KEY: ${{ secrets.QYWX_WEBHOOK_KEY }}
STATUS: ${{ job.status }}
REPO: ${{ gitea.repository }}
RUN_URL: ${{ gitea.server_url }}/${{ gitea.repository }}/actions/runs/${{ gitea.run_id }}
BRANCH: ${{ gitea.ref_name }}
COMMIT: ${{ gitea.sha }}
ACTOR: ${{ gitea.actor }}
run: |
sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories || true
apk add --no-cache curl jq
EMOJI=$( [ "$STATUS" = "success" ] && echo "✅" || echo "❌" )
MSG="$(printf "%s**%s** (Run #%s)\n>**构建结果**: %s\n>**构建详情**: [点击查看](%s)\n>**代码分支**: %s\n>**提交标识**: %s\n>**提交发起**: %s\n" "$EMOJI" "$REPO" "${{ gitea.run_number }}" "$STATUS" "$RUN_URL" "$BRANCH" "$COMMIT" "$ACTOR")"
curl -sS -H 'Content-Type: application/json' -d "{\"msgtype\":\"markdown\",\"markdown\":{\"content\":\"$MSG\"}}" "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=${WEBHOOK_KEY}"

View File

@@ -1,26 +1,19 @@
<component name="ProjectCodeStyleConfiguration"> <component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173"> <code_scheme name="Project" version="173">
<option name="LINE_SEPARATOR" value="&#10;" />
<HTMLCodeStyleSettings> <HTMLCodeStyleSettings>
<option name="HTML_SPACE_INSIDE_EMPTY_TAG" value="true" /> <option name="HTML_SPACE_INSIDE_EMPTY_TAG" value="true" />
<option name="HTML_QUOTE_STYLE" value="Single" />
<option name="HTML_ENFORCE_QUOTES" value="true" />
</HTMLCodeStyleSettings> </HTMLCodeStyleSettings>
<JSCodeStyleSettings version="0"> <JSCodeStyleSettings version="0">
<option name="USE_SEMICOLON_AFTER_STATEMENT" value="false" />
<option name="FORCE_SEMICOLON_STYLE" value="true" /> <option name="FORCE_SEMICOLON_STYLE" value="true" />
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" /> <option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
<option name="USE_DOUBLE_QUOTES" value="false" />
<option name="FORCE_QUOTE_STYlE" value="true" /> <option name="FORCE_QUOTE_STYlE" value="true" />
<option name="ENFORCE_TRAILING_COMMA" value="Remove" /> <option name="ENFORCE_TRAILING_COMMA" value="Remove" />
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" /> <option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
<option name="SPACES_WITHIN_IMPORTS" value="true" /> <option name="SPACES_WITHIN_IMPORTS" value="true" />
</JSCodeStyleSettings> </JSCodeStyleSettings>
<TypeScriptCodeStyleSettings version="0"> <TypeScriptCodeStyleSettings version="0">
<option name="USE_SEMICOLON_AFTER_STATEMENT" value="false" />
<option name="FORCE_SEMICOLON_STYLE" value="true" /> <option name="FORCE_SEMICOLON_STYLE" value="true" />
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" /> <option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
<option name="USE_DOUBLE_QUOTES" value="false" />
<option name="FORCE_QUOTE_STYlE" value="true" /> <option name="FORCE_QUOTE_STYlE" value="true" />
<option name="ENFORCE_TRAILING_COMMA" value="Remove" /> <option name="ENFORCE_TRAILING_COMMA" value="Remove" />
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" /> <option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
@@ -31,16 +24,15 @@
<option name="INTERPOLATION_NEW_LINE_BEFORE_END_DELIMITER" value="false" /> <option name="INTERPOLATION_NEW_LINE_BEFORE_END_DELIMITER" value="false" />
</VueCodeStyleSettings> </VueCodeStyleSettings>
<codeStyleSettings language="HTML"> <codeStyleSettings language="HTML">
<option name="SOFT_MARGINS" value="100" /> <option name="SOFT_MARGINS" value="80" />
<indentOptions> <indentOptions>
<option name="INDENT_SIZE" value="2" /> <option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" /> <option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" /> <option name="TAB_SIZE" value="2" />
<option name="SMART_TABS" value="true" />
</indentOptions> </indentOptions>
</codeStyleSettings> </codeStyleSettings>
<codeStyleSettings language="JavaScript"> <codeStyleSettings language="JavaScript">
<option name="SOFT_MARGINS" value="100" /> <option name="SOFT_MARGINS" value="80" />
<indentOptions> <indentOptions>
<option name="INDENT_SIZE" value="2" /> <option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" /> <option name="CONTINUATION_INDENT_SIZE" value="2" />
@@ -48,7 +40,7 @@
</indentOptions> </indentOptions>
</codeStyleSettings> </codeStyleSettings>
<codeStyleSettings language="TypeScript"> <codeStyleSettings language="TypeScript">
<option name="SOFT_MARGINS" value="100" /> <option name="SOFT_MARGINS" value="80" />
<indentOptions> <indentOptions>
<option name="INDENT_SIZE" value="2" /> <option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" /> <option name="CONTINUATION_INDENT_SIZE" value="2" />
@@ -56,7 +48,7 @@
</indentOptions> </indentOptions>
</codeStyleSettings> </codeStyleSettings>
<codeStyleSettings language="Vue"> <codeStyleSettings language="Vue">
<option name="SOFT_MARGINS" value="100" /> <option name="SOFT_MARGINS" value="80" />
<indentOptions> <indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="2" /> <option name="CONTINUATION_INDENT_SIZE" value="2" />
</indentOptions> </indentOptions>

6
.idea/git_toolbox_blame.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GitToolBoxBlameSettings">
<option name="version" value="2" />
</component>
</project>

View File

@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GitToolBoxProjectSettings">
<option name="commitMessageIssueKeyValidationOverride">
<BoolValueOverride>
<option name="enabled" value="true" />
</BoolValueOverride>
</option>
<option name="commitMessageValidationEnabledOverride">
<BoolValueOverride>
<option name="enabled" value="true" />
</BoolValueOverride>
</option>
</component>
</project>

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<includedPredefinedLibrary name="Node.js Core" />
</component>
</project>

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EslintConfiguration">
<option name="fix-on-save" value="true" />
</component>
</project>

2
.idea/modules.xml generated
View File

@@ -2,7 +2,7 @@
<project version="4"> <project version="4">
<component name="ProjectModuleManager"> <component name="ProjectModuleManager">
<modules> <modules>
<module fileurl="file://$PROJECT_DIR$/.idea/admin.iml" filepath="$PROJECT_DIR$/.idea/admin.iml" /> <module fileurl="file://$PROJECT_DIR$/.idea/jdt-admin.iml" filepath="$PROJECT_DIR$/.idea/jdt-admin.iml" />
</modules> </modules>
</component> </component>
</project> </project>

6
.idea/prettier.xml generated
View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PrettierConfiguration">
<option name="myConfigurationMode" value="AUTOMATIC" />
</component>
</project>

BIN
build/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -2,32 +2,23 @@ export const OUTPUT_DIR = 'dist'
export const PROXY_CONFIG = { export const PROXY_CONFIG = {
/** /**
* @desc 替换匹配值 * @desc 主接口代理
* @请求路径 http://localhost:3100/api/user * @请求路径 http://localhost:3100/api/login
* @转发路径 http://localhost:8080/user * @转发路径 http://localhost:3000/api/login
*/ */
'/admin': { '/api': {
target: 'https://test.wanzhuanyongcheng.cn', target: 'https://test.wanzhuanyongcheng.cn',
changeOrigin: true, changeOrigin: true,
// rewrite: (path) => path.replace(new RegExp('^/api'), ''), rewrite: (path) => path.replace(/^\/api/, '/admin'),
}, },
/** /**
* @desc 不替换匹配值 * @desc 备用接口代理
* @请求路径 http://localhost:3100/api/v2/user * @请求路径 http://localhost:3100/api1/login
* @转发路径 http://localhost:8080/api/v2/user * @转发路径 http://localhost:3001/api/login
*/ */
// '/api/v2': { '/api1': {
// target: 'http://localhost:8080', target: 'https://api.gxwzwh.com',
// changeOrigin: true, changeOrigin: true,
// }, rewrite: (path) => path.replace(/^\/api1/, '/admin'),
/** },
* @desc 替换部分匹配值
* @请求路径 http://localhost:3100/api/v3/user
* @转发路径 http://localhost:8080/user
*/
// '/api/v3': {
// target: 'http://localhost:8080',
// changeOrigin: true,
// rewrite: (path) => path.replace(new RegExp('^/api'), ''),
// },
} }

View File

@@ -15,7 +15,6 @@
}, },
"lint-staged": { "lint-staged": {
"*.{js,vue}": [ "*.{js,vue}": [
"npx prettier --write --end-of-line lf .",
"eslint --ext .js,.vue ." "eslint --ext .js,.vue ."
] ]
}, },
@@ -33,50 +32,53 @@
}, },
"dependencies": { "dependencies": {
"@unocss/eslint-config": "^0.55.7", "@unocss/eslint-config": "^0.55.7",
"@vueuse/core": "^10.6.1", "@vueuse/core": "^10.11.1",
"@wangeditor/editor": "^5.1.23", "@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.12", "@wangeditor/editor-for-vue": "^5.1.12",
"axios": "^1.6.2", "axios": "^1.13.1",
"dayjs": "^1.11.10", "dayjs": "^1.11.19",
"echarts": "^5.4.3", "echarts": "^5.6.0",
"file-saver": "^2.0.5",
"jszip": "^3.10.1",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"md-editor-v3": "^4.9.0", "md-editor-v3": "^4.21.3",
"mockjs": "^1.1.0", "mockjs": "^1.1.0",
"pinia": "^2.1.7", "pinia": "^2.3.1",
"vite": "^4.5.0", "vite": "^4.5.14",
"vue": "3.3.4", "vue": "3.3.4",
"vue-echarts": "^6.6.1", "vue-echarts": "^6.7.3",
"vue-router": "^4.2.5", "vue-router": "^4.6.3",
"xlsx": "^0.18.5" "xlsx": "^0.18.5"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^17.8.1", "@commitlint/cli": "^17.8.1",
"@commitlint/config-conventional": "^17.8.1", "@commitlint/config-conventional": "^17.8.1",
"@iconify/json": "^2.2.150", "@iconify/json": "^2.2.402",
"@iconify/vue": "^4.1.1", "@iconify/vue": "^4.3.0",
"@unocss/preset-rem-to-px": "^0.55.7", "@unocss/preset-rem-to-px": "^0.55.7",
"@vitejs/plugin-vue": "^4.5.1", "@vitejs/plugin-vue": "^4.6.2",
"@vue/compiler-sfc": "^3.3.9", "@vue/compiler-sfc": "^3.5.22",
"@zclzone/eslint-config": "^0.0.4", "@zclzone/eslint-config": "^0.0.4",
"chalk": "^5.3.0", "chalk": "^5.6.2",
"commitizen": "^4.3.0", "commitizen": "^4.3.1",
"cz-conventional-changelog": "^3.3.0", "cz-conventional-changelog": "^3.3.0",
"cz-customizable": "^7.0.0", "cz-customizable": "^7.5.1",
"dotenv": "^16.3.1", "dotenv": "^16.6.1",
"esno": "^0.17.0", "esno": "^0.17.0",
"fs-extra": "^11.2.0", "fs-extra": "^11.3.2",
"husky": "^8.0.3", "husky": "^8.0.3",
"lint-staged": "^13.3.0", "lint-staged": "^13.3.0",
"naive-ui": "^2.35.0", "naive-ui": "^2.43.1",
"rollup-plugin-visualizer": "^5.9.3", "rollup-plugin-visualizer": "^5.14.0",
"sass": "^1.69.5", "sass": "^1.93.2",
"unocss": "0.55.0", "unocss": "0.55.0",
"unplugin-auto-import": "^0.16.7", "unplugin-auto-import": "^0.16.7",
"unplugin-icons": "^0.16.6", "unplugin-icons": "^0.16.6",
"unplugin-vue-components": "^0.25.2", "unplugin-vue-components": "^0.25.2",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vite-plugin-html": "^3.2.0", "vite-plugin-html": "^3.2.2",
"vite-plugin-mock": "^2.9.8", "vite-plugin-mock": "^2.9.8",
"vite-plugin-svg-icons": "^2.0.1" "vite-plugin-svg-icons": "^2.0.1"
} },
"packageManager": "pnpm@9.1.4+sha512.9df9cf27c91715646c7d675d1c9c8e41f6fce88246f1318c1aa6a1ed1aeb3c4f032fcdf4ba63cc69c4fe6d634279176b5358727d8f2cc1e65b65f43ce2f8bfb0"
} }

11522
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

BIN
src/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -68,7 +68,7 @@ const tabs = [
const count = ref(tabs.map((item) => item.messages).flat().length) const count = ref(tabs.map((item) => item.messages).flat().length)
watch(activeTab, (v) => { watch(activeTab, (v) => {
if (count === 0) return if (count.value === 0) return
const tabIndex = tabs.findIndex((item) => item.name === v) const tabIndex = tabs.findIndex((item) => item.name === v)
count.value -= tabs[tabIndex].messages.length count.value -= tabs[tabIndex].messages.length
if (count.value < 0) count.value = 0 if (count.value < 0) count.value = 0

View File

@@ -9,10 +9,14 @@ import { setupRouter } from '@/router'
import { setupStore } from '@/store' import { setupStore } from '@/store'
import App from './App.vue' import App from './App.vue'
import { setupNaiveDiscreteApi } from './utils' import { setupNaiveDiscreteApi } from './utils'
import { initApiEndpoint } from '@/utils/api-config'
async function setupApp() { async function setupApp() {
const app = createApp(App) const app = createApp(App)
// 初始化接口配置
initApiEndpoint()
setupStore(app) setupStore(app)
setupNaiveDiscreteApi() setupNaiveDiscreteApi()

View File

@@ -1,4 +1,5 @@
import { getToken, isNullOrWhitespace } from '@/utils' import { getToken, isNullOrWhitespace } from '@/utils'
import { addDynamicRoutes } from '@/router'
const WHITE_LIST = ['/login', '/404'] const WHITE_LIST = ['/login', '/404']
export function createPermissionGuard(router) { export function createPermissionGuard(router) {
@@ -14,7 +15,20 @@ export function createPermissionGuard(router) {
/** 有token的情况 */ /** 有token的情况 */
if (to.path === '/login') return { path: '/' } if (to.path === '/login') return { path: '/' }
// refreshAccessToken() // 确保动态路由已加载
if (token && !router.hasRoute('Dashboard')) {
try {
await addDynamicRoutes()
// 如果当前路径不存在,重定向到工作台
if (to.path !== '/' && !router.hasRoute(to.name)) {
return { path: '/workbench' }
}
} catch (error) {
console.error('动态路由加载失败:', error)
return { path: '/login' }
}
}
return true return true
}) })
} }

View File

@@ -30,11 +30,44 @@ export async function addDynamicRoutes() {
try { try {
const permissionStore = usePermissionStore() const permissionStore = usePermissionStore()
const accessRoutes = permissionStore.generateRoutes() const accessRoutes = permissionStore.generateRoutes()
// 确保路由按正确顺序添加
accessRoutes.forEach((route) => { accessRoutes.forEach((route) => {
!router.hasRoute(route.name) && router.addRoute(route) if (!router.hasRoute(route.name)) {
router.addRoute(route)
}
}) })
router.hasRoute(EMPTY_ROUTE.name) && router.removeRoute(EMPTY_ROUTE.name)
// 移除空路由添加404路由
if (router.hasRoute(EMPTY_ROUTE.name)) {
router.removeRoute(EMPTY_ROUTE.name)
}
router.addRoute(NOT_FOUND_ROUTE) router.addRoute(NOT_FOUND_ROUTE)
// 确保根路径重定向到工作台
// if (!router.hasRoute('workbench')) {
// const workbenchRoute = {
// name: 'workbench',
// path: '/',
// component: () => import('@/views/workbench/index.vue'),
// redirect: '/workbench',
// // children: [
// // {
// // name: 'Workbench',
// // path: 'workbench',
// // component: () => import('@/views/workbench/index.vue'),
// // meta: {
// // title: '工作台',
// // icon: 'mdi:index',
// // order: 0,
// // },
// // },
// // ],
// }
// router.addRoute(workbenchRoute)
// }
// console.log(router)
} catch (error) { } catch (error) {
console.error(error) console.error(error)
throw error throw error

View File

@@ -58,17 +58,17 @@ function filterAsyncRoutes(routes = [], firstRoute = true) {
const curRoute = { const curRoute = {
path: route.route, path: route.route,
name: route.name, name: route.route,
isHidden, isHidden,
meta, meta,
children: [], children: [],
} }
if (route.route === '/' && firstRoute) { // if (route.route === '/' && firstRoute) {
curRoute['redirect'] = route.subMenu[0].route // curRoute['redirect'] = route.subMenu[0].route
} else if (route.subMenu && route.type === 1) { // } else if (route.subMenu && route.type === 1) {
curRoute['redirect'] = `${route.subMenu[0].route}` // curRoute['redirect'] = `${route.subMenu[0].route}`
} // }
if (route.subMenu && route.subMenu.length) { if (route.subMenu && route.subMenu.length) {
curRoute.children = filterAsyncRoutes(route.subMenu, false) curRoute.children = filterAsyncRoutes(route.subMenu, false)
@@ -84,7 +84,6 @@ function filterAsyncRoutes(routes = [], firstRoute = true) {
curRoute.component = loadRouteView(route.components) curRoute.component = loadRouteView(route.components)
break break
} }
ret.push(curRoute) ret.push(curRoute)
}) })
return ret return ret

View File

@@ -18,10 +18,7 @@ export const useUserStore = defineStore('user', {
return this.userInfo?.name return this.userInfo?.name
}, },
avatar() { avatar() {
return ( return this.userInfo?.avatar || 'https://v2.xxapi.cn/api/head?return=302'
this.userInfo?.avatar ||
'https://pic3.58cdn.com.cn/nowater/webim/big/n_v21bc7874294754e63a22b80febac9cf51.jpg'
)
}, },
role() { role() {
return this.userInfo?.role || [] return this.userInfo?.role || []

61
src/utils/api-config.js Normal file
View File

@@ -0,0 +1,61 @@
// 判断是否为开发环境
const isDev = import.meta.env.DEV
// API接口线路配置管理
export const API_ENDPOINTS = {
primary: {
name: '主要线路',
baseURL: import.meta.env.VITE_BASE_API,
timeout: 10000,
},
backup1: {
name: '备用线路',
baseURL: import.meta.env.VITE_BASE_API_1,
timeout: 10000,
},
}
// 调试信息
console.log('=== 多接口配置信息 ===')
console.log('当前环境:', isDev ? '开发环境' : '生产环境')
console.log('API配置:', API_ENDPOINTS)
console.log('环境变量 VITE_BASE_API:', import.meta.env.VITE_BASE_API)
console.log('环境变量 VITE_BASE_API_1:', import.meta.env.VITE_BASE_API_1)
console.log('是否使用代理:', import.meta.env.VITE_USE_PROXY)
// 当前使用的接口线路
let currentEndpoint = 'primary'
// 获取当前接口配置
export function getCurrentEndpoint() {
return {
key: currentEndpoint,
...API_ENDPOINTS[currentEndpoint],
}
}
// 设置当前接口
export function setCurrentEndpoint(endpoint) {
if (API_ENDPOINTS[endpoint]) {
currentEndpoint = endpoint
localStorage.setItem('api_endpoint', endpoint)
return true
}
return false
}
// 获取所有可用接口
export function getAvailableEndpoints() {
return Object.entries(API_ENDPOINTS).map(([key, config]) => ({
key,
...config,
}))
}
// 初始化时从本地存储恢复接口选择
export function initApiEndpoint() {
const savedEndpoint = localStorage.getItem('api_endpoint')
if (savedEndpoint && API_ENDPOINTS[savedEndpoint]) {
currentEndpoint = savedEndpoint
}
}

View File

@@ -1,5 +1,6 @@
import axios from 'axios' import axios from 'axios'
import { resReject, resResolve, reqReject, reqResolve } from './interceptors' import { resReject, resResolve, reqReject, reqResolve } from './interceptors'
import { getCurrentEndpoint, getAvailableEndpoints } from '../api-config'
export function createAxios(options = {}) { export function createAxios(options = {}) {
const defaultOptions = { const defaultOptions = {
@@ -14,6 +15,93 @@ export function createAxios(options = {}) {
return service return service
} }
export const request = createAxios({ // 创建支持多接口的请求实例
baseURL: import.meta.env.VITE_BASE_API, export function createMultiEndpointRequest() {
}) let instances = null
// 延迟创建axios实例
function ensureInstances() {
if (!instances) {
instances = {}
const endpoints = getAvailableEndpoints()
console.log('创建axios实例接口列表:', endpoints)
endpoints.forEach((endpoint) => {
console.log(`创建实例 ${endpoint.key}:`, endpoint.baseURL)
instances[endpoint.key] = createAxios({
baseURL: endpoint.baseURL,
timeout: endpoint.timeout,
})
})
}
return instances
}
return {
// 使用当前选中的接口发送请求
async request(config) {
const instances = ensureInstances()
const currentEndpoint = getCurrentEndpoint()
console.log('当前接口配置:', currentEndpoint)
console.log('可用实例:', Object.keys(instances))
const instance = instances[currentEndpoint.key]
if (!instance) {
throw new Error(`接口实例不存在: ${currentEndpoint.key}`)
}
console.log(`使用接口: ${currentEndpoint.name} (${currentEndpoint.baseURL})`)
console.log('请求配置:', config)
return await instance(config)
},
// 获取当前接口实例
getCurrentInstance() {
const instances = ensureInstances()
const currentEndpoint = getCurrentEndpoint()
return instances[currentEndpoint.key]
},
// 获取所有接口实例
getAllInstances() {
return ensureInstances()
},
}
}
// export const request = createAxios({
// baseURL: import.meta.env.VITE_BASE_API,
// })
// 支持多接口的请求实例
export const multiRequest = createMultiEndpointRequest()
// 创建自动适配多接口的request实例
const multiEndpointInstance = createMultiEndpointRequest()
// 创建支持axios方法的request实例
export const request = {
// 基础请求方法
request: (config) => multiEndpointInstance.request(config),
// 自动适配axios方法
get: (url, config = {}) => multiEndpointInstance.request({ method: 'get', url, ...config }),
post: (url, data, config = {}) =>
multiEndpointInstance.request({ method: 'post', url, data, ...config }),
put: (url, data, config = {}) =>
multiEndpointInstance.request({ method: 'put', url, data, ...config }),
delete: (url, config = {}) => multiEndpointInstance.request({ method: 'delete', url, ...config }),
patch: (url, data, config = {}) =>
multiEndpointInstance.request({ method: 'patch', url, data, ...config }),
head: (url, config = {}) => multiEndpointInstance.request({ method: 'head', url, ...config }),
options: (url, config = {}) =>
multiEndpointInstance.request({ method: 'options', url, ...config }),
// 获取当前接口实例用于直接访问axios实例
getCurrentInstance: () => multiEndpointInstance.getCurrentInstance(),
getAllInstances: () => multiEndpointInstance.getAllInstances(),
}

View File

@@ -3,7 +3,7 @@
<n-button v-perms="['/admin/store/classify/edit']" type="primary" @click="handleAdd(1)"> <n-button v-perms="['/admin/store/classify/edit']" type="primary" @click="handleAdd(1)">
新增商户分类 新增商户分类
</n-button> </n-button>
<!-- {{ formValue }} -->
<n-data-table <n-data-table
:loading="loading" :loading="loading"
:columns="columns" :columns="columns"
@@ -23,7 +23,15 @@
role="dialog" role="dialog"
aria-modal="true" aria-modal="true"
> >
<!-- {{ formValue }} -->
<n-form ref="formRef" label-placement="left" :model="formValue" :rules="rules"> <n-form ref="formRef" label-placement="left" :model="formValue" :rules="rules">
<n-form-item label="上级分类:" path="sid">
<n-select
v-model:value="formValue.sid"
:options="options"
placeholder="请选择上级分类"
/>
</n-form-item>
<n-form-item label="分类图标:" path="icon"> <n-form-item label="分类图标:" path="icon">
<Upload v-model:list="formValue.icon" /> <Upload v-model:list="formValue.icon" />
</n-form-item> </n-form-item>
@@ -123,6 +131,8 @@ const columns = ref([
}, },
]) ])
const options = ref([])
const data = ref([]) const data = ref([])
const formRef = ref(null) const formRef = ref(null)
@@ -137,10 +147,15 @@ const rules = {
type: 'array', type: 'array',
message: '请上传分类图标', message: '请上传分类图标',
}, },
sid: {
required: true,
type: 'number',
message: '请选择分类',
},
} }
const formValue = ref({ const formValue = ref({
ID: 0, sid: null,
name: '', name: '',
status: 1, status: 1,
icon: [], icon: [],
@@ -165,6 +180,7 @@ const pagination = ref({
onMounted(() => { onMounted(() => {
getList() getList()
getOptions()
}) })
const getList = async () => { const getList = async () => {
@@ -182,6 +198,25 @@ const getList = async () => {
loading.value = false loading.value = false
} }
const getOptions = async () => {
const res = await api.getList({
pageNum: 1,
pageSize: 9999999999,
})
options.value = [
{
label: '顶级分类',
value: 0,
},
]
res.data.data.forEach((item) => {
options.value.push({
label: item.name,
value: item.ID,
})
})
}
const modelTitle = ref('') const modelTitle = ref('')
const handleAdd = (e) => { const handleAdd = (e) => {
@@ -191,7 +226,7 @@ const handleAdd = (e) => {
const clear = () => { const clear = () => {
formValue.value = { formValue.value = {
ID: 0, sid: 0,
name: '', name: '',
status: 1, status: 1,
icon: [], icon: [],
@@ -204,6 +239,7 @@ const handleValidateClick = async (e) => {
formRef.value?.validate(async (errors) => { formRef.value?.validate(async (errors) => {
if (!errors) { if (!errors) {
try { try {
console.log(formValue.value)
await api.addClass({ await api.addClass({
...formValue.value, ...formValue.value,
icon: formValue.value.icon[0].url, icon: formValue.value.icon[0].url,
@@ -211,6 +247,7 @@ const handleValidateClick = async (e) => {
$message.success('成功') $message.success('成功')
clear() clear()
getList() getList()
getOptions()
} catch (error) { } catch (error) {
$message.error(error.msg) $message.error(error.msg)
} }

View File

@@ -128,6 +128,42 @@
<n-form-item label="手续费比例:" path="scale"> <n-form-item label="手续费比例:" path="scale">
<n-input-number v-model:value="formValue.scale" placeholder="请输入手续费比例" /> <n-input-number v-model:value="formValue.scale" placeholder="请输入手续费比例" />
</n-form-item> </n-form-item>
<n-form-item label="提现额度:" path="withdraw_amount">
<n-input-number
v-model:value="formValue.withdraw_amount"
:step="1000"
placeholder="请输入提现额度"
/>
</n-form-item>
<n-form-item label="兑换额度:" path="exchange_amount">
<n-input-number
v-model:value="formValue.exchange_amount"
:step="1000"
placeholder="请输入兑换额度"
/>
</n-form-item>
<!-- <n-form-item label="聚合积分额度:" path="quota">
<n-input-number v-model:value="formValue.quota" placeholder="请输入聚合积分额度" />
</n-form-item>
<n-form-item label="聚合兑换比例:" path="ratio">
<n-input-number
v-model:value="formValue.ratio"
placeholder="请输入聚合兑换比例"
:precision="2"
/>
</n-form-item> -->
<!-- <n-form-item label="聚合appid:" path="appid">
<n-input v-model:value="formValue.appid" placeholder="请输入聚合appid" />
</n-form-item>
<n-form-item label="聚合appKey:" path="appkey">
<n-input v-model:value="formValue.appkey" placeholder="请输入聚合appKey" />
</n-form-item>
<n-form-item label="聚合查询接口:" path="check_url">
<n-input v-model:value="formValue.check_url" placeholder="请输入聚合查询接口" />
</n-form-item>
<n-form-item label="聚合扣除接口:" path="edit_url">
<n-input v-model:value="formValue.edit_url" placeholder="请输入聚合扣除接口" />
</n-form-item> -->
<n-form-item label="商户状态:" path="status"> <n-form-item label="商户状态:" path="status">
<n-switch v-model:value="formValue.status" :checked-value="1" :unchecked-value="2" /> <n-switch v-model:value="formValue.status" :checked-value="1" :unchecked-value="2" />
</n-form-item> </n-form-item>
@@ -268,7 +304,7 @@ const columns = ref([
}, },
}, },
{ {
title: '积分', title: '余额',
align: 'center', align: 'center',
key: 'integral', key: 'integral',
}, },
@@ -300,24 +336,24 @@ const columns = ref([
), ),
[[vPerms, ['/admin/store/edit']]] [[vPerms, ['/admin/store/edit']]]
), ),
withDirectives( // withDirectives(
h( // h(
NButton, // NButton,
{ // {
class: 'ml-10', // class: 'ml-10',
type: 'primary', // type: 'primary',
text: true, // text: true,
size: 'small', // size: 'small',
onClick: () => { // onClick: () => {
model.value.name = row.name // model.value.name = row.name
model.value.bid = row.bid // model.value.bid = row.bid
showModalJf.value = true // showModalJf.value = true
}, // },
}, // },
() => '退积分' // () => '退积分'
), // ),
[[vPerms, ['/admin/store/set/integral']]] // [[vPerms, ['/admin/store/set/integral']]]
), // ),
withDirectives( withDirectives(
h( h(
NButton, NButton,
@@ -333,7 +369,7 @@ const columns = ref([
window.open( window.open(
`${import.meta.env.VITE_MER_LOGIN_URL}?redirect=/workbench&type=${ `${import.meta.env.VITE_MER_LOGIN_URL}?redirect=/workbench&type=${
res.data.type res.data.type
}&tk=${res.data.token}` }&tk=${res.data.token}&api=${localStorage.getItem('api_endpoint')}`
) )
}, },
}, },
@@ -387,8 +423,12 @@ let formValue = ref({
password: '', password: '',
scaleType: null, scaleType: null,
scale: null, scale: null,
quota: null,
ratio: null,
status: 2, status: 2,
sort: 0, sort: 0,
withdraw_amount: 0,
exchange_amount: 0,
}) })
const rules = { const rules = {
@@ -408,7 +448,7 @@ const rules = {
trigger: 'blur', trigger: 'blur',
}, },
mobile: { mobile: {
required: true, required: false,
message: '请输入商户座机', message: '请输入商户座机',
trigger: 'blur', trigger: 'blur',
}, },
@@ -445,6 +485,18 @@ const rules = {
message: '请输入手续费比例', message: '请输入手续费比例',
trigger: 'blur', trigger: 'blur',
}, },
withdraw_amount: {
required: true,
type: 'number',
message: '请输入提现额度',
trigger: 'blur',
},
exchange_amount: {
required: true,
type: 'number',
message: '请输入兑换额度',
trigger: 'blur',
},
status: { status: {
type: 'number', type: 'number',
message: '请选择商户状态', message: '请选择商户状态',
@@ -526,6 +578,8 @@ const handleClearValidateClick = () => {
scaleType: null, scaleType: null,
scale: null, scale: null,
status: 2, status: 2,
quota: null,
ratio: null,
sort: 0, sort: 0,
} }
} }

View File

@@ -3,4 +3,5 @@ import { request } from '@/utils'
export default { export default {
getHotlist: (data) => request.post('/goods', data), getHotlist: (data) => request.post('/goods', data),
getHotStatus: (data) => request.post('/goods/process', data), getHotStatus: (data) => request.post('/goods/process', data),
updateType: (data) => request.post('/goods/edit/activity', data),
} }

View File

@@ -1,13 +1,107 @@
<!-- eslint-disable vue/no-v-html --> <!-- eslint-disable vue/no-v-html -->
<template> <template>
<CommonPage show-footer :title="$route.title"> <CommonPage show-footer :title="$route.title">
<!-- {{ queryParams }} -->
<n-grid class="mb-10" x-gap="12">
<n-gi span="12" mt-10 flex items-center>
<span w-100>筛选条件:</span>
<n-input-group>
<n-select
v-model:value="queryParams.selectKey"
:style="{ width: '25%' }"
:options="selectOptions"
placeholder="请选择"
/>
<n-input v-model:value="queryParams.word" :style="{ width: '50%' }" />
</n-input-group>
</n-gi>
<n-gi :span="24" mt-10>
<div>
<span>审核状态</span>
<n-radio-group v-model:value="queryParams.status">
<n-radio-button
v-for="song in [
{
label: '已审核',
value: 1,
},
{
label: '拒绝',
value: 2,
},
{
label: '待审核',
value: 3,
},
]"
:key="song.value"
:value="song.value"
:label="song.label"
/>
</n-radio-group>
</div>
</n-gi>
<n-gi :span="24" mt-10>
<div>
<span>商品类型:</span>
<n-radio-group v-model:value="queryParams.type">
<n-radio-button
v-for="song in [
{
label: '普通商品',
value: 0,
},
{
label: '活动商品',
value: 1,
},
{
label: '积分商品',
value: 2,
},
{
label: '摇球商品',
value: 3,
},
]"
:key="song.value"
:value="song.value"
:label="song.label"
/>
</n-radio-group>
</div>
</n-gi>
<n-gi span="24" mt-10 flex items-center>
<n-button type="primary" @click="getList">查询</n-button>
<n-button ml-10 @click="clear">重置</n-button>
</n-gi>
<n-gi span="24" mt-10 flex items-center>
<n-button strong secondary type="primary" @click="veeifys()">批量审核</n-button>
<n-button strong secondary ml-10 type="primary" @click="changeGoodsType(0)">
设为普通商品
</n-button>
<n-button strong secondary ml-10 type="warning" @click="changeGoodsType(1)">
设为活动商品
</n-button>
<n-button strong secondary ml-10 type="info" @click="changeGoodsType(2)">
设为兑换商品
</n-button>
<n-button strong secondary ml-10 type="error" @click="changeGoodsType(3)">
设为摇球机活动商品
</n-button>
</n-gi>
</n-grid>
<n-data-table <n-data-table
:loading="loading" :loading="loading"
:columns="columns" :columns="columns"
:data="data" :data="data"
:pagination="pagination" :pagination="pagination"
:bordered="false" :bordered="false"
:row-key="(row) => row.gid"
:checked-row-keys="queryParams.checkedRowKeysRef"
remote remote
@update:checked-row-keys="handleCheck"
/> />
<!-- 拒绝 --> <!-- 拒绝 -->
<n-modal v-model:show="isNoteModel"> <n-modal v-model:show="isNoteModel">
@@ -79,6 +173,17 @@
:min="0" :min="0"
/> />
</n-form-item-gi> </n-form-item-gi>
<n-form-item-gi :span="20" label="商品抵扣比例" path="discount">
<n-input-number
v-model:value="nowRow.discount"
clearable
placeholder="请输入抵扣比例...."
:min="0"
:precision="0"
>
<template #suffix>%</template>
</n-input-number>
</n-form-item-gi>
<n-form-item-gi :span="20" label="豆子过期时间" path="expiration"> <n-form-item-gi :span="20" label="豆子过期时间" path="expiration">
<n-input-number <n-input-number
v-model:value="nowRow.expiration" v-model:value="nowRow.expiration"
@@ -152,7 +257,7 @@
<script setup> <script setup>
import api from './api' import api from './api'
import { NButton, NImage, NSpace, NEllipsis } from 'naive-ui' import { NButton, NImage, NSpace, NEllipsis, NTag } from 'naive-ui'
import { h, withDirectives, resolveDirective } from 'vue' import { h, withDirectives, resolveDirective } from 'vue'
const vPerms = resolveDirective('perms') const vPerms = resolveDirective('perms')
@@ -171,6 +276,39 @@ const notesVal = ref('')
const formRef = ref(null) const formRef = ref(null)
const selectOptions = ref([
{
label: '商品名称',
value: 0,
},
{
label: '商家名称',
value: 1,
},
])
const queryParams = ref({
selectKey: 0,
word: '',
type: '',
status: 0,
checkedRowKeysRef: [],
})
const handleCheck = (row) => {
queryParams.value.checkedRowKeysRef = row
}
const changeGoodsType = async (type) => {
if (queryParams.value.checkedRowKeysRef.length === 0) return $message.info('没有选中商品')
await api.updateType({
type: type,
gid: queryParams.value.checkedRowKeysRef,
})
getList()
queryParams.value.checkedRowKeysRef = []
}
const rules = { const rules = {
pulse: { pulse: {
required: true, required: true,
@@ -196,12 +334,21 @@ const rules = {
message: '请输入分佣比例', message: '请输入分佣比例',
trigger: 'blur', trigger: 'blur',
}, },
discount: {
required: true,
type: 'number',
message: '请输入抵扣比例',
trigger: 'blur',
},
} }
const nowRow = ref({}) const nowRow = ref({})
const nowKey = ref(null) const nowKey = ref(null)
const columns = ref([ const columns = ref([
{
type: 'selection',
},
{ {
title: '商品名称', title: '商品名称',
slot: 'name', slot: 'name',
@@ -218,6 +365,20 @@ const columns = ref([
) )
}, },
}, },
{
title: '商家名称',
slot: 'store_name',
align: 'center',
render: (row) => {
return h(
'span',
{},
{
default: () => row.Store.name,
}
)
},
},
{ {
title: '商品封面', title: '商品封面',
slot: 'cover', slot: 'cover',
@@ -243,11 +404,60 @@ const columns = ref([
) )
}, },
}, },
{
title: '商品类型',
slot: 'type',
align: 'center',
render(row) {
const obj = {
0: {
type: 'success',
text: '普通商品',
},
1: {
type: 'warning',
text: '活动商品',
},
2: {
type: 'info',
text: '兑换商品',
},
3: {
type: 'error',
text: '摇球机活动商品',
},
}
return h(
NTag,
{
type: obj[row.type].type,
},
{
default: () => obj[row.type].text,
}
)
},
},
{ {
title: '商品价格(元)', title: '商品价格(元)',
key: 'number', key: 'number',
align: 'center', align: 'center',
}, },
{
title: '抵扣后价格(元)',
key: 'discount_price',
align: 'center',
},
{
title: '积分抵扣(元)',
key: 'exchange',
align: 'center',
},
{
title: '抵扣比例(%',
key: 'discount',
align: 'center',
},
{ {
title: '商品库存', title: '商品库存',
key: 'stock', key: 'stock',
@@ -408,11 +618,19 @@ onMounted(() => {
const getList = async () => { const getList = async () => {
loading.value = true loading.value = true
try { try {
const query_data = {
status: queryParams.value.status,
type: queryParams.value.type,
}
query_data[queryParams.value.selectKey === 0 ? 'name' : 'store_name'] = queryParams.value.word
const res = await api.getHotlist({ const res = await api.getHotlist({
pageNum: pagination.value.page, pageNum: pagination.value.page,
pageSize: pagination.value.pageSize, pageSize: pagination.value.pageSize,
...query_data,
}) })
data.value = res.data.data || [] data.value = res.data.data.sort((a, b) => b.status - a.status) || []
pagination.value.itemCount = res.data.total pagination.value.itemCount = res.data.total
} catch (error) { } catch (error) {
$message.error(error.msg) $message.error(error.msg)
@@ -425,32 +643,56 @@ const clear = () => {
isDzModel.value = false isDzModel.value = false
notesVal.value = '' notesVal.value = ''
nowRow.value = {} nowRow.value = {}
queryParams.value = {
selectKey: 0,
word: '',
type: '',
status: 0,
checkedRowKeysRef: [],
}
getList()
} }
const veeify = async () => { const veeify = async () => {
let data = {} try {
if (nowKey.value === 1 || nowKey.value === 2) { let data = {}
data = { if (nowKey.value === 1 || nowKey.value === 2) {
gid: nowRow.value.gid, data = {
status: nowKey.value, gid: [nowRow.value.gid],
notes: notesVal.value, status: nowKey.value,
} notes: notesVal.value,
await api.getHotStatus(data)
await getList()
clear()
} else {
formRef.value?.validate(async (errors) => {
if (!errors) {
data = {
...nowRow.value,
}
await api.getHotStatus(data)
await getList()
clear()
} }
}) await api.getHotStatus(data)
await getList()
// clear()
} else {
formRef.value?.validate(async (errors) => {
if (!errors) {
data = {
...nowRow.value,
gid: [nowRow.value.gid],
}
await api.getHotStatus(data)
await getList()
// clear()
}
})
}
} finally {
isNoteModel.value = false
isDzModel.value = false
} }
} }
const veeifys = async () => {
if (queryParams.value.checkedRowKeysRef.length === 0) return $message.info('没有选中商品')
await api.getHotStatus({
gid: queryParams.value.checkedRowKeysRef,
status: 1,
notes: '',
})
clear()
}
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>

View File

@@ -5,7 +5,7 @@
<img src="@/assets/images/404.webp" width="500" /> <img src="@/assets/images/404.webp" width="500" />
</template> </template>
<template #footer> <template #footer>
<n-button @click="replace('/')">返回首页</n-button> <n-button @click="replace('/workbench')">返回首页</n-button>
</template> </template>
</n-result> </n-result>
</AppPage> </AppPage>

View File

@@ -4,4 +4,11 @@ export default {
getData: (data) => request.post('/store/withdraw', data), getData: (data) => request.post('/store/withdraw', data),
// 审核提现 // 审核提现
passAudit: (data) => request.post('/store/withdraw/edit', data), passAudit: (data) => request.post('/store/withdraw/edit', data),
getYdata: (data) => request.post('/store/amount/withdraw', data),
ydataEdit: (data) => request.post('/store/amount/withdraw/edit', data),
// 溯源统计
suyuanData: (data) => request.post('/pulse/count', data),
// 获取游戏大厅
getGameData: (data) => request.post('/game/list', data),
} }

View File

@@ -8,7 +8,18 @@
<n-number-animation <n-number-animation
ref="numberAnimationInstRef" ref="numberAnimationInstRef"
:from="0" :from="0"
:to="cardData.total / 100" :to="cardData.total"
:precision="2"
/>
</n-statistic>
</n-card>
<n-card ml-10 w-400>
<n-statistic label="服务费" tabular-nums>
<n-number-animation
ref="numberAnimationInstRef"
:from="0"
:to="cardData.commission || 0 / 100"
:precision="2"
/> />
</n-statistic> </n-statistic>
</n-card> </n-card>
@@ -17,17 +28,14 @@
<n-number-animation <n-number-animation
ref="numberAnimationInstRef" ref="numberAnimationInstRef"
:from="0" :from="0"
:to="cardData.service / 100" :to="cardData.service"
:precision="2"
/> />
</n-statistic> </n-statistic>
</n-card> </n-card>
<n-card ml-10 w-400> <n-card ml-10 w-400>
<n-statistic label="已审核金额" tabular-nums> <n-statistic label="已审核金额" tabular-nums>
<n-number-animation <n-number-animation ref="numberAnimationInstRef" :from="0" :to="cardData.count" />
ref="numberAnimationInstRef"
:from="0"
:to="cardData.count / 100"
/>
</n-statistic> </n-statistic>
</n-card> </n-card>
</div> </div>
@@ -126,6 +134,7 @@ const cardData = ref({
total: 0, total: 0,
service: 0, service: 0,
count: 0, count: 0,
commission: 0,
}) })
const songs = ref([ const songs = ref([
@@ -192,7 +201,7 @@ const columns = ref([
align: 'center', align: 'center',
}, },
{ {
title: '上次留存积分', title: '上次留存余额',
key: 'balance', key: 'balance',
align: 'center', align: 'center',
}, },
@@ -353,6 +362,7 @@ const getList = async () => {
pagination.value.itemCount = res.data.total pagination.value.itemCount = res.data.total
cardData.value.total = res.data.all cardData.value.total = res.data.all
cardData.value.service = res.data.audit_integral cardData.value.service = res.data.audit_integral
cardData.value.commission = res.data.audit_commission
cardData.value.count = res.data.success_integral cardData.value.count = res.data.success_integral
loading.value = false loading.value = false
} }

View File

@@ -0,0 +1,279 @@
<template>
<CommonPage show-footer :title="$route.title">
<n-grid class="mb-10" x-gap="12">
<n-gi :span="24">
<div flex>
<n-card w-250>
<n-statistic label="总赢积分" tabular-nums>
<n-number-animation ref="numberAnimationInstRef" :from="0" :to="cardData.win" />
</n-statistic>
</n-card>
<n-card ml-10 w-250>
<n-statistic label="总豆子" tabular-nums>
<n-number-animation ref="numberAnimationInstRef" :from="0" :to="cardData.pulse" />
</n-statistic>
</n-card>
</div>
</n-gi>
<n-gi :span="12">
<div mt-10 flex items-center>
<div w-100>筛选条件:</div>
<n-input-group>
<n-select
v-model:value="queryParams.selectKey"
:style="{ width: '30rem' }"
:options="selectOptions"
placeholder="请选择"
/>
<n-input v-model:value="queryParams.word" :style="{ width: '50rem' }" />
</n-input-group>
</div>
</n-gi>
<n-gi :span="24">
<div mt-10 flex items-center>
<div mr-10>游戏名称:</div>
<n-select
v-model:value="queryParams.hall_id"
w-250
:options="gamelist"
placeholder="请选择游戏"
/>
</div>
</n-gi>
<n-gi :span="24" mt-10>
<div>
<span>豆子状态</span>
<n-radio-group v-model:value="queryParams.Status">
<n-radio-button
v-for="song in songs"
:key="song.value"
:value="song.value"
:label="song.label"
/>
</n-radio-group>
</div>
</n-gi>
<n-gi :span="24" mt-10>
<div>
<span>活动赠送</span>
<n-radio-group v-model:value="queryParams.Type">
<n-radio-button
v-for="song in songs1"
:key="song.value"
:value="song.value"
:label="song.label"
/>
</n-radio-group>
</div>
</n-gi>
<n-gi :span="24">
<div mt-10 flex items-center>
<div>时间筛选</div>
<n-date-picker
v-model:formatted-value="queryParams.time"
value-format="yyyy-MM-dd HH:mm:ss"
type="datetimerange"
clearable
/>
</div>
</n-gi>
<n-gi span="24" mt-10 flex items-center>
<n-button type="primary" @click="getList">查询</n-button>
<n-button ml-10 @click="clear">重置</n-button>
</n-gi>
</n-grid>
<n-data-table
class="mt-10"
:loading="loading"
:columns="columns"
:data="data"
:pagination="pagination"
:bordered="false"
remote
/>
</CommonPage>
</template>
<script setup>
import api from './api'
import dayjs from 'dayjs'
const queryParams = ref({})
const cardData = ref({})
const selectOptions = [
{
label: '商家电话',
value: 0,
},
{
label: '用户电话',
value: 1,
},
]
const songs = ref([
{
value: 1,
label: '未使用',
},
{
value: 3,
label: '用户赢',
},
{
value: 4,
label: '用户输',
},
{
value: 5,
label: '已过期',
},
])
const gamelist = ref([])
const songs1 = ref([
{
value: 5,
label: '注册赠送',
},
{
value: 6,
label: '签到赠送',
},
{
value: 7,
label: '平台赠送',
},
])
const loading = ref(false)
const columns = ref([
{
title: '游戏名称',
align: 'center',
slot: 'game_name',
render: (row) => {
const res = gamelist.value.find((item) => item.value === Number(row.hall_id))
return h('span', null, {
default: () => res?.label || '',
})
},
},
{
title: '订单ID',
align: 'center',
key: 'order_id',
},
{
title: '期数',
align: 'center',
key: 'periods',
},
{
title: '投注豆子',
align: 'center',
key: 'number',
},
{
title: '赢积分',
align: 'center',
key: 'integral',
},
{
title: '用户电话',
align: 'center',
key: 'user_phone',
},
{
title: '商户电话',
align: 'center',
key: 'merchant_phone',
},
{
title: '投注时间',
align: 'center',
key: 'add_time',
},
{
title: '过期时间',
align: 'center',
slot: 'expire',
render: (row) => {
return h('span', null, {
default: () => dayjs(row.expire).format('YYYY-MM-DD HH:mm:ss'),
})
},
},
])
const data = ref([])
const pagination = ref({
page: 1,
pageSize: 10,
itemCount: 0,
onChange: (page) => {
pagination.value.page = page
getList()
},
})
onMounted(() => {
getList()
getGameList()
})
const getGameList = async () => {
const res = await api.getGameData({ pageSize: 9999999, pageNum: 1 })
gamelist.value = res.data.data.map((item) => ({ value: item.ID, label: item.name }))
}
const getList = async () => {
loading.value = true
const query_data = {
...queryParams.value,
StartTime: queryParams.value.time?.[0] || '',
EndTime: queryParams.value.time?.[1] || '',
}
switch (queryParams.value.selectKey) {
case 0:
query_data['merchant_phone'] = queryParams.value.word
break
case 1:
query_data['user_phone'] = queryParams.value.word
break
}
delete query_data.time
delete query_data.word
const res = await api.suyuanData({
pageNum: pagination.value.page,
pageSize: pagination.value.pageSize,
...query_data,
})
data.value = res.data.result || []
pagination.value.itemCount = res.data.count
cardData.value.win = res.data.win
cardData.value.pulse = res.data.pulse
loading.value = false
}
const clear = () => {
queryParams.value = {
word: '',
selectKey: null,
Status: '',
time: null,
Type: '',
hall_id: '',
}
getList()
}
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,414 @@
<template>
<CommonPage show-footer :title="$route.title">
<n-row gutter="12">
<n-col :span="24">
<div flex>
<n-card w-400>
<n-statistic label="总提现金额(含已驳回)" tabular-nums>
<n-number-animation
ref="numberAnimationInstRef"
:from="0"
:to="cardData.total / 100"
:precision="2"
/>
</n-statistic>
</n-card>
<n-card ml-10 w-400>
<n-statistic label="待处理金额" tabular-nums>
<n-number-animation
ref="numberAnimationInstRef"
:from="0"
:to="cardData.service || 0 / 100"
:precision="2"
/>
</n-statistic>
</n-card>
<n-card ml-10 w-400>
<n-statistic label="服务费" tabular-nums>
<n-number-animation
ref="numberAnimationInstRef"
:from="0"
:to="cardData.commission || 0 / 100"
:precision="2"
/>
</n-statistic>
</n-card>
<n-card ml-10 w-400>
<n-statistic label="已审核金额" tabular-nums>
<n-number-animation
ref="numberAnimationInstRef"
:from="0"
:to="cardData.count || 0 / 100"
/>
</n-statistic>
</n-card>
</div>
</n-col>
<n-col :span="24">
<div mt-10>
<span w-80>提现状态:</span>
<n-radio-group v-model:value="queryData.status" class="ml-10">
<n-radio-button
v-for="song in songs"
:key="song.value"
:value="song.value"
:label="song.label"
/>
</n-radio-group>
</div>
</n-col>
<n-col :span="24">
<div mt-10 flex items-center>
<span w-80>号码搜索:</span>
<n-input v-model:value="queryData.word" style="width: 25%" placeholder="请输入手机号码" />
</div>
</n-col>
<n-col :span="10">
<div mt-10 flex items-center>
<span w-80>申请时间:</span>
<n-date-picker
v-model:formatted-value="queryData.time"
value-format="yyyy-MM-dd"
type="daterange"
clearable
/>
</div>
</n-col>
<n-col :span="24">
<div mt-10>
<n-button type="primary" @click="getList">搜索</n-button>
<n-button ml-10 @click="clearQueryData">重置</n-button>
</div>
</n-col>
</n-row>
<n-data-table
class="mt-10"
:loading="loading"
:columns="columns"
:data="data"
:pagination="pagination"
:bordered="false"
remote
/>
<!-- 打款记录 -->
<n-modal v-model:show="showModal">
<n-card
style="width: 500px"
title="打款记录"
:bordered="false"
size="huge"
role="dialog"
aria-modal="true"
>
<n-form ref="formRef" :model="model" :rules="rules" label-placement="left">
<n-grid :cols="1" :x-gap="24">
<n-form-item-gi :span="12" label="打款截图" path="img">
<Upload v-model:list="model.img" />
</n-form-item-gi>
<n-form-item-gi>
<div w-full flex justify-center>
<n-button type="primary" @click="ok">确定</n-button>
<n-button class="ml-10" @click="clear">关闭</n-button>
</div>
</n-form-item-gi>
</n-grid>
</n-form>
</n-card>
</n-modal>
</CommonPage>
</template>
<script setup>
import api from './api'
import { NButton, NImage, NTag } from 'naive-ui'
import Upload from '@/components/Upload.vue'
import { h, withDirectives, resolveDirective } from 'vue'
const vPerms = resolveDirective('perms')
const vRole = [[vPerms, ['/admin/store/amount/withdraw/edit']]]
const showModal = ref(false)
const queryData = ref({
status: '',
word: '',
time: '',
})
const cardData = ref({
total: 0,
service: 0,
count: 0,
commission: 0,
})
const songs = ref([
{
value: 1,
label: '已审核',
},
{
value: 2,
label: '已驳回',
},
{
value: 3,
label: '待审核',
},
])
const nowRow = ref({})
const formRef = ref(null)
const model = ref({
img: [],
})
const rules = {
img: { required: true, type: 'array', message: '请上传打款截图' },
}
const columns = ref([
{
title: '名字',
key: 'name',
align: 'center',
},
{
title: '电话',
key: 'phone',
align: 'center',
},
{
title: '银行名称',
key: 'bank',
align: 'center',
},
{
title: '银行卡号',
key: 'bank_card',
align: 'center',
},
{
title: '账户名称',
key: 'bank_name',
align: 'center',
},
{
title: '法人',
key: 'bank_user',
align: 'center',
},
{
title: '提现金额',
key: 'integral',
align: 'center',
},
{
title: '服务费',
key: 'commission',
align: 'center',
},
{
title: '实际到账',
key: 'number',
align: 'center',
},
{
title: '剩余余额',
key: 'residue',
align: 'center',
},
{
title: '手续费比例',
slot: 'commission_number',
align: 'center',
render: (row) => {
return h(
'span',
{},
{
default: () => `${row.commission_number}%`,
}
)
},
},
{
title: '手续费类型',
key: 'commission_type',
align: 'center',
render: (row) => {
return h(
NTag,
{
type: row.commission_type === 1 ? 'success' : 'warning',
},
{
default: () => (row.commission_type === 1 ? '百分比' : '固定值'),
}
)
},
},
{
title: '审核状态',
align: 'center',
slot: 'status',
render: (row) => {
return h(
NTag,
{
type: row.status === 1 ? 'success' : row.status === 2 ? 'error' : 'warning',
},
{
default: () => (row.status === 1 ? '已审核' : row.status === 2 ? '已驳回' : '待审核'),
}
)
},
},
{
title: '申请时间',
key: 'add_time',
align: 'center',
},
{
title: '打款截图',
slot: 'img',
render: (row) => {
return h(NImage, {
src: row.status_img,
width: '50',
})
},
},
{
title: '操作',
align: 'center',
slot: 'action',
render: (row) => {
if (row.status === 3) {
return [
withDirectives(
h(
NButton,
{
text: true,
type: 'primary',
onClick: () => {
nowRow.value = row
showModal.value = true
},
},
{
default: () => '审核',
}
),
vRole
),
withDirectives(
h(
NButton,
{
class: 'ml-10',
text: true,
type: 'error',
onClick: () => {
nowRow.value = row
refuse()
},
},
{
default: () => '拒绝',
}
),
vRole
),
]
}
},
},
])
const loading = ref(false)
const data = ref([])
const pagination = ref({
page: 1,
pageSize: 10,
itemCount: 0,
onChange: (page) => {
pagination.value.page = page
getList()
},
})
onMounted(() => {
getList()
})
const getList = async () => {
loading.value = true
const query_data = {
Status: queryData.value.status || '',
Phone: queryData.value.word || '',
StartTime: queryData.value.time === null ? '' : queryData.value.time[0] || '',
EndTime: queryData.value.time === null ? '' : queryData.value.time[1] || '',
}
const res = await api.getYdata({
pageNum: pagination.value.page,
pageSize: pagination.value.pageSize,
...query_data,
})
data.value = res.data.data || []
pagination.value.itemCount = res.data.total
cardData.value.total = res.data.all
cardData.value.service = res.data.audit_number
cardData.value.commission = res.data.audit_commission
cardData.value.count = res.data.success_amount
loading.value = false
}
const clear = async () => {
model.value = {
img: [],
}
showModal.value = false
formRef.value?.restoreValidation()
await getList()
}
const ok = () => {
formRef.value?.validate(async (errors) => {
if (!errors) {
const res = await api.passAudit({
bid: nowRow.value.bid,
wid: nowRow.value.wid,
img: model.value.img[0].url,
status: 1,
})
$message.success(res.msg)
clear()
}
})
}
const refuse = async () => {
const res = await api.passAudit({
bid: nowRow.value.bid,
wid: nowRow.value.wid,
img: model.value.img[0]?.url || '',
status: 2,
})
clear()
$message.success(res.msg)
}
const clearQueryData = () => {
queryData.value = {
status: '',
time: null,
word: '',
}
getList()
}
</script>
<style lang="scss" scoped></style>

View File

@@ -1,5 +1,13 @@
import { request } from '@/utils' import { request, multiRequest } from '@/utils'
export default { export default {
login: (data) => request.post('/login', data, { noNeedToken: true }), login: (data) => request.post('/login', data, { noNeedToken: true }),
// 使用多接口的登录方法
loginWithMultiEndpoint: (data) =>
multiRequest.request({
method: 'post',
url: '/login',
data,
noNeedToken: true,
}),
} }

View File

@@ -14,7 +14,17 @@
<img src="@/assets/images/logo.png" height="50" class="mr-10" /> <img src="@/assets/images/logo.png" height="50" class="mr-10" />
{{ title }} {{ title }}
</h5> </h5>
<div mt-30> <!-- 接口线路选择 -->
<div mt-20>
<n-select
v-model:value="selectedEndpoint"
:options="endpointOptions"
placeholder="选择接口线路"
size="large"
@update:value="handleEndpointChange"
/>
</div>
<div mt-10>
<n-input <n-input
v-model:value="loginInfo.name" v-model:value="loginInfo.name"
autofocus autofocus
@@ -23,7 +33,7 @@
:maxlength="20" :maxlength="20"
/> />
</div> </div>
<div mt-30> <div mt-10>
<n-input <n-input
v-model:value="loginInfo.password" v-model:value="loginInfo.password"
class="h-50 items-center pl-10 text-16" class="h-50 items-center pl-10 text-16"
@@ -67,6 +77,12 @@ import { useStorage } from '@vueuse/core'
import bgImg from '@/assets/images/login_bg.webp' import bgImg from '@/assets/images/login_bg.webp'
import api from './api' import api from './api'
import { addDynamicRoutes } from '@/router' import { addDynamicRoutes } from '@/router'
import {
getAvailableEndpoints,
setCurrentEndpoint,
getCurrentEndpoint,
initApiEndpoint,
} from '@/utils/api-config'
const title = import.meta.env.VITE_TITLE const title = import.meta.env.VITE_TITLE
@@ -90,6 +106,61 @@ function initLoginInfo() {
const isRemember = useStorage('isRemember', false) const isRemember = useStorage('isRemember', false)
const loading = ref(false) const loading = ref(false)
// 接口线路相关
const selectedEndpoint = ref('primary')
const endpointOptions = ref([])
// 初始化接口配置
initApiEndpoint()
const currentEndpoint = getCurrentEndpoint()
selectedEndpoint.value = currentEndpoint.key
// 加载接口选项
function loadEndpointOptions() {
const endpoints = getAvailableEndpoints()
endpointOptions.value = endpoints.map((endpoint) => ({
label: endpoint.name,
value: endpoint.key,
}))
}
// 处理接口切换
function handleEndpointChange(value) {
if (setCurrentEndpoint(value)) {
selectedEndpoint.value = value
$message.success(`已切换到${endpointOptions.value.find((opt) => opt.value === value)?.label}`)
} else {
$message.error('接口切换失败')
}
}
// 初始化
onMounted(() => {
loadEndpointOptions()
})
// 获取第一个可用的路由页面
function getFirstAvailableRoute(menuData) {
if (!menuData || !Array.isArray(menuData)) return null
// 递归查找第一个type为2的路由页面路由
function findFirstPageRoute(routes) {
for (const route of routes) {
if (route.type === 2 && route.route) {
return route.route
}
if (route.subMenu && route.subMenu.length) {
const found = findFirstPageRoute(route.subMenu)
if (found) return found
}
}
return null
}
return findFirstPageRoute(menuData)
}
async function handleLogin() { async function handleLogin() {
const { name, password } = loginInfo.value const { name, password } = loginInfo.value
if (!name || !password) { if (!name || !password) {
@@ -99,7 +170,14 @@ async function handleLogin() {
try { try {
loading.value = true loading.value = true
$message.loading('正在验证...') $message.loading('正在验证...')
const res = await api.login({ phone: name, password: password.toString() })
console.log('开始登录请求...')
console.log('登录数据:', { phone: name, password: password.toString() })
console.log('当前选中接口:', selectedEndpoint.value)
const res = await api.loginWithMultiEndpoint({ phone: name, password: password.toString() })
console.log('登录响应:', res)
$message.success('登录成功') $message.success('登录成功')
window.localStorage.setItem('menu', JSON.stringify(res.data.auth)) window.localStorage.setItem('menu', JSON.stringify(res.data.auth))
setToken(res.data.token) setToken(res.data.token)
@@ -108,15 +186,23 @@ async function handleLogin() {
} else { } else {
lStorage.remove('loginInfo') lStorage.remove('loginInfo')
} }
// 先添加动态路由
await addDynamicRoutes() await addDynamicRoutes()
// 获取第一个可用的路由页面
const firstRoute = getFirstAvailableRoute(res.data.auth)
if (query.redirect) { if (query.redirect) {
const path = query.redirect const path = query.redirect
Reflect.deleteProperty(query, 'redirect') Reflect.deleteProperty(query, 'redirect')
router.push({ path, query }) router.push({ path, query })
} else { } else {
router.push('/') // 跳转到第一个可用路由,如果没有则跳转到工作台
router.push(firstRoute || '/workbench')
} }
} catch (error) { } catch (error) {
console.error('登录请求失败:', error)
$message.removeMessage() $message.removeMessage()
} }
loading.value = false loading.value = false

View File

@@ -3,4 +3,7 @@ import { request } from '@/utils'
export default { export default {
getSignConfig: () => request.post('/gift/setting'), getSignConfig: () => request.post('/gift/setting'),
setSignConfig: (data) => request.post('/gift/setting/edit', data), setSignConfig: (data) => request.post('/gift/setting/edit', data),
getCodelist: (data) => request.post('/qrcode/list', data),
addCode: (data) => request.post('/qrcode/add', data),
updateCode: (data) => request.post('/qrcode/edit', data),
} }

View File

@@ -0,0 +1,494 @@
<template>
<CommonPage show-footer :title="$route.title">
<!-- {{ queryData }} -->
<n-row gutter="12">
<n-col :span="24">
<div flex>
<n-card w-500>
<n-statistic label="未激活" tabular-nums>
<n-number-animation ref="numberAnimationInstRef" :from="0" :to="cardData.total" />
</n-statistic>
</n-card>
<n-card ml-10 w-500>
<n-statistic label="已激活" tabular-nums>
<n-number-animation ref="numberAnimationInstRef" :from="0" :to="cardData.service" />
</n-statistic>
</n-card>
<n-card ml-10 w-500>
<n-statistic label="未领取" tabular-nums>
<n-number-animation ref="numberAnimationInstRef" :from="0" :to="cardData.count" />
</n-statistic>
</n-card>
<n-card ml-10 w-500>
<n-statistic label="已领取" tabular-nums>
<n-number-animation ref="numberAnimationInstRef" :from="0" :to="cardData.count" />
</n-statistic>
</n-card>
</div>
</n-col>
<n-col :span="24">
<div mt-10>
<span>激活状态</span>
<n-radio-group v-model:value="queryData.status">
<n-radio-button
v-for="song in songs"
:key="song.value"
:value="song.value"
:label="song.label"
/>
</n-radio-group>
</div>
</n-col>
<n-col :span="24">
<div mt-10>
<span>领取状态</span>
<n-radio-group v-model:value="queryData.status1">
<n-radio-button
v-for="song in songs1"
:key="song.value"
:value="song.value"
:label="song.label"
/>
</n-radio-group>
</div>
</n-col>
<n-col :span="24">
<div mt-10 flex items-center>
<div w-100>关键字搜索</div>
<n-input-group>
<n-select
v-model:value="queryData.selectKey"
:style="{ width: '15%' }"
:options="selectOptions"
placeholder="请选择"
/>
<n-input v-model:value="queryData.word" :style="{ width: '20%' }" />
</n-input-group>
</div>
</n-col>
<n-col :span="10">
<div mt-10 flex items-center>
<span w-100>时间搜索</span>
<n-date-picker
v-model:formatted-value="queryData.time"
value-format="yyyy-MM-dd HH:mm:ss"
type="datetimerange"
clearable
/>
</div>
</n-col>
<n-col :span="24">
<div mt-10>
<n-button v-perms="['/admin/qrcode/add']" mr-10 type="primary" @click="openModal(1)">
新增二维码
</n-button>
<n-button v-perms="['/admin/qrcode/edit']" mr-10 type="warning" @click="openModal(2)">
修改二维码
</n-button>
<n-button mr-10 type="info" @click="saveCode">下载二维码</n-button>
<!-- <n-button mr-10 type="primary" @click="openModal(1)">新增二维码</n-button> -->
<!-- <n-button mr-10 type="warning" @click="openModal(3)">修改二维码</n-button> -->
<n-button type="primary" @click="getList">搜索</n-button>
<n-button ml-10 @click="clear">重置</n-button>
</div>
</n-col>
</n-row>
<n-data-table
class="mt-5"
:loading="loading"
:columns="columns"
:data="data"
:pagination="pagination"
:bordered="false"
:row-key="rowKey"
:checked-row-keys="checkedRowKeysRef"
remote
@update:checked-row-keys="handleCheck"
/>
<n-modal v-model:show="showModal" :auto-focus="false">
<n-card
style="width: 600px"
:title="modelTitle"
:bordered="false"
size="huge"
role="dialog"
aria-modal="true"
>
<n-form ref="formRef" label-placement="left" :model="formValue" :rules="rules">
<n-form-item v-if="modalType === 1" label="新增数量:" path="number">
<n-input-number v-model:value="formValue.number" :min="1" clearable />
</n-form-item>
<n-form-item label="赠送豆子:" path="pulse">
<n-input-number v-model:value="formValue.pulse" :min="1" clearable />
</n-form-item>
<n-form-item label="激活状态:" path="activate_status">
<n-switch
v-model:value="formValue.activate_status"
:checked-value="1"
:unchecked-value="2"
/>
</n-form-item>
<n-form-item>
<div class="m-auto">
<n-button
class="m-auto"
type="primary"
attr-type="button"
@click="handleValidateClick"
>
提交
</n-button>
<n-button class="ml-10" @click="handleclear">取消</n-button>
</div>
</n-form-item>
</n-form>
</n-card>
</n-modal>
</CommonPage>
</template>
<script setup>
import { NButton } from 'naive-ui'
import JSZip from 'jszip'
import { saveAs } from 'file-saver'
import api from '../api'
import { ref, resolveDirective, withDirectives } from 'vue'
const vPerms = resolveDirective('perms')
const songs = ref([
{
value: 1,
label: '已激活',
},
{
value: 2,
label: '未激活',
},
])
const songs1 = ref([
{
value: 1,
label: '已领取',
},
{
value: 2,
label: '未领取',
},
])
const selectOptions = ref([
{
value: 0,
label: 'ID',
},
{
value: 1,
label: '手机号',
},
])
const queryData = ref({
status: '',
status1: '',
time: null,
word: '',
selectKey: null,
})
const cardData = ref({})
const columns = ref([
{
type: 'selection',
},
{
title: 'ID',
align: 'center',
key: 'qid',
},
{
title: '二维码',
align: 'center',
slot: 'qrcode',
render(row) {
return h('img', {
src: row.url,
style: {
width: '50px',
height: '50px',
},
})
},
},
{
title: '用户',
align: 'center',
slot: 'user_name',
render(row) {
return h('span', row.User.nickName)
},
},
{
title: '用户电话',
align: 'center',
slot: 'phone',
render(row) {
return h('span', row.User.phone)
},
},
{
title: '赠送豆子',
align: 'center',
key: 'pulse',
},
{
title: '激活状态',
align: 'center',
slot: 'status',
render(row) {
switch (row.activate_status) {
case 1:
return h('span', '已激活')
case 2:
return h('span', '未激活')
}
},
},
{
title: '领取状态',
align: 'center',
slot: 'status',
render(row) {
switch (row.use_status) {
case 1:
return h('span', '已领取')
case 2:
return h('span', '未领取')
}
},
},
{
title: '激活时间',
align: 'center',
key: 'activate_time',
},
{
title: '领取时间',
align: 'center',
key: 'use_time',
},
{
title: '操作',
align: 'center',
slot: 'action',
render(row) {
if (row.use_status === 2) {
return withDirectives(
h(
NButton,
{
type: 'primary',
size: 'small',
onClick: () => openModal(2, row),
},
{
default: () => '编辑',
}
),
[[vPerms, ['/admin/qrcode/edit']]]
)
// return h(
// NButton,
// {
// type: 'primary',
// size: 'small',
// onClick: () => {
// openModal(2, row)
// },
// },
// {
// default: () => '编辑',
// }
// )
}
},
},
])
const data = ref([])
const pagination = ref({
page: 1,
pageSize: 10,
itemCount: 0,
prefix: ({ itemCount }) => {
return `${itemCount}`
},
onChange: (page) => {
pagination.value.page = page
getList()
},
onUpdatePageSize: (pageSize) => {
pagination.value.pageSize = pageSize
pagination.value.page = 1
getList()
},
})
onMounted(() => {
getList()
})
const loading = ref(false)
const getList = async () => {
loading.value = true
try {
const query_data = {
UseStatus: queryData.value.status,
ActivateStatus: queryData.value.status1,
StartTime: queryData.value.time === null ? '' : queryData.value.time[0] || '',
EndTime: queryData.value.time === null ? '' : queryData.value.time[1] || '',
}
switch (queryData.value.selectKey) {
case 0:
query_data['Qid'] = queryData.value.word
break
case 1:
query_data['Phone'] = queryData.value.word
break
}
const res = await api.getCodelist({
pageNum: pagination.value.page,
pageSize: pagination.value.pageSize,
...query_data,
})
data.value = res.data.data || []
pagination.value.itemCount = res.data.total
cardData.value.total = res.data.number
cardData.value.service = res.data.commission
cardData.value.count = 0
} catch (error) {
$message.error(error.msg)
}
loading.value = false
}
const clear = () => {
queryData.value = {
status: '',
status1: '',
time: null,
word: '',
selectKey: null,
}
getList()
}
const rowKey = (row) => row.qid
const checkedRowKeysRef = ref([])
const handleCheck = (rowKeys) => {
checkedRowKeysRef.value = rowKeys
}
const showModal = ref(false)
const modelTitle = ref('')
const formValue = ref({
number: null,
pulse: null,
activate_status: 2,
})
const rules = {
number: {
required: true,
type: 'number',
message: '请输入生成数量',
trigger: 'blur',
},
pulse: {
required: true,
type: 'number',
message: '请输入豆子数量',
trigger: 'blur',
},
}
const handleclear = () => {
showModal.value = false
formValue.value = {
number: null,
pulse: null,
activate_status: 2,
}
checkedRowKeysRef.value = []
}
const modalType = ref(null)
const nowRow = ref([])
const openModal = (e, row) => {
showModal.value = true
modalType.value = e
modelTitle.value = e === 1 ? '新增二维码' : '编辑二维码'
if (e === 2) {
nowRow.value = [row.qid]
formValue.value.pulse = row.pulse
formValue.value.activate_status = row.activate_status
} else if (e === 3) {
nowRow.value = checkedRowKeysRef.value
}
}
const formRef = ref(null)
const handleValidateClick = (e) => {
e.preventDefault()
formRef.value?.validate(async (errors) => {
if (!errors) {
try {
if (modalType.value === 1) {
await api.addCode(formValue.value)
} else {
await api.updateCode({
qid: nowRow.value,
...formValue.value,
})
}
handleclear()
getList()
} catch (error) {
// $message.error(error.msg)
}
}
})
}
// 下载二维码
const saveCode = async () => {
if (checkedRowKeysRef.value.length === 0) return $message.error('请选择二维码')
const zip = new JSZip()
data.value.forEach((item) => {
checkedRowKeysRef.value.forEach((id) => {
if (item.qid === id) {
const imageData = item.url.split(',')[1]
zip.file(`${item.qid}.png`, imageData, {
base64: true,
})
}
})
})
const content = await zip.generateAsync({ type: 'blob' })
saveAs(content, '二维码.zip')
}
</script>
<style lang="scss" scoped></style>

View File

@@ -18,6 +18,16 @@
<n-number-animation :from="0" :to="cardData.count" /> <n-number-animation :from="0" :to="cardData.count" />
</n-statistic> </n-statistic>
</n-card> </n-card>
<n-card ml-10 w-500>
<n-statistic label="赠送豆子" tabular-nums>
<n-number-animation :from="0" :to="cardData.gift_pulse" />
</n-statistic>
</n-card>
<n-card ml-10 w-500>
<n-statistic label="抵扣后价格(元)" tabular-nums>
<n-number-animation :from="0" :to="cardData.discount_number" :precision="2" />
</n-statistic>
</n-card>
</div> </div>
</n-col> </n-col>
<n-col :span="24" mt-10> <n-col :span="24" mt-10>
@@ -40,13 +50,21 @@
<n-radio-button <n-radio-button
v-for="song in [ v-for="song in [
{ {
label: '微信', label: '微信支付',
value: 1, value: 1,
}, },
{ {
label: '积分', label: '平台积分',
value: 2, value: 2,
}, },
{
label: '智多鑫积分',
value: 3,
},
{
label: '渔乐潮玩积分',
value: 5,
},
]" ]"
:key="song.value" :key="song.value"
:value="song.value" :value="song.value"
@@ -83,24 +101,27 @@
<n-col :span="24"> <n-col :span="24">
<div mt-10> <div mt-10>
<n-button type="primary" @click="getList">搜索</n-button> <n-button type="primary" @click="getList">搜索</n-button>
<n-button type="primary" ml-10 @click="exportTable">导出表格</n-button>
<n-button ml-10 @click="clear">重置</n-button> <n-button ml-10 @click="clear">重置</n-button>
</div> </div>
</n-col> </n-col>
</n-row> </n-row>
<n-data-table <n-data-table
ref="dataTableRef"
class="mt-5" class="mt-5"
:loading="loading" :loading="loading"
:columns="columns" :columns="columns"
:data="data" :data="data"
:pagination="pagination" :pagination="pagination"
:bordered="false" :bordered="false"
:scroll-x="1800" :scroll-x="2000"
remote remote
/> />
</CommonPage> </CommonPage>
</template> </template>
<script setup> <script setup>
import * as XLSX from 'xlsx'
import api from './api' import api from './api'
const loading = ref(false) const loading = ref(false)
@@ -117,6 +138,7 @@ const cardData = ref({
total: 0, total: 0,
service: 0, service: 0,
count: 0, count: 0,
gift_pulse: 0,
}) })
const songs = ref([ const songs = ref([
@@ -164,12 +186,11 @@ const selectOptions = ref([
label: '商家名称', label: '商家名称',
}, },
]) ])
const columns = ref([ const columns = ref([
{ {
title: '订单号', title: '订单号',
align: 'center', align: 'center',
key: 'oid', key: 'jl_oid',
width: 200, width: 200,
fixed: 'left', fixed: 'left',
}, },
@@ -232,16 +253,26 @@ const columns = ref([
key: 'count', key: 'count',
}, },
{ {
title: '订单总价', title: '订单金额',
align: 'center', align: 'center',
slot: 'number', slot: 'number',
render: (row) => h('span', row.pay_type === 1 ? `${row.price}元` : `${row.exchange}积分`), render: (row) => h('span', `${row.price}元`),
},
{
title: '抵扣后价格(元)',
key: 'discount_price',
align: 'center',
},
{
title: '积分抵扣',
key: 'exchange',
align: 'center',
}, },
{ {
title: '支付方式', title: '支付方式',
align: 'center', align: 'center',
slot: 'pay_type', slot: 'pay_type',
render: (row) => h('span', row.pay_type === 1 ? '微信' : '积分'), render: (row) => h('span', row.PayInfo.name),
}, },
{ {
title: '商家名称', title: '商家名称',
@@ -297,6 +328,8 @@ const pagination = ref({
page: 1, page: 1,
pageSize: 10, pageSize: 10,
itemCount: 0, itemCount: 0,
showSizePicker: true,
pageSizes: [10, 20, 50, 100],
onChange: (page) => { onChange: (page) => {
pagination.value.page = page pagination.value.page = page
getList() getList()
@@ -311,6 +344,21 @@ const pagination = ref({
onMounted(() => { onMounted(() => {
getList() getList()
}) })
const dataTableRef = ref(null)
const exportTable = () => {
// console.log(dataTableRef.value)
// dataTableRef.value?.downloadCsv({
// fileName: new Date().getTime() + '.csv',
// keepOriginalData: false,
// })
// 将数据转换为工作簿
const worksheet = XLSX.utils.json_to_sheet(data.value)
const workbook = XLSX.utils.book_new()
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1')
// 生成Excel文件并触发下载
XLSX.writeFile(workbook, 'table.xlsx')
}
const getList = async () => { const getList = async () => {
loading.value = true loading.value = true
@@ -347,6 +395,8 @@ const getList = async () => {
cardData.value.total = res.data.number cardData.value.total = res.data.number
cardData.value.service = res.data.commission cardData.value.service = res.data.commission
cardData.value.count = res.data.total cardData.value.count = res.data.total
cardData.value.gift_pulse = res.data.gift_pulse
cardData.value.discount_number = res.data.discount_number
} catch (error) { } catch (error) {
$message.error(error.msg) $message.error(error.msg)
} }

View File

@@ -0,0 +1,5 @@
import { request } from '@/utils'
export default {
getList: (data) => request.post('/later/order', data),
}

View File

@@ -0,0 +1,382 @@
<template>
<CommonPage show-footer :title="$route.title">
<n-row gutter="12">
<n-col :span="24">
<div flex>
<n-card w-500 rounded-5 style="background-color: #5579e9">
<n-statistic label="交易金额(元)" tabular-nums>
<n-number-animation :from="0" :to="cardData.payments" :precision="2" />
</n-statistic>
</n-card>
<n-card ml-10 w-500 rounded-5 style="background-color: #e74f5b">
<n-statistic label="订单数量(条)" tabular-nums>
<n-number-animation :from="0" :to="cardData.count" />
</n-statistic>
</n-card>
<n-card ml-10 w-500 rounded-5 style="background-color: #00b4c3">
<n-statistic label="赠送豆子(个)" tabular-nums>
<n-number-animation :from="0" :to="cardData.total_pulse" :precision="2" />
</n-statistic>
</n-card>
<n-card ml-10 w-500 rounded-5 style="background-color: #f1b301">
<n-statistic label="挂帐(元)" tabular-nums>
<n-number-animation :from="0" :to="cardData.onAccount" :precision="2" />
</n-statistic>
</n-card>
<n-card ml-10 w-500 rounded-5 style="background-color: #ffa940">
<n-statistic label="抹零(元)" tabular-nums>
<n-number-animation :from="0" :to="cardData.zero" :precision="2" />
</n-statistic>
</n-card>
</div>
</n-col>
<n-col :span="24" mt-10>
<div>
<span>订单状态</span>
<n-radio-group v-model:value="queryData.status">
<n-radio-button
v-for="song in songs"
:key="song.value"
:value="song.value"
:label="song.label"
/>
</n-radio-group>
</div>
</n-col>
<n-col :span="24">
<div mt-10 flex items-center>
<div w-100>关键字搜索</div>
<n-input-group>
<n-select
v-model:value="queryData.selectKey"
:style="{ width: '25rem' }"
:options="selectOptions"
placeholder="请选择"
/>
<n-input v-model:value="queryData.word" :style="{ width: '20%' }" />
</n-input-group>
</div>
</n-col>
<n-col :span="10">
<div mt-10 flex items-center>
<span w-100>订单时间</span>
<n-date-picker
v-model:formatted-value="queryData.time"
value-format="yyyy-MM-dd HH:mm:ss"
type="datetimerange"
clearable
/>
</div>
</n-col>
<n-col :span="24">
<div mt-10>
<n-button type="primary" @click="getList">搜索</n-button>
<n-button type="primary" ml-10 @click="exportTable">导出表格</n-button>
<n-button ml-10 @click="clear">重置</n-button>
</div>
</n-col>
</n-row>
<n-data-table
ref="tableRef"
class="mt-5"
:loading="loading"
:columns="columns"
:data="data"
:pagination="pagination"
:bordered="false"
:scroll-x="1800"
remote
/>
</CommonPage>
</template>
<script setup>
import api from './api'
import * as XLSX from 'xlsx'
import { NEllipsis, NButton, NPopconfirm } from 'naive-ui'
const loading = ref(false)
const queryData = ref({
status: '',
time: null,
word: '',
selectKey: null,
})
const songs = ref([
{
value: 1,
label: '未付款',
},
{
value: 2,
label: '已付款',
},
{
value: 3,
label: '挂帐中',
},
])
const selectOptions = ref([
{
value: 0,
label: '商品名称',
},
{
value: 1,
label: '用户昵称',
},
{
value: 2,
label: '手机号',
},
{
value: 3,
label: '订单号',
},
{
value: 4,
label: '商家名称',
},
])
const columns = ref([
{
title: '桌号',
align: 'center',
key: 'seat',
},
{
title: '订单号',
align: 'center',
key: 'jl_oid',
width: 150,
fixed: 'left',
},
{
title: '用户',
align: 'center',
slot: 'nickName',
render: (row) => h('span', {}, { default: () => row.User.nickName }),
},
{
title: '手机号',
align: 'center',
key: 'phone',
},
{
title: '商品名称',
align: 'center',
slot: 'goods_name',
render: (row) => {
const el = []
row.OrderGoods.forEach((item) => {
el.push(
h(
NEllipsis,
{
style: 'max-width: 100px',
},
{
default: () => `${item.Goods.name}|${item.pay_price}元|X${item.number}`,
}
)
)
})
return el
},
},
{
title: '商品数量',
align: 'center',
key: 'count',
},
{
title: '商品总价',
align: 'center',
key: 'payments',
},
{
title: '实付金额',
align: 'center',
key: 'pay_amount',
},
{
title: '抹零',
align: 'center',
key: 'zero',
},
{
title: '订单状态',
align: 'center',
slot: 'status',
render(row) {
switch (row.status) {
case 0:
return h('span', '未付款')
case 1:
return h('span', '已付款')
case 2:
return h('span', '挂帐中')
}
},
},
{
title: '下单时间',
align: 'center',
key: 'add_time',
},
{
title: '挂帐时间',
align: 'center',
key: 'on_account_time',
},
{
title: '付款时间',
align: 'center',
key: 'payment_time',
},
{
title: '备注',
align: 'center',
key: 'notes',
},
{
title: '操作',
align: 'center',
slot: 'action',
width: 100,
fixed: 'right',
render(row) {
return h(
NPopconfirm,
{},
{
default: () => '确定核销吗?',
trigger: () =>
h(
NButton,
{
text: true,
type: 'info',
onClick: () => {
console.log(row)
},
},
{ default: () => '核销' }
),
}
)
},
},
])
const data = ref([])
const cardData = ref({
total: 0,
service: 0,
count: 0,
})
const pagination = ref({
page: 1,
pageSize: 10,
itemCount: 0,
showSizePicker: true,
pageSizes: [10, 20, 50, 100],
onChange: (page) => {
pagination.value.page = page
getList()
},
onUpdatePageSize: (pageSize) => {
pagination.value.pageSize = pageSize
pagination.value.page = 1
getList()
},
})
onMounted(() => {
getList()
})
const getList = async () => {
try {
loading.value = true
const query_data = {
Status: queryData.value.status,
StartTime: queryData.value.time === null ? '' : queryData.value.time[0] || '',
EndTime: queryData.value.time === null ? '' : queryData.value.time[1] || '',
}
switch (queryData.value.selectKey) {
case 0:
query_data['GoodName'] = queryData.value.word
break
case 1:
query_data['UserName'] = queryData.value.word
break
case 2:
query_data['Phone'] = queryData.value.word
break
case 3:
query_data['Oid'] = queryData.value.word
break
case 4:
query_data['StoreName'] = queryData.value.word
break
}
const res = await api.getList({
PageNum: pagination.value.page,
PageSize: pagination.value.pageSize,
...query_data,
})
data.value = res.data.data || []
pagination.value.itemCount = res.data.total
cardData.value.payments = res.data.payments
cardData.value.onAccount = res.data.onAccount
cardData.value.total_pulse = res.data.total_pulse
cardData.value.count = res.data.total
loading.value = false
} catch (error) {
loading.value = false
$message.error(error.msg)
throw error
}
}
const clear = () => {
queryData.value = {
status: '',
store_name: '',
time: null,
word: '',
selectKey: null,
}
getList()
}
const tableRef = ref()
const exportTable = () => {
// 将数据转换为工作簿
const worksheet = XLSX.utils.json_to_sheet(data.value)
const workbook = XLSX.utils.book_new()
XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1')
// 生成Excel文件并触发下载
XLSX.writeFile(workbook, 'table.xlsx')
}
</script>
<style lang="scss" scoped>
:deep(.n-statistic__label) {
color: #fff !important;
font-size: 16px;
}
:deep(.n-statistic-value__content) {
color: #fff !important;
font-size: 16px;
}
</style>

View File

@@ -0,0 +1,6 @@
import { request } from '@/utils'
export default {
getAgreement: () => request.post('/agreement'),
updateAgreement: (data) => request.post('/edit/agreement', data),
}

View File

@@ -0,0 +1,43 @@
<template>
<CommonPage show-footer :title="$route.title">
<n-tabs type="line" animated>
<n-tab-pane name="1" tab="用户协议">
<Editor v-model:value-html="model.user" :height="550" />
</n-tab-pane>
<n-tab-pane name="2" tab="隐私政策">
<Editor v-model:value-html="model.policy" :height="550" />
</n-tab-pane>
<n-tab-pane name="3" tab="积分使用规则">
<Editor v-model:value-html="model.integral" :height="550" />
</n-tab-pane>
</n-tabs>
<n-button v-perms="['/admin/edit/agreement']" mt-10 type="primary" @click="save">保存</n-button>
</CommonPage>
</template>
<script setup>
import api from './api'
import Editor from '@/components/Editor.vue'
const model = ref({
user: '',
policy: '',
integral: '',
})
onMounted(() => {
get_agreement()
})
const get_agreement = async () => {
const res = await api.getAgreement()
model.value = res.data.data
}
const save = async () => {
const res = await api.updateAgreement(model.value)
$message.success(res.msg)
}
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,7 @@
<template>
<CommonPage show-footer :title="$route.title"></CommonPage>
</template>
<script setup></script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,7 @@
import { request } from '@/utils'
export default {
getData: (data) => request.post('/paytype/list', data),
add: (data) => request.post('/paytype/add', data),
edit: (data) => request.post('/paytype/edit', data),
}

View File

@@ -0,0 +1,283 @@
<template>
<CommonPage show-footer :title="$route.title">
<n-button v-perms="['/admin/paytype/add']" type="primary" @click="openModal(1)">
添加支付方式
</n-button>
<n-data-table
class="mt-5"
:loading="loading"
:columns="columns"
:data="data"
:pagination="pagination"
:bordered="false"
remote
/>
<n-modal v-model:show="showModal">
<n-card
style="width: 600px"
title="添加/编辑支付方式"
:bordered="false"
size="huge"
role="dialog"
aria-modal="true"
>
<n-form ref="formRef" :model="model" :rules="rules" label-placement="left">
<n-grid :cols="24" :x-gap="24">
<n-form-item-gi :span="16" label="支付图标:" path="icon">
<Upload v-model:list="model.icon" />
</n-form-item-gi>
<n-form-item-gi :span="16" label="支付名称:" path="name">
<n-input v-model:value="model.name" placeholder="请输入支付名称" />
</n-form-item-gi>
<n-form-item-gi :span="16" label="启用状态:">
<n-switch v-model:value="model.status" :checked-value="1" :unchecked-value="2" />
</n-form-item-gi>
<n-form-item-gi :span="16" label="余额状态:" path="state">
<n-select
v-model:value="model.state"
:options="[
{
label: '无限',
value: 1,
},
{
label: '有限',
value: 2,
},
]"
/>
</n-form-item-gi>
<n-form-item-gi v-if="model.state === 2" :span="16" label="商家余额:" path="amount">
<n-input-number v-model:value="model.amount" placeholder="请输入商家余额" />
</n-form-item-gi>
<n-form-item-gi :span="18">
<div m-auto>
<n-button type="primary" @click="handleValidateClick">保存</n-button>
<n-button ml-10 @click="clear">取消</n-button>
</div>
</n-form-item-gi>
</n-grid>
</n-form>
</n-card>
</n-modal>
</CommonPage>
</template>
<script setup>
import { h, withDirectives, resolveDirective } from 'vue'
import api from './api'
import { NTag, NButton } from 'naive-ui'
import Upload from '@/components/Upload.vue'
const vPerms = resolveDirective('perms')
onMounted(() => {
getList()
})
const loading = ref(false)
const columns = ref([
{
title: 'icon',
slot: 'icon',
align: 'center',
render: (row) => {
return h('img', {
src: row.icon[0].url,
width: 50,
height: 50,
})
},
},
{
title: '支付名称',
key: 'name',
align: 'center',
},
{
title: '状态',
slot: 'status',
align: 'center',
render: (row) => {
return h(
NTag,
{
type: row.status === 1 ? 'success' : 'warning',
},
{
default: () => (row.status === 1 ? '启用' : '禁用'),
}
)
},
},
{
title: '商家余额',
slot: 'amount',
align: 'center',
render: (row) => {
return h(
'span',
{},
{
default: () => (row.state === 1 ? '无限' : row.amount),
}
)
},
},
{
title: '余额状态',
slot: 'state',
align: 'center',
render: (row) => {
return h(
'span',
{},
{
default: () => (row.state === 1 ? '无限' : '有限'),
}
)
},
},
{
title: '操作',
slot: 'action',
align: 'center',
render: (row) => {
return [
withDirectives(
h(
NButton,
{
text: true,
onClick: () => {
openModal(2, row)
},
},
{
default: () => '编辑',
}
),
[[vPerms, ['/admin/paytype/edit']]]
),
]
},
},
])
const data = ref([])
const pagination = ref({
page: 1,
pageSize: 10,
itemCount: 0,
onChange: (page) => {
pagination.value.page = page
getList()
},
})
const getList = async () => {
loading.value = true
const res = await api.getData({
page: pagination.value.page,
pageSize: pagination.value.pageSize,
})
data.value =
res.data.data.map((item) => ({
...item,
icon: [
{
id: item.ID,
url: item.icon,
status: 'finished',
},
],
})) || []
// console.log(res)
pagination.value.itemCount = res.data.total
loading.value = false
}
const showModal = ref(false)
const model = ref({
icon: [],
name: '',
status: 1,
state: 1,
amount: 0,
})
const formRef = ref(null)
const rules = ref({
icon: {
required: true,
type: 'array',
message: '请上传图片',
},
name: {
required: true,
message: '请输入支付名称',
},
state: {
required: true,
type: 'number',
message: '请选择余额状态',
},
amount: {
required: true,
type: 'number',
message: '请输入商家余额',
},
})
const nowType = ref(1)
const openModal = (type, row = {}) => {
nowType.value = type
if (type === 2) {
model.value = {
...row,
}
}
showModal.value = true
}
const handleValidateClick = async (e) => {
e.preventDefault()
formRef.value?.validate(async (errors) => {
if (!errors) {
try {
const data = {
...model.value,
icon: model.value.icon[0].url,
}
if (nowType.value === 1) {
await api.add(data)
} else {
await api.edit(data)
}
clear()
getList()
} catch (error) {
$message.error(error.msg)
}
}
})
}
const clear = async () => {
model.value = {
icon: [],
name: '',
status: 1,
state: 1,
amount: 0,
}
showModal.value = false
}
</script>
<style lang="scss" scoped></style>

View File

@@ -3,21 +3,21 @@
<n-grid class="mb-10" x-gap="12"> <n-grid class="mb-10" x-gap="12">
<n-gi :span="24"> <n-gi :span="24">
<div flex> <div flex>
<n-card w-250> <!-- <n-card w-250>
<n-statistic label="用户总赢积分" tabular-nums> <n-statistic label="用户总赢积分" tabular-nums>
<n-number-animation ref="numberAnimationInstRef" :from="0" :to="cardData.win" /> <n-number-animation ref="numberAnimationInstRef" :from="0" :to="cardData.win" />
</n-statistic> </n-statistic>
</n-card> </n-card> -->
<n-card ml-10 w-250> <n-card w-250>
<n-statistic label="用户积分(留存)" tabular-nums> <n-statistic label="用户积分(留存)" tabular-nums>
<n-number-animation ref="numberAnimationInstRef" :from="0" :to="cardData.integral" /> <n-number-animation ref="numberAnimationInstRef" :from="0" :to="cardData.integral" />
</n-statistic> </n-statistic>
</n-card> </n-card>
<n-card ml-10 w-250> <!-- <n-card ml-10 w-250>
<n-statistic label="总豆子" tabular-nums> <n-statistic label="总豆子" tabular-nums>
<n-number-animation ref="numberAnimationInstRef" :from="0" :to="cardData.pulse" /> <n-number-animation ref="numberAnimationInstRef" :from="0" :to="cardData.pulse" />
</n-statistic> </n-statistic>
</n-card> </n-card> -->
<n-card ml-10 w-250> <n-card ml-10 w-250>
<n-statistic label="今日新增用户" tabular-nums> <n-statistic label="今日新增用户" tabular-nums>
<n-number-animation <n-number-animation
@@ -27,7 +27,7 @@
/> />
</n-statistic> </n-statistic>
</n-card> </n-card>
<n-card ml-10 w-250> <!-- <n-card ml-10 w-250>
<n-statistic label="用户总流水(元)" tabular-nums> <n-statistic label="用户总流水(元)" tabular-nums>
<n-number-animation <n-number-animation
ref="numberAnimationInstRef" ref="numberAnimationInstRef"
@@ -36,8 +36,8 @@
:precision="2" :precision="2"
/> />
</n-statistic> </n-statistic>
</n-card> </n-card> -->
<n-card ml-10 w-250> <!-- <n-card ml-10 w-250>
<n-statistic label="总佣金(积分)" tabular-nums> <n-statistic label="总佣金(积分)" tabular-nums>
<n-number-animation <n-number-animation
ref="numberAnimationInstRef" ref="numberAnimationInstRef"
@@ -46,7 +46,7 @@
:precision="2" :precision="2"
/> />
</n-statistic> </n-statistic>
</n-card> </n-card> -->
<n-card ml-10 w-250> <n-card ml-10 w-250>
<n-statistic label="平台总用户" tabular-nums> <n-statistic label="平台总用户" tabular-nums>
<n-number-animation <n-number-animation
@@ -58,7 +58,7 @@
</n-card> </n-card>
</div> </div>
</n-gi> </n-gi>
<n-gi span="12" mt-10 flex items-center> <n-gi :span="12" mt-10 flex items-center>
<span w-100>筛选条件:</span> <span w-100>筛选条件:</span>
<n-input-group> <n-input-group>
<n-select <n-select
@@ -70,7 +70,7 @@
<n-input v-model:value="queryParams.word" :style="{ width: '30%' }" /> <n-input v-model:value="queryParams.word" :style="{ width: '30%' }" />
</n-input-group> </n-input-group>
</n-gi> </n-gi>
<n-gi :span="24" mt-10> <!-- <n-gi :span="24" mt-10>
<div> <div>
<span>筛选状态</span> <span>筛选状态</span>
<n-radio-group v-model:value="queryParams.status"> <n-radio-group v-model:value="queryParams.status">
@@ -95,8 +95,8 @@
/> />
</n-radio-group> </n-radio-group>
</div> </div>
</n-gi> </n-gi> -->
<n-gi :span="10"> <n-gi :span="24">
<div mt-10 flex items-center> <div mt-10 flex items-center>
<span w-100>时间筛选</span> <span w-100>时间筛选</span>
<n-date-picker <n-date-picker
@@ -279,40 +279,6 @@ const cardData = ref({
win: 0, win: 0,
}) })
const songs = ref([
{
value: 1,
label: '未使用',
},
{
value: 3,
label: '用户赢',
},
{
value: 4,
label: '用户输',
},
{
value: 5,
label: '已过期',
},
])
const songs1 = ref([
{
value: 5,
label: '注册赠送',
},
{
value: 6,
label: '签到赠送',
},
{
value: 7,
label: '主动赠送',
},
])
const selectOptions = [ const selectOptions = [
{ {
label: '用户昵称', label: '用户昵称',
@@ -364,20 +330,20 @@ const columns = ref([
sorter: true, sorter: true,
sortOrder: false, sortOrder: false,
}, },
{ // {
title: '赢积分', // title: '赢积分',
align: 'center', // align: 'center',
key: 'win', // key: 'win',
sorter: true, // sorter: true,
sortOrder: false, // sortOrder: false,
}, // },
{ // {
title: '用户豆子', // title: '用户豆子',
align: 'center', // align: 'center',
key: 'pulse', // key: 'pulse',
sorter: true, // sorter: true,
sortOrder: false, // sortOrder: false,
}, // },
{ {
title: '用户状态', title: '用户状态',
align: 'center', align: 'center',

View File

@@ -12,7 +12,7 @@ export default {
component: () => import('./index.vue'), component: () => import('./index.vue'),
meta: { meta: {
title: '工作台', title: '工作台',
icon: 'mdi:home', icon: 'mdi:index',
order: 0, order: 0,
}, },
}, },

View File

@@ -13,6 +13,13 @@ export default defineConfig(({ command, mode }) => {
const viteEnv = convertEnv(env) const viteEnv = convertEnv(env)
const { VITE_PORT, VITE_PUBLIC_PATH, VITE_USE_PROXY, VITE_BASE_API, VITE_SENTRY } = viteEnv const { VITE_PORT, VITE_PUBLIC_PATH, VITE_USE_PROXY, VITE_BASE_API, VITE_SENTRY } = viteEnv
// 调试代理配置
console.log('=== Vite代理配置调试 ===')
console.log('VITE_USE_PROXY:', VITE_USE_PROXY)
console.log('VITE_BASE_API:', VITE_BASE_API)
console.log('PROXY_CONFIG:', PROXY_CONFIG)
console.log('所有环境变量:', viteEnv)
return { return {
base: VITE_PUBLIC_PATH || '/', base: VITE_PUBLIC_PATH || '/',
resolve: { resolve: {
@@ -26,12 +33,10 @@ export default defineConfig(({ command, mode }) => {
host: '0.0.0.0', host: '0.0.0.0',
port: VITE_PORT, port: VITE_PORT,
open: false, open: false,
proxy: VITE_USE_PROXY proxy: {
? { '/api1': PROXY_CONFIG['/api1'],
[VITE_BASE_API]: PROXY_CONFIG[VITE_BASE_API], '/api': PROXY_CONFIG['/api'],
'/api/v2': PROXY_CONFIG['/api/v2'], },
}
: undefined,
}, },
build: { build: {
target: 'es2015', target: 'es2015',