Compare commits
58 Commits
a0f1d29597
...
test
| Author | SHA1 | Date | |
|---|---|---|---|
| 58985b9a6b | |||
| ee659da4c1 | |||
| b4dfb62193 | |||
| af76eda747 | |||
| 55d59131a6 | |||
| e4df9adce8 | |||
| a6e570d936 | |||
| 8a885d9c5e | |||
| 614923b395 | |||
| 36612722e1 | |||
| ec3d7e20e5 | |||
| 5041b83386 | |||
| 13d6bdd5f0 | |||
| fe15b87b42 | |||
| 59de06f8e8 | |||
| 57ff4e3cb1 | |||
| dc70eb3000 | |||
| 25c836c008 | |||
| 6756f80cd6 | |||
| 0fe04dd0c3 | |||
| c97f3c655f | |||
| fd715b51a8 | |||
| c0d46d3b7b | |||
| 829f86908f | |||
| b87ae167bb | |||
| 736675608a | |||
| 1306e2acf6 | |||
| 3782abcd2e | |||
| 11386f9864 | |||
| 3f11741c33 | |||
| 592fb2ede7 | |||
| 2f11eae9d2 | |||
| f868be28a3 | |||
| 10ed1bb6d6 | |||
| 346930fac9 | |||
| df796d6a1d | |||
| 0744da45f6 | |||
| f899201e4a | |||
| 981fff22de | |||
| 8f8e024cfe | |||
| db9d37d370 | |||
| 1f963a84f7 | |||
| aba5d6d6c6 | |||
| 79f34be500 | |||
| e139fed2a6 | |||
| 760c843060 | |||
| 71f72ff03d | |||
| af20de8776 | |||
| dd49ee9cd6 | |||
| 3a246511e1 | |||
| 7d7ac808f8 | |||
| a53552943a | |||
| eb1f6ff400 | |||
| 2faf99c2ff | |||
| c8974937ff | |||
| d619dcb268 | |||
| 011284c5dc | |||
| 893649afdd |
1
.dockerignore
Normal file
1
.dockerignore
Normal file
@@ -0,0 +1 @@
|
||||
node_modules
|
||||
@@ -8,9 +8,8 @@ VITE_USE_MOCK=false
|
||||
VITE_USE_PROXY=true
|
||||
|
||||
# base api
|
||||
VITE_BASE_API='/store'
|
||||
VITE_BASE_API='/api'
|
||||
VITE_BASE_API_1='/api1'
|
||||
|
||||
VITE_ADMIN_API='/admin'
|
||||
|
||||
# 是否启用监控
|
||||
VITE_SENTRY=false
|
||||
VITE_ADMIN_API_1='/admin1'
|
||||
|
||||
@@ -6,14 +6,13 @@ VITE_USE_MOCK=false
|
||||
|
||||
# base api
|
||||
VITE_BASE_API='//www.wanzhuanyongcheng.cn/store'
|
||||
VITE_BASE_API_1='//api.gxwzwh.com/store'
|
||||
|
||||
VITE_ADMIN_API='//www.wanzhuanyongcheng.cn'
|
||||
VITE_ADMIN_API_1='//api.gxwzwh.com'
|
||||
|
||||
# 是否启用压缩
|
||||
VITE_USE_COMPRESS=true
|
||||
|
||||
# 压缩类型
|
||||
VITE_COMPRESS_TYPE=gzip
|
||||
|
||||
# 是否启用监控
|
||||
VITE_SENTRY=true
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# DO NOT commit this file to your repository!
|
||||
# The SENTRY_AUTH_TOKEN variable is picked up by the Sentry Build Plugin.
|
||||
# It's used for authentication when uploading source maps.
|
||||
# You can also set this env variable in your own `.env` files and remove this file.
|
||||
SENTRY_AUTH_TOKEN="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"
|
||||
@@ -13,6 +13,3 @@ VITE_USE_COMPRESS=true
|
||||
|
||||
# 压缩类型
|
||||
VITE_COMPRESS_TYPE=gzip
|
||||
|
||||
# 是否启用监控
|
||||
VITE_SENTRY=false
|
||||
|
||||
162
.gitea/workflows/ci.yaml
Normal file
162
.gitea/workflows/ci.yaml
Normal 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-mer-dev
|
||||
mkdir -p jdt-mer-dev
|
||||
tar -xzvf dist.tar -C /www/builder/jdt-mer-dev
|
||||
rm -rf dist.tar
|
||||
cd jdt-mer-dev
|
||||
docker build -t jdt-mer-dev .
|
||||
docker stop jdt-mer-dev || true
|
||||
docker rm jdt-mer-dev || true
|
||||
docker run -d -p 8083:80 --restart=always --name jdt-mer-dev jdt-mer-dev
|
||||
cd ..
|
||||
rm -rf jdt-mer-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-mer-prod
|
||||
mkdir -p jdt-mer-prod
|
||||
tar -xzvf dist.tar -C /www/builder/jdt-mer-prod
|
||||
rm -rf dist.tar
|
||||
cd jdt-mer-prod
|
||||
docker build -t jdt-mer-prod .
|
||||
docker stop jdt-mer-prod || true
|
||||
docker rm jdt-mer-prod || true
|
||||
docker run -d -p 8083:80 --restart=always --name jdt-mer-prod jdt-mer-prod
|
||||
cd ..
|
||||
rm -rf jdt-mer-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}"
|
||||
5
.idea/.gitignore
generated
vendored
5
.idea/.gitignore
generated
vendored
@@ -1,5 +0,0 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
65
.idea/codeStyles/Project.xml
generated
65
.idea/codeStyles/Project.xml
generated
@@ -1,65 +0,0 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<option name="LINE_SEPARATOR" value=" " />
|
||||
<HTMLCodeStyleSettings>
|
||||
<option name="HTML_SPACE_INSIDE_EMPTY_TAG" value="true" />
|
||||
<option name="HTML_QUOTE_STYLE" value="Single" />
|
||||
<option name="HTML_ENFORCE_QUOTES" value="true" />
|
||||
</HTMLCodeStyleSettings>
|
||||
<JSCodeStyleSettings version="0">
|
||||
<option name="USE_SEMICOLON_AFTER_STATEMENT" value="false" />
|
||||
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
||||
<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="ENFORCE_TRAILING_COMMA" value="Remove" />
|
||||
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
|
||||
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
||||
</JSCodeStyleSettings>
|
||||
<TypeScriptCodeStyleSettings version="0">
|
||||
<option name="USE_SEMICOLON_AFTER_STATEMENT" value="false" />
|
||||
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
||||
<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="ENFORCE_TRAILING_COMMA" value="Remove" />
|
||||
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
|
||||
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
||||
</TypeScriptCodeStyleSettings>
|
||||
<VueCodeStyleSettings>
|
||||
<option name="INTERPOLATION_NEW_LINE_AFTER_START_DELIMITER" value="false" />
|
||||
<option name="INTERPOLATION_NEW_LINE_BEFORE_END_DELIMITER" value="false" />
|
||||
</VueCodeStyleSettings>
|
||||
<codeStyleSettings language="HTML">
|
||||
<option name="SOFT_MARGINS" value="100" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JavaScript">
|
||||
<option name="SOFT_MARGINS" value="100" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="TypeScript">
|
||||
<option name="SOFT_MARGINS" value="100" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="Vue">
|
||||
<option name="SOFT_MARGINS" value="100" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
5
.idea/codeStyles/codeStyleConfig.xml
generated
5
.idea/codeStyles/codeStyleConfig.xml
generated
@@ -1,5 +0,0 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
||||
6
.idea/inspectionProfiles/Project_Default.xml
generated
6
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -1,6 +0,0 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
</profile>
|
||||
</component>
|
||||
6
.idea/jsLibraryMappings.xml
generated
6
.idea/jsLibraryMappings.xml
generated
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaScriptLibraryMappings">
|
||||
<includedPredefinedLibrary name="Node.js Core" />
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/jsLinters/eslint.xml
generated
6
.idea/jsLinters/eslint.xml
generated
@@ -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>
|
||||
12
.idea/mer.iml
generated
12
.idea/mer.iml
generated
@@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
8
.idea/modules.xml
generated
8
.idea/modules.xml
generated
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/mer.iml" filepath="$PROJECT_DIR$/.idea/mer.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/prettier.xml
generated
6
.idea/prettier.xml
generated
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="PrettierConfiguration">
|
||||
<option name="myConfigurationMode" value="AUTOMATIC" />
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
6
.idea/vcs.xml
generated
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
21
LICENSE
21
LICENSE
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ronnie Zhang
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
BIN
build/.DS_Store
vendored
Normal file
BIN
build/.DS_Store
vendored
Normal file
Binary file not shown.
@@ -2,37 +2,36 @@ export const OUTPUT_DIR = 'dist'
|
||||
|
||||
export const PROXY_CONFIG = {
|
||||
/**
|
||||
* @desc 替换匹配值
|
||||
* @请求路径 http://localhost:3100/api/user
|
||||
* @转发路径 http://localhost:8080/user
|
||||
* @desc 主接口代理
|
||||
* @请求路径 http://localhost:3100/api/login
|
||||
* @转发路径 http://localhost:3000/api/login
|
||||
*/
|
||||
'/store': {
|
||||
'/api': {
|
||||
target: 'https://test.wanzhuanyongcheng.cn',
|
||||
changeOrigin: true,
|
||||
// rewrite: (path) => path.replace(new RegExp('^/api'), ''),
|
||||
rewrite: (path) => path.replace(/^\/api/, '/store'),
|
||||
},
|
||||
/**
|
||||
* @desc 备用接口代理
|
||||
* @请求路径 http://localhost:3100/api1/login
|
||||
* @转发路径 http://localhost:3001/api/login
|
||||
*/
|
||||
'/api1': {
|
||||
target: 'https://api.gxwzwh.com',
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/api1/, '/store'),
|
||||
},
|
||||
/**
|
||||
* @desc null
|
||||
*/
|
||||
'/admin': {
|
||||
target: 'https://test.wanzhuanyongcheng.cn',
|
||||
changeOrigin: true,
|
||||
// rewrite: (path) => path.replace(new RegExp('^/admin'), ''),
|
||||
rewrite: (path) => path.replace(/^\/admin/, ''),
|
||||
},
|
||||
'/admin1': {
|
||||
target: 'https://api.gxwzwh.com',
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/admin1/, ''),
|
||||
},
|
||||
/**
|
||||
* @desc 不替换匹配值
|
||||
* @请求路径 http://localhost:3100/api/v2/user
|
||||
* @转发路径 http://localhost:8080/api/v2/user
|
||||
*/
|
||||
// '/api/v2': {
|
||||
// target: 'http://localhost:8080',
|
||||
// changeOrigin: true,
|
||||
// },
|
||||
/**
|
||||
* @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'), ''),
|
||||
// },
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import viteCompression from 'vite-plugin-compression'
|
||||
import { configHtmlPlugin } from './html'
|
||||
import { configMockPlugin } from './mock'
|
||||
import unplugin from './unplugin'
|
||||
import { sentryVitePlugin } from '@sentry/vite-plugin'
|
||||
|
||||
export function createVitePlugins(viteEnv, isBuild) {
|
||||
const plugins = [vue(), ...unplugin, configHtmlPlugin(viteEnv, isBuild), Unocss()]
|
||||
@@ -36,21 +35,5 @@ export function createVitePlugins(viteEnv, isBuild) {
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
if (viteEnv.VITE_SENTRY) {
|
||||
plugins.push(
|
||||
sentryVitePlugin({
|
||||
authToken: process.env.SENTRY_AUTH_TOKEN,
|
||||
org: 'sentry',
|
||||
project: 'jdt-mer',
|
||||
url: 'https://w.huakk.top',
|
||||
sourcemaps: {
|
||||
ignore: ['node_modules'],
|
||||
filesToDeleteAfterUpload: ['dist/**/*.js.map'],
|
||||
},
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
return plugins
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import { NaiveUiResolver } from 'unplugin-vue-components/resolvers'
|
||||
import { FileSystemIconLoader } from 'unplugin-icons/loaders'
|
||||
import IconsResolver from 'unplugin-icons/resolver'
|
||||
import mkcert from 'vite-plugin-mkcert'
|
||||
import { sentryVitePlugin } from '@sentry/vite-plugin'
|
||||
|
||||
/**
|
||||
* * unplugin-icons插件,自动引入iconify图标
|
||||
@@ -46,11 +45,4 @@ export default [
|
||||
customDomId: '__CUSTOM_SVG_ICON__',
|
||||
}),
|
||||
mkcert(),
|
||||
sentryVitePlugin({
|
||||
authToken:
|
||||
'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',
|
||||
org: 'sentry',
|
||||
project: 'jdt-mer',
|
||||
url: 'https://w.huakk.top',
|
||||
}),
|
||||
]
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
import { resolveToken } from '../utils'
|
||||
|
||||
const token = {
|
||||
admin: 'admin',
|
||||
editor: 'editor',
|
||||
}
|
||||
|
||||
export default [
|
||||
{
|
||||
url: '/api/auth/login',
|
||||
method: 'post',
|
||||
response: ({ body }) => {
|
||||
if (['admin', 'editor'].includes(body?.name)) {
|
||||
return {
|
||||
code: 0,
|
||||
data: {
|
||||
token: token[body.name],
|
||||
},
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
code: -1,
|
||||
message: '没有此用户',
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/api/auth/refreshToken',
|
||||
method: 'post',
|
||||
response: ({ headers }) => {
|
||||
return {
|
||||
code: 0,
|
||||
data: {
|
||||
token: resolveToken(headers?.authorization),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
@@ -1,5 +0,0 @@
|
||||
import auth from './auth'
|
||||
import user from './user'
|
||||
import post from './post'
|
||||
|
||||
export default [...auth, ...user, ...post]
|
||||
138
mock/api/post.js
138
mock/api/post.js
@@ -1,138 +0,0 @@
|
||||
const posts = [
|
||||
{
|
||||
title: '使用纯css优雅配置移动端rem布局',
|
||||
author: '大脸怪',
|
||||
category: 'Css',
|
||||
description: '通常配置rem布局会使用js进行处理,比如750的设计稿会这样...',
|
||||
content: '通常配置rem布局会使用js进行处理,比如750的设计稿会这样',
|
||||
isRecommend: true,
|
||||
isPublish: true,
|
||||
createDate: '2021-11-04T04:03:36.000Z',
|
||||
updateDate: '2021-11-04T04:03:36.000Z',
|
||||
},
|
||||
{
|
||||
title: 'Vue2&Vue3项目风格指南',
|
||||
author: 'Ronnie',
|
||||
category: 'Vue',
|
||||
description: '总结的Vue2和Vue3的项目风格',
|
||||
content: '### 1. 命名风格\n\n> 文件夹如果是由多个单词组成,应该始终是横线连接 ',
|
||||
isRecommend: true,
|
||||
isPublish: true,
|
||||
createDate: '2021-10-25T08:57:47.000Z',
|
||||
updateDate: '2022-02-28T04:02:39.000Z',
|
||||
},
|
||||
{
|
||||
title: '如何优雅的给图片添加水印',
|
||||
author: '大脸怪',
|
||||
category: 'JavaScript',
|
||||
description: '优雅的给图片添加水印',
|
||||
content: '我之前写过一篇文章记录了一次上传图片的优化史',
|
||||
isRecommend: true,
|
||||
isPublish: true,
|
||||
createDate: '2021-06-24T18:46:19.000Z',
|
||||
updateDate: '2021-09-23T07:51:22.000Z',
|
||||
},
|
||||
|
||||
{
|
||||
title: '前端缓存的理解',
|
||||
author: '大脸怪',
|
||||
category: 'Http',
|
||||
description: '谈谈前端缓存的理解',
|
||||
content:
|
||||
'> 背景\n\n公司有个vue-cli3移动端web项目发版更新后发现部分用户手机在钉钉内置浏览器打开出现了缓存',
|
||||
isRecommend: true,
|
||||
isPublish: true,
|
||||
createDate: '2021-06-10T18:51:19.000Z',
|
||||
updateDate: '2021-09-17T09:33:24.000Z',
|
||||
},
|
||||
{
|
||||
title: 'Promise的五个静态方法',
|
||||
author: '大脸怪',
|
||||
category: 'JavaScript',
|
||||
description: '简单介绍下在 Promise 类中,有5 种静态方法及它们的使用场景',
|
||||
content:
|
||||
'## 1. Promise.all\n\n并行执行多个 promise,并等待所有 promise 都准备就绪。再对它们进行处理。',
|
||||
isRecommend: true,
|
||||
isPublish: true,
|
||||
createDate: '2021-02-22T22:37:06.000Z',
|
||||
updateDate: '2021-09-17T09:33:24.000Z',
|
||||
},
|
||||
]
|
||||
|
||||
export default [
|
||||
{
|
||||
url: '/api/posts',
|
||||
method: 'get',
|
||||
response: (data = {}) => {
|
||||
const { title, pageNo, pageSize } = data.query
|
||||
let pageData = []
|
||||
let total = 60
|
||||
const filterData = posts.filter(
|
||||
(item) => item.title.includes(title) || (!title && title !== 0)
|
||||
)
|
||||
if (filterData.length) {
|
||||
if (pageSize) {
|
||||
while (pageData.length < pageSize) {
|
||||
pageData.push(filterData[Math.round(Math.random() * (filterData.length - 1))])
|
||||
}
|
||||
} else {
|
||||
pageData = filterData
|
||||
}
|
||||
pageData = pageData.map((item, index) => ({
|
||||
id: pageSize * (pageNo - 1) + index + 1,
|
||||
...item,
|
||||
}))
|
||||
} else {
|
||||
total = 0
|
||||
}
|
||||
return {
|
||||
code: 0,
|
||||
message: 'ok',
|
||||
data: {
|
||||
pageData,
|
||||
total,
|
||||
pageNo,
|
||||
pageSize,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/api/post',
|
||||
method: 'post',
|
||||
response: ({ body }) => {
|
||||
return {
|
||||
code: 0,
|
||||
message: 'ok',
|
||||
data: body,
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/api/post/:id',
|
||||
method: 'put',
|
||||
response: ({ query, body }) => {
|
||||
return {
|
||||
code: 0,
|
||||
message: 'ok',
|
||||
data: {
|
||||
id: query.id,
|
||||
body,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
url: '/api/post/:id',
|
||||
method: 'delete',
|
||||
response: ({ query }) => {
|
||||
return {
|
||||
code: 0,
|
||||
message: 'ok',
|
||||
data: {
|
||||
id: query.id,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
@@ -1,39 +0,0 @@
|
||||
import { resolveToken } from '../utils'
|
||||
|
||||
const users = {
|
||||
admin: {
|
||||
id: 1,
|
||||
name: '大脸怪(admin)',
|
||||
avatar: 'https://assets.qszone.com/images/avatar.jpg',
|
||||
email: 'Ronnie@123.com',
|
||||
role: ['admin'],
|
||||
},
|
||||
editor: {
|
||||
id: 2,
|
||||
name: '大脸怪(editor)',
|
||||
avatar: 'https://assets.qszone.com/images/avatar.jpg',
|
||||
email: 'Ronnie@123.com',
|
||||
role: ['editor'],
|
||||
},
|
||||
guest: {
|
||||
id: 3,
|
||||
name: '访客(guest)',
|
||||
avatar: 'https://assets.qszone.com/images/avatar.jpg',
|
||||
role: [],
|
||||
},
|
||||
}
|
||||
export default [
|
||||
{
|
||||
url: '/api/user',
|
||||
method: 'get',
|
||||
response: ({ headers }) => {
|
||||
const token = resolveToken(headers?.authorization)
|
||||
return {
|
||||
code: 0,
|
||||
data: {
|
||||
...(users[token] || users.guest),
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
@@ -1,6 +0,0 @@
|
||||
import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer'
|
||||
import api from './api'
|
||||
|
||||
export function setupProdMockServer() {
|
||||
createProdMockServer(api)
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
export function resolveToken(authorization) {
|
||||
/**
|
||||
* * jwt token
|
||||
* * Bearer + token
|
||||
* ! 认证方案: Bearer
|
||||
*/
|
||||
const reqTokenSplit = authorization.split(' ')
|
||||
if (reqTokenSplit.length === 2) {
|
||||
return reqTokenSplit[1]
|
||||
}
|
||||
return ''
|
||||
}
|
||||
53
package.json
53
package.json
@@ -2,7 +2,7 @@
|
||||
"name": "vue-naive-admin",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"build": "vite build",
|
||||
"build:prod": "vite build",
|
||||
"build:test": "vite build --mode test",
|
||||
"cz": "cz",
|
||||
"dev": "vite",
|
||||
@@ -32,54 +32,53 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@sentry/vite-plugin": "^2.10.2",
|
||||
"@sentry/vue": "^7.84.0",
|
||||
"@unocss/eslint-config": "^0.55.7",
|
||||
"@vueuse/core": "^10.6.1",
|
||||
"@vueuse/core": "^10.11.1",
|
||||
"@wangeditor/editor": "^5.1.23",
|
||||
"@wangeditor/editor-for-vue": "5.1.12",
|
||||
"axios": "^1.6.2",
|
||||
"dayjs": "^1.11.10",
|
||||
"echarts": "^5.5.0",
|
||||
"axios": "^1.13.1",
|
||||
"dayjs": "^1.11.19",
|
||||
"echarts": "^5.6.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"md-editor-v3": "^4.9.0",
|
||||
"md-editor-v3": "^4.21.3",
|
||||
"mockjs": "^1.1.0",
|
||||
"pinia": "^2.1.7",
|
||||
"vite": "^4.5.0",
|
||||
"pinia": "^2.3.1",
|
||||
"vite": "^4.5.14",
|
||||
"vue": "3.3.4",
|
||||
"vue-echarts": "^6.6.9",
|
||||
"vue-router": "^4.2.5",
|
||||
"vue-echarts": "^6.7.3",
|
||||
"vue-router": "^4.6.3",
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^17.8.1",
|
||||
"@commitlint/config-conventional": "^17.8.1",
|
||||
"@iconify/json": "^2.2.150",
|
||||
"@iconify/vue": "^4.1.1",
|
||||
"@iconify/json": "^2.2.402",
|
||||
"@iconify/vue": "^4.3.0",
|
||||
"@unocss/preset-rem-to-px": "^0.55.7",
|
||||
"@vitejs/plugin-vue": "^4.5.1",
|
||||
"@vue/compiler-sfc": "^3.3.9",
|
||||
"@vitejs/plugin-vue": "^4.6.2",
|
||||
"@vue/compiler-sfc": "^3.5.22",
|
||||
"@zclzone/eslint-config": "^0.0.4",
|
||||
"chalk": "^5.3.0",
|
||||
"commitizen": "^4.3.0",
|
||||
"chalk": "^5.6.2",
|
||||
"commitizen": "^4.3.1",
|
||||
"cz-conventional-changelog": "^3.3.0",
|
||||
"cz-customizable": "^7.0.0",
|
||||
"dotenv": "^16.3.1",
|
||||
"cz-customizable": "^7.5.1",
|
||||
"dotenv": "^16.6.1",
|
||||
"esno": "^0.17.0",
|
||||
"fs-extra": "^11.2.0",
|
||||
"fs-extra": "^11.3.2",
|
||||
"husky": "^8.0.3",
|
||||
"lint-staged": "^14.0.1",
|
||||
"naive-ui": "^2.35.0",
|
||||
"rollup-plugin-visualizer": "^5.9.3",
|
||||
"sass": "^1.69.5",
|
||||
"naive-ui": "^2.43.1",
|
||||
"rollup-plugin-visualizer": "^5.14.0",
|
||||
"sass": "^1.93.2",
|
||||
"unocss": "0.55.3",
|
||||
"unplugin-auto-import": "^0.16.7",
|
||||
"unplugin-icons": "^0.16.6",
|
||||
"unplugin-vue-components": "^0.25.2",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite-plugin-html": "^3.2.0",
|
||||
"vite-plugin-mkcert": "^1.17.1",
|
||||
"vite-plugin-html": "^3.2.2",
|
||||
"vite-plugin-mkcert": "^1.17.9",
|
||||
"vite-plugin-mock": "2.9.6",
|
||||
"vite-plugin-svg-icons": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"packageManager": "pnpm@9.1.4+sha512.9df9cf27c91715646c7d675d1c9c8e41f6fce88246f1318c1aa6a1ed1aeb3c4f032fcdf4ba63cc69c4fe6d634279176b5358727d8f2cc1e65b65f43ce2f8bfb0"
|
||||
}
|
||||
|
||||
11727
pnpm-lock.yaml
generated
11727
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
BIN
src/.DS_Store
vendored
Normal file
BIN
src/.DS_Store
vendored
Normal file
Binary file not shown.
527
src/components/TiandituPicker.vue
Normal file
527
src/components/TiandituPicker.vue
Normal file
@@ -0,0 +1,527 @@
|
||||
<template>
|
||||
<div class="tianditu-picker">
|
||||
<!-- 顶部搜索框 -->
|
||||
<div class="search-header">
|
||||
<n-input-group>
|
||||
<n-auto-complete
|
||||
v-model:value="keyword"
|
||||
placeholder="搜索地点"
|
||||
clearable
|
||||
@select="selectFromSearch"
|
||||
@input:value="handleInput"
|
||||
/>
|
||||
<n-button type="primary" @click="search">搜索位置</n-button>
|
||||
</n-input-group>
|
||||
</div>
|
||||
|
||||
<!-- 地图区域 -->
|
||||
<div id="mapDiv" class="map-container"></div>
|
||||
|
||||
<!-- 底部位置信息和搜索结果 -->
|
||||
<div class="location-footer">
|
||||
<!-- 统一的结果列表 -->
|
||||
<div class="results-section">
|
||||
<div class="results-header">位置选择</div>
|
||||
<div class="results-list">
|
||||
<!-- 我的位置项 -->
|
||||
<div
|
||||
class="result-item current-location-item"
|
||||
:class="{ selected: !showSearchResults }"
|
||||
@click="relocateToCurrentPosition"
|
||||
>
|
||||
<div class="location-icon">📍</div>
|
||||
<div class="result-content">
|
||||
<div class="result-name">我的位置</div>
|
||||
<div class="result-address">
|
||||
{{ currentLocation?.address || '请点击获取当前位置' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 搜索结果项 -->
|
||||
<div
|
||||
v-for="(result, index) in searchResults"
|
||||
:key="`search-${index}`"
|
||||
class="result-item search-result-item"
|
||||
@click="selectSearchResult(result)"
|
||||
>
|
||||
<div class="location-icon">📍</div>
|
||||
<div class="result-content">
|
||||
<div class="result-name">{{ result.name }}</div>
|
||||
<div class="result-address">{{ result.address }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="action-buttons">
|
||||
<n-space justify="space-between">
|
||||
<n-button type="tertiary" @click="getCurrentLocation">定位当前位置</n-button>
|
||||
<n-space>
|
||||
<n-button @click="$emit('cancel')">取消</n-button>
|
||||
<n-button type="primary" @click="confirmLocation">确认位置</n-button>
|
||||
</n-space>
|
||||
</n-space>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted, nextTick } from 'vue'
|
||||
import axios from 'axios'
|
||||
|
||||
const emit = defineEmits(['confirm', 'cancel'])
|
||||
|
||||
const keyword = ref('')
|
||||
const selectedLocation = ref(null)
|
||||
const currentLocation = ref(null)
|
||||
const searchResults = ref([])
|
||||
const showSearchResults = ref(false)
|
||||
let map = null
|
||||
let marker = null
|
||||
|
||||
const TDT_KEY = '42db4f3dfd1a18d31e73ee90aa2ce054'
|
||||
|
||||
// 初始化地图
|
||||
const initMap = () => {
|
||||
if (typeof T === 'undefined') {
|
||||
console.error('天地图API未加载完成')
|
||||
return
|
||||
}
|
||||
|
||||
map = new window.T.Map('mapDiv')
|
||||
map.centerAndZoom(new window.T.LngLat(116.40969, 39.89945), 12)
|
||||
|
||||
// 地图点击事件
|
||||
map.addEventListener('click', function (e) {
|
||||
const lnglat = e.lnglat
|
||||
addMarker(lnglat)
|
||||
getAddress(lnglat)
|
||||
// 点击地图时隐藏搜索结果
|
||||
showSearchResults.value = false
|
||||
})
|
||||
|
||||
// 获取当前位置
|
||||
getCurrentLocation()
|
||||
}
|
||||
|
||||
// 添加标记
|
||||
const addMarker = (lnglat) => {
|
||||
if (marker) {
|
||||
map.removeOverLay(marker)
|
||||
}
|
||||
|
||||
// 创建自定义图标
|
||||
const icon = new window.T.Icon({
|
||||
iconUrl:
|
||||
'data:image/svg+xml;base64,' +
|
||||
btoa(`
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="40" viewBox="0 0 32 40">
|
||||
<path fill="#ff4757" d="M16 0C7.2 0 0 7.2 0 16c0 8.8 16 24 16 24s16-15.2 16-24C32 7.2 24.8 0 16 0zm0 22c-3.3 0-6-2.7-6-6s2.7-6 6-6 6 2.7 6 6-2.7 6-6 6z"/>
|
||||
<circle fill="white" cx="16" cy="16" r="4"/>
|
||||
</svg>
|
||||
`),
|
||||
iconSize: new window.T.Point(32, 40),
|
||||
iconAnchor: new window.T.Point(16, 40),
|
||||
})
|
||||
|
||||
marker = new window.T.Marker(lnglat, { icon })
|
||||
map.addOverLay(marker)
|
||||
map.panTo(lnglat)
|
||||
|
||||
selectedLocation.value = {
|
||||
lat: lnglat.lat,
|
||||
lng: lnglat.lng,
|
||||
address: '正在获取地址...',
|
||||
}
|
||||
}
|
||||
|
||||
// 地理编码
|
||||
const getAddress = (lnglat) => {
|
||||
const geocoder = new window.T.Geocoder()
|
||||
geocoder.getLocation(lnglat, function (result) {
|
||||
const address = result.getAddress()
|
||||
if (selectedLocation.value) {
|
||||
selectedLocation.value.address = address
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 获取当前位置
|
||||
const getCurrentLocation = () => {
|
||||
if (navigator.geolocation) {
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
(position) => {
|
||||
const lat = position.coords.latitude
|
||||
const lng = position.coords.longitude
|
||||
const lnglat = new window.T.LngLat(lng, lat)
|
||||
|
||||
map.centerAndZoom(lnglat, 15)
|
||||
addMarker(lnglat)
|
||||
|
||||
// 获取地址信息,专门为当前位置设置
|
||||
const geocoder = new window.T.Geocoder()
|
||||
geocoder.getLocation(lnglat, function (result) {
|
||||
const address = result.getAddress()
|
||||
// 同时更新当前位置和选中位置
|
||||
currentLocation.value = {
|
||||
lat: lat,
|
||||
lng: lng,
|
||||
address: address,
|
||||
}
|
||||
if (selectedLocation.value) {
|
||||
selectedLocation.value.address = address
|
||||
}
|
||||
})
|
||||
},
|
||||
(error) => {
|
||||
console.error('获取位置失败:', error)
|
||||
$message.warning('无法获取当前位置,请手动选择')
|
||||
}
|
||||
)
|
||||
} else {
|
||||
$message.warning('浏览器不支持定位功能')
|
||||
}
|
||||
}
|
||||
|
||||
// 使用axios调用天地图HTTP API搜索
|
||||
const searchWithAPI = async (query) => {
|
||||
try {
|
||||
console.log('搜索关键词:', query)
|
||||
|
||||
// 构建搜索参数
|
||||
const postStr = {
|
||||
keyWord: query,
|
||||
level: 12,
|
||||
mapBound: '116.02524,39.83833,116.65592,39.99185',
|
||||
queryType: 1,
|
||||
start: 0,
|
||||
count: 10,
|
||||
}
|
||||
|
||||
const response = await axios.get('https://api.tianditu.gov.cn/v2/search', {
|
||||
params: {
|
||||
postStr: JSON.stringify(postStr),
|
||||
type: 'query',
|
||||
tk: TDT_KEY,
|
||||
},
|
||||
timeout: 10000, // 10秒超时
|
||||
})
|
||||
|
||||
console.log('搜索响应:', response.data)
|
||||
|
||||
if (
|
||||
response.data.status.infocode === 1000 &&
|
||||
response.data.pois &&
|
||||
response.data.pois.length > 0
|
||||
) {
|
||||
return response.data.pois.map((poi) => ({
|
||||
name: poi.name,
|
||||
address: poi.address,
|
||||
lonlat: poi.lonlat,
|
||||
adminName: poi.adminName,
|
||||
}))
|
||||
}
|
||||
return []
|
||||
} catch (error) {
|
||||
console.error('搜索失败:', error)
|
||||
|
||||
// 更详细的错误处理
|
||||
if (error.code === 'ECONNABORTED') {
|
||||
$message.error('请求超时,请重试')
|
||||
} else if (error.response) {
|
||||
$message.error(`搜索失败: ${error.response.status}`)
|
||||
} else if (error.request) {
|
||||
$message.error('网络请求失败,请检查网络连接')
|
||||
} else {
|
||||
$message.error('搜索出错,请重试')
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索功能
|
||||
const search = async () => {
|
||||
if (!keyword.value || !map) return
|
||||
|
||||
try {
|
||||
const results = await searchWithAPI(keyword.value)
|
||||
|
||||
if (results.length > 0) {
|
||||
searchResults.value = results
|
||||
showSearchResults.value = true
|
||||
console.log('搜索结果:', results)
|
||||
} else {
|
||||
$message.warning('未找到相关位置')
|
||||
searchResults.value = []
|
||||
showSearchResults.value = false
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('搜索出错:', error)
|
||||
$message.error('搜索失败,请重试')
|
||||
}
|
||||
}
|
||||
|
||||
// 选择搜索结果
|
||||
const selectSearchResult = (result) => {
|
||||
if (!result.lonlat) return
|
||||
|
||||
const [lng, lat] = result.lonlat.split(',').map(Number)
|
||||
const lnglat = new window.T.LngLat(lng, lat)
|
||||
|
||||
addMarker(lnglat)
|
||||
// 只更新选中位置,不影响当前位置
|
||||
if (selectedLocation.value) {
|
||||
selectedLocation.value.address = `${result.name} - ${result.address}`
|
||||
}
|
||||
showSearchResults.value = false
|
||||
}
|
||||
|
||||
// 处理搜索输入
|
||||
const handleInput = (value) => {
|
||||
if (value && value.length > 1) {
|
||||
// 延迟搜索,避免频繁请求
|
||||
clearTimeout(handleInput.timer)
|
||||
handleInput.timer = setTimeout(() => {
|
||||
search()
|
||||
}, 500)
|
||||
} else {
|
||||
searchResults.value = []
|
||||
showSearchResults.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 从搜索建议中选择
|
||||
const selectFromSearch = (value, option) => {
|
||||
if (option && option.data) {
|
||||
selectSearchResult(option.data)
|
||||
}
|
||||
}
|
||||
|
||||
// 确认选择的位置
|
||||
const confirmLocation = () => {
|
||||
if (selectedLocation.value) {
|
||||
emit('confirm', {
|
||||
latlng: {
|
||||
lat: selectedLocation.value.lat,
|
||||
lng: selectedLocation.value.lng,
|
||||
},
|
||||
address: selectedLocation.value.address || '',
|
||||
})
|
||||
} else {
|
||||
$message.warning('请先选择位置')
|
||||
}
|
||||
}
|
||||
|
||||
// 重新定位到当前位置
|
||||
const relocateToCurrentPosition = () => {
|
||||
if (currentLocation.value) {
|
||||
// 如果已有当前位置信息,直接使用
|
||||
const lnglat = new window.T.LngLat(currentLocation.value.lng, currentLocation.value.lat)
|
||||
addMarker(lnglat)
|
||||
if (selectedLocation.value) {
|
||||
selectedLocation.value.address = currentLocation.value.address
|
||||
}
|
||||
} else {
|
||||
// 重新获取当前位置
|
||||
getCurrentLocation()
|
||||
}
|
||||
showSearchResults.value = false
|
||||
}
|
||||
|
||||
// 加载天地图API
|
||||
const loadTiandituScript = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (typeof T !== 'undefined') {
|
||||
resolve()
|
||||
return
|
||||
}
|
||||
|
||||
const script = document.createElement('script')
|
||||
script.type = 'text/javascript'
|
||||
script.src = `https://api.tianditu.gov.cn/api?v=4.0&tk=${TDT_KEY}`
|
||||
script.onload = resolve
|
||||
script.onerror = reject
|
||||
document.head.appendChild(script)
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
await loadTiandituScript()
|
||||
await nextTick()
|
||||
initMap()
|
||||
} catch (error) {
|
||||
console.error('加载天地图API失败:', error)
|
||||
$message.error('地图加载失败,请检查网络连接')
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (map) {
|
||||
map = null
|
||||
marker = null
|
||||
}
|
||||
if (handleInput.timer) {
|
||||
clearTimeout(handleInput.timer)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.tianditu-picker {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
background-color: #f5f5f5;
|
||||
|
||||
.search-header {
|
||||
padding: 12px 0;
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid #eee;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.map-container {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.location-footer {
|
||||
background-color: #fff;
|
||||
border-top: 1px solid #eee;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
|
||||
.results-section {
|
||||
.results-header {
|
||||
padding: 12px 16px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
background-color: #f5f5f5;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.results-list {
|
||||
max-height: 180px;
|
||||
overflow-y: auto;
|
||||
|
||||
.result-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
|
||||
&:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.location-icon {
|
||||
font-size: 18px;
|
||||
margin-right: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.result-content {
|
||||
flex: 1;
|
||||
|
||||
.result-name {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.result-address {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.current-location-item {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.search-result-item {
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
padding: 12px 16px;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 深色模式适配
|
||||
.dark {
|
||||
.tianditu-picker {
|
||||
background-color: #1f1f1f;
|
||||
|
||||
.search-header {
|
||||
background-color: #2a2a2a;
|
||||
border-bottom-color: #3a3a3a;
|
||||
}
|
||||
|
||||
.location-footer {
|
||||
background-color: #2a2a2a;
|
||||
border-top-color: #3a3a3a;
|
||||
|
||||
.results-section {
|
||||
.results-header {
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
border-bottom-color: #3a3a3a;
|
||||
}
|
||||
|
||||
.results-list {
|
||||
.result-item {
|
||||
border-bottom-color: #3a3a3a;
|
||||
|
||||
&:hover {
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
.result-name {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.result-address {
|
||||
color: #ccc;
|
||||
}
|
||||
}
|
||||
|
||||
.current-location-item {
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
.search-result-item {
|
||||
background-color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
background-color: #2a2a2a;
|
||||
border-top-color: #3a3a3a;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -68,7 +68,7 @@ const tabs = [
|
||||
const count = ref(tabs.map((item) => item.messages).flat().length)
|
||||
|
||||
watch(activeTab, (v) => {
|
||||
if (count === 0) return
|
||||
if (count.value === 0) return
|
||||
const tabIndex = tabs.findIndex((item) => item.name === v)
|
||||
count.value -= tabs[tabIndex].messages.length
|
||||
if (count.value < 0) count.value = 0
|
||||
|
||||
@@ -9,10 +9,14 @@ import { setupRouter } from '@/router'
|
||||
import { setupStore } from '@/store'
|
||||
import App from './App.vue'
|
||||
import { setupNaiveDiscreteApi } from './utils'
|
||||
import { initApiEndpoint } from '@/utils/api-config'
|
||||
|
||||
async function setupApp() {
|
||||
const app = createApp(App)
|
||||
|
||||
// 初始化接口配置
|
||||
initApiEndpoint()
|
||||
|
||||
setupStore(app)
|
||||
setupNaiveDiscreteApi()
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { getToken, refreshAccessToken, isNullOrWhitespace } from '@/utils'
|
||||
import { addDynamicRoutes } from '@/router'
|
||||
|
||||
const WHITE_LIST = ['/login', '/404']
|
||||
export function createPermissionGuard(router) {
|
||||
@@ -17,6 +18,20 @@ export function createPermissionGuard(router) {
|
||||
/** 有token的情况 */
|
||||
if (to.path === '/login') return { path: '/' }
|
||||
|
||||
// 确保动态路由已加载
|
||||
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' }
|
||||
}
|
||||
}
|
||||
|
||||
refreshAccessToken()
|
||||
return true
|
||||
})
|
||||
|
||||
@@ -3,7 +3,6 @@ import { setupRouterGuard } from './guard'
|
||||
import { basicRoutes, EMPTY_ROUTE, NOT_FOUND_ROUTE } from './routes'
|
||||
import { getToken, isNullOrWhitespace } from '@/utils'
|
||||
import { useUserStore, usePermissionStore } from '@/store'
|
||||
import * as Sentry from '@sentry/vue'
|
||||
|
||||
const isHash = false
|
||||
export const router = createRouter({
|
||||
@@ -15,22 +14,6 @@ export const router = createRouter({
|
||||
export async function setupRouter(app) {
|
||||
await addDynamicRoutes()
|
||||
setupRouterGuard(router)
|
||||
if (import.meta.env.VITE_SENTRY === 'true') {
|
||||
Sentry.init({
|
||||
app,
|
||||
dsn: 'https://aa4308fc56a9d107786b8dbcd2ae56e8@w.huakk.top/13',
|
||||
integrations: [
|
||||
new Sentry.BrowserTracing({
|
||||
tracePropagationTargets: ['localhost', /^https:\/\/w\.huakk\.top\/api/],
|
||||
routingInstrumentation: Sentry.vueRouterInstrumentation(router),
|
||||
}),
|
||||
new Sentry.Replay(),
|
||||
],
|
||||
tracesSampleRate: 1.0,
|
||||
replaysSessionSampleRate: 0.1,
|
||||
replaysOnErrorSampleRate: 1.0,
|
||||
})
|
||||
}
|
||||
app.use(router)
|
||||
}
|
||||
|
||||
@@ -59,13 +42,45 @@ export async function addDynamicRoutes() {
|
||||
const permissionStore = usePermissionStore()
|
||||
!userStore.userId && (await userStore.getUserInfo())
|
||||
const accessRoutes = permissionStore.generateRoutes(userStore.role)
|
||||
|
||||
// 确保路由按正确顺序添加
|
||||
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)
|
||||
|
||||
// 确保根路径重定向到工作台
|
||||
if (!router.hasRoute('Dashboard')) {
|
||||
const workbenchRoute = {
|
||||
name: 'Dashboard',
|
||||
path: '/',
|
||||
component: () => import('@/layout/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)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,10 +18,7 @@ export const useUserStore = defineStore('user', {
|
||||
return this.userInfo?.name
|
||||
},
|
||||
avatar() {
|
||||
return (
|
||||
this.userInfo?.avatar ||
|
||||
'https://pic3.58cdn.com.cn/nowater/webim/big/n_v21bc7874294754e63a22b80febac9cf51.jpg'
|
||||
)
|
||||
return this.userInfo?.avatar || 'https://v2.xxapi.cn/api/head?return=302'
|
||||
},
|
||||
role() {
|
||||
return this.userInfo?.role || []
|
||||
|
||||
65
src/utils/api-config.js
Normal file
65
src/utils/api-config.js
Normal file
@@ -0,0 +1,65 @@
|
||||
// 判断是否为开发环境
|
||||
const isDev = import.meta.env.DEV
|
||||
|
||||
// API接口线路配置管理
|
||||
export const API_ENDPOINTS = {
|
||||
primary: {
|
||||
name: '主要线路',
|
||||
baseURL: import.meta.env.VITE_BASE_API,
|
||||
base_admin_url: import.meta.env.VITE_ADMIN_API,
|
||||
timeout: 10000,
|
||||
},
|
||||
backup1: {
|
||||
name: '备用线路',
|
||||
baseURL: import.meta.env.VITE_BASE_API_1,
|
||||
base_admin_url: import.meta.env.VITE_ADMIN_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('环境变量 VITE_BASE_ADMIN:', import.meta.env.VITE_ADMIN_API)
|
||||
console.log('环境变量 VITE_BASE_ADMIN_1:', import.meta.env.VITE_ADMIN_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
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import axios from 'axios'
|
||||
import { resReject, resResolve, reqReject, reqResolve } from './interceptors'
|
||||
import { getCurrentEndpoint, getAvailableEndpoints } from '../api-config'
|
||||
|
||||
export function createAxios(options = {}) {
|
||||
const defaultOptions = {
|
||||
@@ -14,6 +15,114 @@ export function createAxios(options = {}) {
|
||||
return service
|
||||
}
|
||||
|
||||
export const request = createAxios({
|
||||
baseURL: import.meta.env.VITE_BASE_API,
|
||||
})
|
||||
// 检测是否为admin类接口
|
||||
function isAdminEndpoint(url) {
|
||||
console.log('url', url)
|
||||
// 检查URL是否包含admin相关路径
|
||||
return url.includes('/admin')
|
||||
}
|
||||
|
||||
// 根据URL类型选择对应的baseURL
|
||||
function getBaseURLByUrlType(endpoint, url) {
|
||||
console.log('endpoint', endpoint)
|
||||
console.log('url', url)
|
||||
if (isAdminEndpoint(url)) {
|
||||
return endpoint.base_admin_url || endpoint.baseURL
|
||||
}
|
||||
return endpoint.baseURL
|
||||
}
|
||||
|
||||
// 创建支持多接口的请求实例
|
||||
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: isAdminEndpoint(endpoint.baseURL) ? endpoint.base_admin_url : endpoint.baseURL,
|
||||
timeout: endpoint.timeout,
|
||||
})
|
||||
})
|
||||
}
|
||||
return instances
|
||||
}
|
||||
|
||||
return {
|
||||
// 使用当前选中的接口发送请求
|
||||
async request(config) {
|
||||
const currentEndpoint = getCurrentEndpoint()
|
||||
const url = config.url || ''
|
||||
|
||||
// 根据URL类型选择baseURL
|
||||
const baseURL = getBaseURLByUrlType(currentEndpoint, url)
|
||||
|
||||
console.log('当前接口配置:', currentEndpoint)
|
||||
console.log('请求URL:', url)
|
||||
console.log('是否为admin接口:', isAdminEndpoint(url))
|
||||
console.log('选择的baseURL:', baseURL)
|
||||
|
||||
// 创建临时axios实例
|
||||
const instance = createAxios({
|
||||
baseURL: baseURL,
|
||||
timeout: currentEndpoint.timeout,
|
||||
})
|
||||
|
||||
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(),
|
||||
}
|
||||
|
||||
@@ -2,13 +2,11 @@ import { getToken } from '@/utils'
|
||||
import { resolveResError } from './helpers'
|
||||
|
||||
export function reqResolve(config) {
|
||||
if (config.url.includes('/admin')) {
|
||||
config.url = config.url.replace(new RegExp('^/admin'), '')
|
||||
console.log(config)
|
||||
config.baseURL = import.meta.env.VITE_ADMIN_API
|
||||
} else {
|
||||
config.baseURL = import.meta.env.VITE_BASE_API
|
||||
}
|
||||
// if (config.url.includes('/admin')) {
|
||||
// config.url = config.url.replace(new RegExp('^/admin'), '')
|
||||
// console.log(config)
|
||||
// config.baseURL = import.meta.env.VITE_ADMIN_API
|
||||
// }
|
||||
// 处理不需要token的请求
|
||||
if (config.noNeedToken) {
|
||||
return config
|
||||
|
||||
@@ -58,6 +58,17 @@
|
||||
/>
|
||||
</div>
|
||||
</n-col>
|
||||
<n-col :span="24">
|
||||
<div mt-10 flex items-center>
|
||||
<span w-100>投注时间:</span>
|
||||
<n-date-picker
|
||||
v-model:formatted-value="queryData.time1"
|
||||
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="get_list">搜索</n-button>
|
||||
@@ -86,10 +97,10 @@ const songs = ref([
|
||||
label: '未使用',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '已使用',
|
||||
value: 2,
|
||||
},
|
||||
// {
|
||||
// label: '已使用',
|
||||
// value: 2,
|
||||
// },
|
||||
{
|
||||
label: '赢',
|
||||
value: 3,
|
||||
@@ -109,6 +120,7 @@ const queryData = ref({
|
||||
hall_id: null,
|
||||
status: null,
|
||||
time: null,
|
||||
time1: null,
|
||||
})
|
||||
|
||||
const options = ref([])
|
||||
@@ -199,6 +211,8 @@ const get_list = async () => {
|
||||
pageSize: pagination.value.pageSize,
|
||||
StartTime: queryData.value.time ? queryData.value.time[0] : '',
|
||||
EndTime: queryData.value.time ? queryData.value.time[1] : '',
|
||||
UseStartTime: queryData.value.time1 ? queryData.value.time1[0] : '',
|
||||
UseEndTime: queryData.value.time1 ? queryData.value.time1[1] : '',
|
||||
}
|
||||
Reflect.deleteProperty(obj, 'time')
|
||||
const res = await api.getData(obj)
|
||||
|
||||
@@ -7,7 +7,7 @@ export default {
|
||||
redirect: '/game_jl',
|
||||
meta: {
|
||||
title: '游戏统计',
|
||||
icon: 'mdi:home',
|
||||
icon: 'mdi:access-point',
|
||||
order: 100,
|
||||
},
|
||||
children: [
|
||||
@@ -17,7 +17,7 @@ export default {
|
||||
component: () => import('./jl/index.vue'),
|
||||
meta: {
|
||||
title: '豆子记录',
|
||||
icon: 'mdi:home',
|
||||
icon: 'mdi:access-point-check',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -32,12 +32,12 @@
|
||||
<!-- ]"-->
|
||||
<!-- />-->
|
||||
<!-- </n-form-item-gi>-->
|
||||
<n-form-item-gi :span="12" label="现金价格:" path="number">
|
||||
<n-form-item-gi :span="12" label="商品价格:" path="number">
|
||||
<n-input-number v-model:value="model.number" :min="0" placeholder="输入现金价格" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="12" label="积分价格:" path="exchange">
|
||||
<n-input-number v-model:value="model.exchange" :min="0" placeholder="输入积分价格" />
|
||||
</n-form-item-gi>
|
||||
<!-- <n-form-item-gi :span="12" label="积分价格:" path="exchange">-->
|
||||
<!-- <n-input-number v-model:value="model.exchange" :min="0" placeholder="输入积分价格" />-->
|
||||
<!-- </n-form-item-gi>-->
|
||||
<n-form-item-gi :span="12" label="商品库存:" path="stock">
|
||||
<n-input-number v-model:value="model.stock" :min="0" placeholder="输入商品库存" />
|
||||
</n-form-item-gi>
|
||||
@@ -118,12 +118,12 @@ const rules = {
|
||||
message: '请输入商品价格',
|
||||
trigger: 'blur',
|
||||
},
|
||||
exchange: {
|
||||
required: true,
|
||||
type: 'number',
|
||||
message: '请输入市场价格',
|
||||
trigger: 'blur',
|
||||
},
|
||||
// exchange: {
|
||||
// required: true,
|
||||
// type: 'number',
|
||||
// message: '请输入市场价格',
|
||||
// trigger: 'blur',
|
||||
// },
|
||||
stock: {
|
||||
required: true,
|
||||
type: 'number',
|
||||
@@ -147,10 +147,10 @@ const classList = ref([])
|
||||
const getClassList = async () => {
|
||||
isShowSpin.value = true
|
||||
const res = await api.getGoodClass()
|
||||
classList.value = res.data.data
|
||||
classList.value = res.data.data.filter((item) => item.status !== 2)
|
||||
|
||||
if (row && type.value === 'edit') {
|
||||
console.log(row)
|
||||
// console.log(row)
|
||||
type.value = 'edit'
|
||||
model.value = {
|
||||
...row,
|
||||
|
||||
@@ -1,6 +1,25 @@
|
||||
<template>
|
||||
<CommonPage show-footer :title="$route.title">
|
||||
<n-button type="primary" @click="addGood(1)">添加商品</n-button>
|
||||
<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: '20%' }"
|
||||
:options="selectOptions"
|
||||
placeholder="请选择"
|
||||
/>
|
||||
<n-input v-model:value="queryParams.word" :style="{ width: '30%' }" />
|
||||
</n-input-group>
|
||||
</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-button ml-10 type="primary" @click="addGood(1)">添加商品</n-button>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
|
||||
<n-data-table
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
@@ -21,6 +40,18 @@ import { useGoodsStore } from '@/store/modules/goods'
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const selectOptions = ref([
|
||||
{
|
||||
label: '商品名称',
|
||||
value: 0,
|
||||
},
|
||||
])
|
||||
|
||||
const queryParams = ref({
|
||||
selectKey: 0,
|
||||
word: '',
|
||||
})
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: 'ID',
|
||||
@@ -160,6 +191,7 @@ const getList = async () => {
|
||||
const res = await api.getGoods({
|
||||
pageNum: pagination.value.page,
|
||||
pageSize: pagination.value.pageSize,
|
||||
name: queryParams.value.word,
|
||||
})
|
||||
data.value = res.data.data || []
|
||||
pagination.value.itemCount = res.data.total
|
||||
@@ -182,6 +214,11 @@ const toEdit = (item) => {
|
||||
setRow(item)
|
||||
addGood()
|
||||
}
|
||||
|
||||
const clear = () => {
|
||||
queryParams.value.word = ''
|
||||
getList()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import { request } from '@/utils'
|
||||
import { request, multiRequest } from '@/utils'
|
||||
|
||||
export default {
|
||||
login: (data) => request.post('/login', data, { noNeedToken: true }),
|
||||
// 使用多接口的登录方法
|
||||
loginWithMultiEndpoint: (data) =>
|
||||
multiRequest.request({
|
||||
method: 'post',
|
||||
url: '/login',
|
||||
data,
|
||||
noNeedToken: true,
|
||||
}),
|
||||
}
|
||||
|
||||
@@ -14,7 +14,17 @@
|
||||
<img src="@/assets/images/logo.png" height="50" class="mr-10" />
|
||||
{{ title }}
|
||||
</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
|
||||
v-model:value="loginInfo.name"
|
||||
autofocus
|
||||
@@ -23,7 +33,7 @@
|
||||
:maxlength="20"
|
||||
/>
|
||||
</div>
|
||||
<div mt-30>
|
||||
<div mt-10>
|
||||
<n-input
|
||||
v-model:value="loginInfo.password"
|
||||
class="h-50 items-center pl-10 text-16"
|
||||
@@ -68,6 +78,12 @@ import bgImg from '@/assets/images/login_bg.webp'
|
||||
import api from './api'
|
||||
import { addDynamicRoutes } from '@/router'
|
||||
import { useUserStore } from '@/store'
|
||||
import {
|
||||
getAvailableEndpoints,
|
||||
setCurrentEndpoint,
|
||||
getCurrentEndpoint,
|
||||
initApiEndpoint,
|
||||
} from '@/utils/api-config'
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
@@ -89,6 +105,7 @@ const easyLogin = async () => {
|
||||
console.log(query)
|
||||
$message.success('登录成功')
|
||||
setToken(query.tk)
|
||||
setCurrentEndpoint(query.api)
|
||||
window.localStorage.setItem('type', query.type)
|
||||
await addDynamicRoutes()
|
||||
if (query.redirect) {
|
||||
@@ -113,6 +130,39 @@ function initLoginInfo() {
|
||||
|
||||
const isRemember = useStorage('isRemember', 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()
|
||||
})
|
||||
async function handleLogin() {
|
||||
const { name, password } = loginInfo.value
|
||||
if (!name || !password) {
|
||||
@@ -122,7 +172,14 @@ async function handleLogin() {
|
||||
try {
|
||||
loading.value = true
|
||||
$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('登录成功')
|
||||
setToken(res.data.token)
|
||||
window.localStorage.setItem('type', res.data.type)
|
||||
@@ -140,7 +197,7 @@ async function handleLogin() {
|
||||
router.push('/')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
console.error('登录请求失败:', error)
|
||||
$message.removeMessage()
|
||||
}
|
||||
loading.value = false
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
<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>
|
||||
@@ -82,6 +83,7 @@
|
||||
:data="data"
|
||||
:pagination="pagination"
|
||||
:bordered="false"
|
||||
:scroll-x="1800"
|
||||
remote
|
||||
/>
|
||||
</CommonPage>
|
||||
@@ -89,6 +91,7 @@
|
||||
|
||||
<script setup>
|
||||
import api from './api'
|
||||
import * as XLSX from 'xlsx'
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
@@ -101,15 +104,15 @@ const queryData = ref({
|
||||
|
||||
const songs = ref([
|
||||
{
|
||||
value: 0,
|
||||
value: 1,
|
||||
label: '未付款',
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
value: 2,
|
||||
label: '已付款',
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
value: 3,
|
||||
label: '挂帐中',
|
||||
},
|
||||
])
|
||||
@@ -134,10 +137,20 @@ const selectOptions = ref([
|
||||
])
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '桌号',
|
||||
align: 'center',
|
||||
key: 'seat',
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
align: 'center',
|
||||
key: 'notes',
|
||||
},
|
||||
{
|
||||
title: '订单号',
|
||||
align: 'center',
|
||||
key: 'oid',
|
||||
key: 'jl_oid',
|
||||
},
|
||||
{
|
||||
title: '用户',
|
||||
@@ -242,6 +255,8 @@ const pagination = ref({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
itemCount: 0,
|
||||
showSizePicker: true,
|
||||
pageSizes: [10, 20, 50, 100],
|
||||
onChange: (page) => {
|
||||
pagination.value.page = page
|
||||
getList()
|
||||
@@ -306,6 +321,16 @@ const clear = () => {
|
||||
}
|
||||
getList()
|
||||
}
|
||||
|
||||
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>
|
||||
|
||||
@@ -2,4 +2,6 @@ import { request } from '@/utils'
|
||||
|
||||
export default {
|
||||
getOrder: (data) => request.post('/order', data),
|
||||
// 核销订单
|
||||
verifyOrder: (data) => request.post('/order/verify', data),
|
||||
}
|
||||
|
||||
@@ -20,7 +20,12 @@
|
||||
</n-card>
|
||||
<n-card ml-10 w-200 rounded-5>
|
||||
<n-statistic label="总佣金" tabular-nums>
|
||||
<n-number-animation :from="0" :to="cardData.commission" />
|
||||
<n-number-animation :from="0" :to="cardData.commission" :precision="2" />
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
<n-card ml-10 w-200 rounded-5>
|
||||
<n-statistic label="现金部分" tabular-nums>
|
||||
<n-number-animation :from="0" :to="cardData.discount_number" :precision="2" />
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
</div>
|
||||
@@ -39,6 +44,10 @@
|
||||
label: '积分',
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
label: '小猪积分',
|
||||
value: 3,
|
||||
},
|
||||
]"
|
||||
:key="song.value"
|
||||
:value="song.value"
|
||||
@@ -88,6 +97,7 @@
|
||||
<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>
|
||||
@@ -99,6 +109,7 @@
|
||||
:data="data"
|
||||
:pagination="pagination"
|
||||
:bordered="false"
|
||||
:scroll-x="2000"
|
||||
remote
|
||||
/>
|
||||
</CommonPage>
|
||||
@@ -106,6 +117,8 @@
|
||||
|
||||
<script setup>
|
||||
import api from './api'
|
||||
import { NButton, NPopconfirm } from 'naive-ui'
|
||||
import * as XLSX from 'xlsx'
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
@@ -164,11 +177,15 @@ const columns = ref([
|
||||
title: '订单号',
|
||||
align: 'center',
|
||||
key: 'oid',
|
||||
width: 200,
|
||||
fixed: 'left',
|
||||
},
|
||||
{
|
||||
title: '用户',
|
||||
align: 'center',
|
||||
slot: 'user',
|
||||
width: 100,
|
||||
fixed: 'left',
|
||||
render: (row) => {
|
||||
return [
|
||||
h(
|
||||
@@ -224,22 +241,26 @@ const columns = ref([
|
||||
key: 'count',
|
||||
},
|
||||
{
|
||||
title: '订单总价',
|
||||
title: '订单金额',
|
||||
align: 'center',
|
||||
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: '支付方式',
|
||||
align: 'center',
|
||||
slot: 'pay_type',
|
||||
render: (row) => h('span', row.pay_type === 1 ? '微信' : '积分'),
|
||||
},
|
||||
{
|
||||
title: '订单总价',
|
||||
align: 'center',
|
||||
slot: 'number',
|
||||
render: (row) => h('span', row.pay_type === 1 ? `${row.price}元` : `${row.exchange}积分`),
|
||||
render: (row) => h('span', row.pay_type === 1 ? '微信' : '平台抵扣'),
|
||||
},
|
||||
{
|
||||
title: '订单状态',
|
||||
@@ -301,14 +322,47 @@ const columns = ref([
|
||||
align: 'center',
|
||||
key: 'cancel_time',
|
||||
},
|
||||
// {
|
||||
// title: '操作',
|
||||
// align: 'center',
|
||||
// slot: 'action',
|
||||
// render() {
|
||||
// // console.log(row)
|
||||
// },
|
||||
// },
|
||||
{
|
||||
title: '操作',
|
||||
align: 'center',
|
||||
slot: 'action',
|
||||
fixed: 'right',
|
||||
render(row) {
|
||||
const el = []
|
||||
if (row.status === 1) {
|
||||
el.push(
|
||||
h(
|
||||
NPopconfirm,
|
||||
{
|
||||
onPositiveClick: async () => {
|
||||
await api.verifyOrder({
|
||||
oids: [row.oid],
|
||||
})
|
||||
getList()
|
||||
},
|
||||
onNegativeClick: () => $message.info('已取消'),
|
||||
},
|
||||
{
|
||||
default: () => `确定核销此${row.oid}订单吗?`,
|
||||
trigger: () =>
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
type: 'primary',
|
||||
size: 'small',
|
||||
text: true,
|
||||
},
|
||||
{
|
||||
default: () => '核销',
|
||||
}
|
||||
),
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
return el
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const data = ref([])
|
||||
@@ -325,6 +379,8 @@ const pagination = ref({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
itemCount: 0,
|
||||
showSizePicker: true,
|
||||
pageSizes: [10, 20, 50, 100],
|
||||
onChange: (page) => {
|
||||
pagination.value.page = page
|
||||
getList()
|
||||
@@ -375,6 +431,7 @@ const getList = async () => {
|
||||
cardData.value.count = res.data.total
|
||||
cardData.value.commission = res.data.total_commission
|
||||
cardData.value.pulse = res.data.total_pulse
|
||||
cardData.value.discount_number = res.data.discount_number
|
||||
} catch (error) {
|
||||
$message.error(error.msg)
|
||||
throw error
|
||||
@@ -392,6 +449,16 @@ const clear = () => {
|
||||
}
|
||||
getList()
|
||||
}
|
||||
|
||||
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></style>
|
||||
|
||||
@@ -1,5 +1,25 @@
|
||||
<template>
|
||||
<CommonPage show-footer :title="$route.title">
|
||||
<n-grid mb-10 flex items-center x-gap="10" :y-gap="8" :cols="3">
|
||||
<n-gi>
|
||||
<!-- {{ queryData }} -->
|
||||
<div flex items-center>
|
||||
<span w-120>时间筛选:</span>
|
||||
<n-date-picker
|
||||
v-model:formatted-value="queryData.time"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
type="datetimerange"
|
||||
clearable
|
||||
/>
|
||||
</div>
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
<div>
|
||||
<n-button type="primary" @click="get_data">搜索</n-button>
|
||||
<n-button ml-10 @click="clear">重置</n-button>
|
||||
</div>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
<n-card mb-5 rounded-5 title="营业额汇总">
|
||||
<n-grid x-gap="10" :y-gap="8" :cols="4">
|
||||
<n-gi>
|
||||
@@ -47,7 +67,7 @@
|
||||
<n-gi>
|
||||
<n-card rounded-5 style="background-color: #797979">
|
||||
<n-statistic label="抹零(元)" tabular-nums>
|
||||
<n-number-animation :from="0" :to="cardData.zero" />
|
||||
<n-number-animation :from="0" :to="cardData.zero" :precision="2" />
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
</n-gi>
|
||||
@@ -67,6 +87,10 @@ const cardData = ref({})
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const queryData = ref({
|
||||
time: null,
|
||||
})
|
||||
|
||||
const option = ref({
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
@@ -91,6 +115,7 @@ const option = ref({
|
||||
{
|
||||
data: [],
|
||||
type: 'bar',
|
||||
barWidth: '10%',
|
||||
name: '金额(元)',
|
||||
label: {
|
||||
show: true,
|
||||
@@ -119,14 +144,12 @@ const get_data = async () => {
|
||||
loading.value = true
|
||||
option.value.xAxis.data = []
|
||||
option.value.series[0].data = []
|
||||
const res = await api.getData()
|
||||
cardData.value = res.data
|
||||
for (let i = 0; i < 100; i++) {
|
||||
res.data.data.push({
|
||||
Name: '商品' + i,
|
||||
Price: 2000 - i * 20,
|
||||
})
|
||||
const query_data = {
|
||||
StartTime: queryData.value.time === null ? '' : queryData.value.time[0] || '',
|
||||
EndTime: queryData.value.time === null ? '' : queryData.value.time[1] || '',
|
||||
}
|
||||
const res = await api.getData(query_data)
|
||||
cardData.value = res.data
|
||||
for (let i = 0; i < res.data.data.length; i++) {
|
||||
option.value.xAxis.data.push(res.data.data[i].Name)
|
||||
option.value.series[0].data.push(res.data.data[i].Price)
|
||||
@@ -134,6 +157,11 @@ const get_data = async () => {
|
||||
console.log(cardData.value)
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
const clear = async () => {
|
||||
queryData.value.time = null
|
||||
get_data()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
<n-col :span="24">
|
||||
<div flex>
|
||||
<n-card w-500>
|
||||
<n-statistic label="订单流水(积分)" tabular-nums>
|
||||
<n-statistic label="订单流水(元)" tabular-nums>
|
||||
<n-number-animation :from="0" :to="cardData.total" />
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
<n-card ml-10 w-500>
|
||||
<n-statistic label="订单服务费(积分)" tabular-nums>
|
||||
<n-statistic label="订单服务费(元)" tabular-nums>
|
||||
<n-number-animation :from="0" :to="cardData.service" />
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
@@ -139,13 +139,13 @@ const getList = async () => {
|
||||
key: 'oid',
|
||||
align: 'center',
|
||||
},
|
||||
// {
|
||||
// title: '用户名称',
|
||||
// key: 'user_name',
|
||||
// align: 'center',
|
||||
// },
|
||||
{
|
||||
title: '用户名称',
|
||||
key: 'user_name',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '上次留存积分',
|
||||
title: '上次留存余额',
|
||||
key: 'balance',
|
||||
align: 'center',
|
||||
},
|
||||
@@ -155,13 +155,13 @@ const getList = async () => {
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '获取积分',
|
||||
title: '获取余额',
|
||||
key: 'number',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '时间',
|
||||
key: 'record_time',
|
||||
key: 'add_time',
|
||||
align: 'center',
|
||||
},
|
||||
]
|
||||
@@ -174,18 +174,18 @@ const getList = async () => {
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '上次留存积分',
|
||||
title: '上次留存余额',
|
||||
key: 'balance',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '扣除积分',
|
||||
title: '扣除余额',
|
||||
key: 'record_number',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '时间',
|
||||
key: 'record_time',
|
||||
key: 'add_time',
|
||||
align: 'center',
|
||||
},
|
||||
]
|
||||
@@ -198,18 +198,18 @@ const getList = async () => {
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '上次留存积分',
|
||||
title: '上次留存余额',
|
||||
key: 'balance',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '获取积分',
|
||||
title: '获取余额',
|
||||
key: 'record_number',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '时间',
|
||||
key: 'record_time',
|
||||
key: 'add_time',
|
||||
align: 'center',
|
||||
},
|
||||
]
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
const Layout = () => import('@/layout/index.vue')
|
||||
|
||||
export default {
|
||||
name: '积分管理',
|
||||
name: '余额管理',
|
||||
path: '/settlement',
|
||||
component: Layout,
|
||||
redirect: 'jf_list',
|
||||
meta: {
|
||||
title: '积分管理',
|
||||
title: '余额管理',
|
||||
icon: 'mdi:account-multiple',
|
||||
order: 10,
|
||||
},
|
||||
@@ -16,7 +16,7 @@ export default {
|
||||
path: 'jf_list',
|
||||
component: () => import('./jf_list/index.vue'),
|
||||
meta: {
|
||||
title: '积分明细',
|
||||
title: '余额明细',
|
||||
icon: 'mdi:account-multiple',
|
||||
order: 10,
|
||||
},
|
||||
@@ -26,10 +26,20 @@ export default {
|
||||
path: 'tx_list',
|
||||
component: () => import('./tx_list/index.vue'),
|
||||
meta: {
|
||||
title: '积分提现',
|
||||
title: '余额提现',
|
||||
icon: 'mdi:account-multiple',
|
||||
order: 10,
|
||||
},
|
||||
},
|
||||
// {
|
||||
// name: 'Ylist',
|
||||
// path: 'y_list',
|
||||
// component: () => import('./y_list/index.vue'),
|
||||
// meta: {
|
||||
// title: '后结提现',
|
||||
// icon: 'mdi:account-multiple',
|
||||
// order: 10,
|
||||
// },
|
||||
// },
|
||||
],
|
||||
}
|
||||
|
||||
@@ -1,9 +1,26 @@
|
||||
<template>
|
||||
<!-- <div> -->
|
||||
<CommonPage show-footer :title="$route.title">
|
||||
<div w-1200 flex items-center justify-between>
|
||||
<div w-1200 flex items-center>
|
||||
<!-- <n-card class="w-300">-->
|
||||
<!-- <n-statistic label="可提现积分">-->
|
||||
<!-- <n-number-animation-->
|
||||
<!-- ref="numberAnimationInstRef"-->
|
||||
<!-- :precision="2"-->
|
||||
<!-- :from="0"-->
|
||||
<!-- :to="userInfo.integral"-->
|
||||
<!-- />-->
|
||||
<!-- </n-statistic>-->
|
||||
<!-- </n-card>-->
|
||||
<!-- <div w-100 text-center text-25>/</div>-->
|
||||
<!-- <n-card class="w-300">-->
|
||||
<!-- <n-statistic label="兑换比例">-->
|
||||
<!-- <n-number-animation ref="numberAnimationInstRef" :precision="2" :from="0" :to="100" />-->
|
||||
<!-- </n-statistic>-->
|
||||
<!-- </n-card>-->
|
||||
<!-- <div w-100 text-center text-25>=</div>-->
|
||||
<n-card class="w-300">
|
||||
<n-statistic label="可提现积分">
|
||||
<n-statistic label="CNY">
|
||||
<n-number-animation
|
||||
ref="numberAnimationInstRef"
|
||||
:precision="2"
|
||||
@@ -12,25 +29,13 @@
|
||||
/>
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
<div w-100 text-center text-25>/</div>
|
||||
<n-card class="w-300">
|
||||
<n-statistic label="兑换比例">
|
||||
<n-number-animation ref="numberAnimationInstRef" :precision="2" :from="0" :to="100" />
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
<div w-100 text-center text-25>=</div>
|
||||
<n-card class="w-300">
|
||||
<n-statistic label="CNY">
|
||||
<n-number-animation
|
||||
ref="numberAnimationInstRef"
|
||||
:precision="2"
|
||||
:from="0"
|
||||
:to="userInfo.integral / 100"
|
||||
/>
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
<div ml-10 w-300 flex flex-col items-center justify-center>
|
||||
<n-input-number v-model:value="formData.integral" clearable placeholder="请输入提现积分" />
|
||||
<n-input-number
|
||||
v-model:value="formData.integral"
|
||||
clearable
|
||||
w-full
|
||||
placeholder="请输入提现余额"
|
||||
/>
|
||||
<n-button mt-10 w-full type="primary" @click="ok">立即提现</n-button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -66,7 +71,7 @@ const columns = ref([
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '上次留存积分',
|
||||
title: '上次留存余额',
|
||||
key: 'balance',
|
||||
align: 'center',
|
||||
},
|
||||
|
||||
7
src/views/settlement/y_list/api.js
Normal file
7
src/views/settlement/y_list/api.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import { request } from '@/utils'
|
||||
|
||||
export default {
|
||||
getList: (data) => request.post('/amount/withdraw', data),
|
||||
// 申请提现
|
||||
apply: (data) => request.post('/amount/withdraw/set', data),
|
||||
}
|
||||
243
src/views/settlement/y_list/index.vue
Normal file
243
src/views/settlement/y_list/index.vue
Normal file
@@ -0,0 +1,243 @@
|
||||
<template>
|
||||
<!-- <div> -->
|
||||
<CommonPage show-footer :title="$route.title">
|
||||
<div w-1200 flex items-center justify-between>
|
||||
<n-card class="w-300">
|
||||
<n-statistic label="可提现余额">
|
||||
<n-number-animation
|
||||
ref="numberAnimationInstRef"
|
||||
:precision="2"
|
||||
:from="0"
|
||||
:to="userInfo.integral"
|
||||
/>
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
<div w-100 text-center text-25>/</div>
|
||||
<n-card class="w-300">
|
||||
<n-statistic label="兑换比例">
|
||||
<n-number-animation ref="numberAnimationInstRef" :precision="2" :from="0" :to="100" />
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
<div w-100 text-center text-25>=</div>
|
||||
<n-card class="w-300">
|
||||
<n-statistic label="CNY">
|
||||
<n-number-animation
|
||||
ref="numberAnimationInstRef"
|
||||
:precision="2"
|
||||
:from="0"
|
||||
:to="userInfo.integral / 100"
|
||||
/>
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
<div ml-10 w-300 flex flex-col items-center justify-center>
|
||||
<n-input-number v-model:value="formData.integral" clearable placeholder="请输入提现积分" />
|
||||
<n-button mt-10 w-full type="primary" @click="ok">立即提现</n-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<n-data-table
|
||||
class="mt-10"
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:data="data"
|
||||
:pagination="pagination"
|
||||
:bordered="false"
|
||||
remote
|
||||
/>
|
||||
</CommonPage>
|
||||
<!-- </div> -->
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import api from './api'
|
||||
import comm from '@/api'
|
||||
import { NTag, NImage } from 'naive-ui'
|
||||
|
||||
const loading = ref(false)
|
||||
const columns = ref([
|
||||
{
|
||||
title: 'ID',
|
||||
key: 'ID',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '提现金额',
|
||||
key: 'integral',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '上次留存余额',
|
||||
key: 'balance',
|
||||
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: '银行名称',
|
||||
key: 'bank',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '银行卡号',
|
||||
key: 'bank_card',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '账户名称',
|
||||
key: 'bank_name',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '法人',
|
||||
key: 'bank_user',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '提现状态',
|
||||
slot: 'status',
|
||||
align: 'center',
|
||||
render: (row) => {
|
||||
return h(
|
||||
NTag,
|
||||
{
|
||||
type: row.status === 1 ? 'success' : row.status === 2 ? 'error' : 'warning',
|
||||
},
|
||||
{
|
||||
default: () => (row.status === 1 ? '已审核' : row.status === 2 ? '已拒绝' : '待审核'),
|
||||
}
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '提现时间',
|
||||
slot: 'add_time',
|
||||
align: 'center',
|
||||
render: (row) => {
|
||||
return h(
|
||||
'span',
|
||||
{},
|
||||
{
|
||||
default: () => row.add_time,
|
||||
}
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '打款截图',
|
||||
slot: 'img',
|
||||
render: (row) => {
|
||||
return h(NImage, {
|
||||
src: row.status_img,
|
||||
width: '50',
|
||||
})
|
||||
},
|
||||
},
|
||||
])
|
||||
const data = ref([])
|
||||
|
||||
const formData = ref({
|
||||
integral: null,
|
||||
})
|
||||
|
||||
const pagination = ref({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
itemCount: 0,
|
||||
onChange: (page) => {
|
||||
pagination.value.page = page
|
||||
getList()
|
||||
},
|
||||
onUpdatePageSize: (pageSize) => {
|
||||
pagination.value.pageSize = pageSize
|
||||
pagination.value.page = 1
|
||||
getList()
|
||||
},
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
getData()
|
||||
})
|
||||
|
||||
const userInfo = ref({})
|
||||
|
||||
const getData = async () => {
|
||||
const res = await comm.getMerchantInfo()
|
||||
userInfo.value = res.data.data
|
||||
}
|
||||
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await api.getList({
|
||||
pageNum: pagination.value.page,
|
||||
pageSize: pagination.value.pageSize,
|
||||
})
|
||||
data.value = res.data.data || []
|
||||
pagination.value.itemCount = res.data.total
|
||||
} catch (error) {
|
||||
$message.error(error.msg)
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
const ok = async () => {
|
||||
// if (formData.value.integral < 1000) return $message.warning('提现积分不能小于10000')
|
||||
const res = await api.apply({
|
||||
number: formData.value.integral,
|
||||
})
|
||||
$message.success(res.msg)
|
||||
clear()
|
||||
}
|
||||
|
||||
const clear = () => {
|
||||
formData.value.integral = null
|
||||
getList()
|
||||
getData()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup>
|
||||
import api from '../api'
|
||||
import Upload from '@/components/Upload.vue'
|
||||
import TiandituPicker from '@/components/TiandituPicker.vue'
|
||||
|
||||
onMounted(() => {
|
||||
getInfo()
|
||||
@@ -114,6 +115,13 @@ window.addEventListener('message', (res) => {
|
||||
showModal.value = false
|
||||
})
|
||||
|
||||
const confirm = (data) => {
|
||||
model.value.lt = `${data.latlng.lat},${data.latlng.lng}`
|
||||
model.value.lat = data.latlng.lat
|
||||
model.value.lon = data.latlng.lng
|
||||
showModal.value = false
|
||||
}
|
||||
|
||||
const submit = () => {
|
||||
formRef.value?.validate(async (errors) => {
|
||||
if (!errors) {
|
||||
@@ -214,19 +222,14 @@ const submit = () => {
|
||||
<!-- h5地图 -->
|
||||
<n-modal v-if="showModal" v-model:show="showModal">
|
||||
<n-card
|
||||
style="width: 600px; height: 600px"
|
||||
style="width: 600px; height: auto"
|
||||
title="查找地图位置"
|
||||
:bordered="false"
|
||||
size="huge"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<iframe
|
||||
src="https://apis.map.qq.com/tools/locpicker?type=1&key=S3GBZ-WR26O-IXNW2-SXBOD-LZXV6-WAFNO&referer=myapp"
|
||||
width="100%"
|
||||
height="100%"
|
||||
frameborder="0"
|
||||
></iframe>
|
||||
<TiandituPicker @confirm="confirm" @cancel="showModal = false" />
|
||||
</n-card>
|
||||
</n-modal>
|
||||
</CommonPage>
|
||||
|
||||
@@ -28,7 +28,6 @@ const columns = ref([
|
||||
slot: 'action',
|
||||
align: 'center',
|
||||
render: (row) => {
|
||||
console.log(row)
|
||||
return [
|
||||
h(
|
||||
NButton,
|
||||
@@ -112,6 +111,7 @@ const addUser = (type, row = {}) => {
|
||||
id: row.uid,
|
||||
url: row.avatarUrl,
|
||||
name: row.nickName,
|
||||
pres: JSON.parse(row.permission),
|
||||
}
|
||||
}
|
||||
showModal.value = true
|
||||
@@ -124,6 +124,12 @@ const model = ref({
|
||||
url: '',
|
||||
id: null,
|
||||
name: '',
|
||||
pres: {
|
||||
dd: false,
|
||||
tj: false,
|
||||
hx: false,
|
||||
tx: false,
|
||||
},
|
||||
})
|
||||
|
||||
const search = async () => {
|
||||
@@ -135,6 +141,12 @@ const search = async () => {
|
||||
id: res.data.data.uid,
|
||||
url: res.data.data.avatarUrl,
|
||||
name: res.data.data.nickName,
|
||||
pres: {
|
||||
dd: false,
|
||||
tj: false,
|
||||
hx: false,
|
||||
tx: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,6 +154,7 @@ const submit = async () => {
|
||||
if (!model.value.id) return $message.error('请绑定核销人员')
|
||||
const res = await api.bindUser({
|
||||
uid: model.value.id,
|
||||
permission: JSON.stringify(model.value.pres),
|
||||
})
|
||||
$message.success(res.msg)
|
||||
clear()
|
||||
@@ -153,6 +166,12 @@ const clear = () => {
|
||||
url: '',
|
||||
id: null,
|
||||
name: '',
|
||||
pres: {
|
||||
dd: false,
|
||||
tj: false,
|
||||
hx: false,
|
||||
tx: false,
|
||||
},
|
||||
}
|
||||
showModal.value = false
|
||||
get_list()
|
||||
@@ -206,9 +225,20 @@ const delVerifyUser = async (row) => {
|
||||
<img class="h-100 w-100 border rounded-5" :src="model.url || ''" />
|
||||
<div>{{ model.name }}</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="checkBox">+</div>
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi label="开启点单" :span="12">
|
||||
<n-switch v-model:value="model.pres.dd" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi label="开启核销" :span="12">
|
||||
<n-switch v-model:value="model.pres.hx" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi label="开启统计" :span="12">
|
||||
<n-switch v-model:value="model.pres.tj" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi label="开启提现" :span="12">
|
||||
<n-switch v-model:value="model.pres.tx" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi>
|
||||
<div m-auto>
|
||||
<n-button type="primary" @click="submit">提交</n-button>
|
||||
|
||||
@@ -9,18 +9,18 @@
|
||||
</n-statistic>
|
||||
</n-card> -->
|
||||
<n-card ml-10 w-200 rounded-5>
|
||||
<n-statistic label="总豆子" tabular-nums>
|
||||
<n-statistic label="用户豆子(留存)" tabular-nums>
|
||||
<n-number-animation :from="0" :to="cardData.pulse" />
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
<n-card ml-10 w-200 rounded-5>
|
||||
<n-statistic label="用户赢(积分)" tabular-nums>
|
||||
<n-statistic label="用户积分(留存)" tabular-nums>
|
||||
<n-number-animation :from="0" :to="cardData.win" />
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
</div>
|
||||
</n-col>
|
||||
<n-col :span="24" mt-10>
|
||||
<!-- <n-col :span="24" mt-10>
|
||||
<div>
|
||||
<span>筛选状态:</span>
|
||||
<n-radio-group v-model:value="queryData.status">
|
||||
@@ -32,7 +32,7 @@
|
||||
/>
|
||||
</n-radio-group>
|
||||
</div>
|
||||
</n-col>
|
||||
</n-col> -->
|
||||
<n-col :span="24">
|
||||
<div mt-10 flex items-center>
|
||||
<div w-100>关键字搜索:</div>
|
||||
@@ -162,12 +162,8 @@
|
||||
|
||||
<script setup>
|
||||
import api from './api'
|
||||
import {
|
||||
// NDropdown,
|
||||
NButton,
|
||||
NEllipsis,
|
||||
} from 'naive-ui'
|
||||
// import TheIcon from '@/components/icon/TheIcon.vue'
|
||||
import { NDropdown, NButton, NEllipsis } from 'naive-ui'
|
||||
import TheIcon from '@/components/icon/TheIcon.vue'
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
@@ -184,24 +180,24 @@ const queryData = ref({
|
||||
word: '',
|
||||
})
|
||||
|
||||
const songs = ref([
|
||||
{
|
||||
value: 1,
|
||||
label: '未使用',
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
label: '用户赢',
|
||||
},
|
||||
{
|
||||
value: 4,
|
||||
label: '用户输',
|
||||
},
|
||||
{
|
||||
value: 5,
|
||||
label: '已过期',
|
||||
},
|
||||
])
|
||||
// const songs = ref([
|
||||
// {
|
||||
// value: 1,
|
||||
// label: '未使用',
|
||||
// },
|
||||
// {
|
||||
// value: 3,
|
||||
// label: '用户赢',
|
||||
// },
|
||||
// {
|
||||
// value: 4,
|
||||
// label: '用户输',
|
||||
// },
|
||||
// {
|
||||
// value: 5,
|
||||
// label: '已过期',
|
||||
// },
|
||||
// ])
|
||||
|
||||
const selectOptions = ref([
|
||||
{
|
||||
@@ -252,65 +248,65 @@ const columns = ref([
|
||||
sorter: true,
|
||||
sortOrder: false,
|
||||
},
|
||||
{
|
||||
title: '赢积分',
|
||||
align: 'center',
|
||||
key: 'win',
|
||||
sorter: true,
|
||||
sortOrder: false,
|
||||
},
|
||||
{
|
||||
title: '用户豆子',
|
||||
align: 'center',
|
||||
key: 'pulse',
|
||||
sorter: true,
|
||||
sortOrder: false,
|
||||
},
|
||||
// {
|
||||
// title: '操作',
|
||||
// title: '赢积分',
|
||||
// align: 'center',
|
||||
// slot: 'action',
|
||||
// render(row) {
|
||||
// return [
|
||||
// h(
|
||||
// NDropdown,
|
||||
// {
|
||||
// trigger: 'click',
|
||||
// options: [
|
||||
// {
|
||||
// label: '用户详情',
|
||||
// key: 1,
|
||||
// },
|
||||
// ],
|
||||
// onSelect: (key) => {
|
||||
// switch (key) {
|
||||
// case 1:
|
||||
// openDrawer(row)
|
||||
// break
|
||||
// }
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// default: () =>
|
||||
// h(
|
||||
// NButton,
|
||||
// {
|
||||
// text: true,
|
||||
// iconPlacement: 'right',
|
||||
// },
|
||||
// {
|
||||
// default: () => '更多',
|
||||
// icon: () =>
|
||||
// h(TheIcon, {
|
||||
// icon: 'ant-design:down-outlined',
|
||||
// }),
|
||||
// }
|
||||
// ),
|
||||
// }
|
||||
// ),
|
||||
// ]
|
||||
// },
|
||||
// key: 'win',
|
||||
// sorter: true,
|
||||
// sortOrder: false,
|
||||
// },
|
||||
// {
|
||||
// title: '用户豆子',
|
||||
// align: 'center',
|
||||
// key: 'pulse',
|
||||
// sorter: true,
|
||||
// sortOrder: false,
|
||||
// },
|
||||
{
|
||||
title: '操作',
|
||||
align: 'center',
|
||||
slot: 'action',
|
||||
render(row) {
|
||||
return [
|
||||
h(
|
||||
NDropdown,
|
||||
{
|
||||
trigger: 'click',
|
||||
options: [
|
||||
{
|
||||
label: '用户详情',
|
||||
key: 1,
|
||||
},
|
||||
],
|
||||
onSelect: (key) => {
|
||||
switch (key) {
|
||||
case 1:
|
||||
openDrawer(row)
|
||||
break
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
default: () =>
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
text: true,
|
||||
iconPlacement: 'right',
|
||||
},
|
||||
{
|
||||
default: () => '更多',
|
||||
icon: () =>
|
||||
h(TheIcon, {
|
||||
icon: 'ant-design:down-outlined',
|
||||
}),
|
||||
}
|
||||
),
|
||||
}
|
||||
),
|
||||
]
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const data = ref([])
|
||||
|
||||
@@ -12,7 +12,7 @@ export default {
|
||||
component: () => import('./index.vue'),
|
||||
meta: {
|
||||
title: '工作台',
|
||||
icon: 'mdi:home',
|
||||
icon: 'mdi:index',
|
||||
order: 0,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -20,6 +20,14 @@ export default defineConfig(({ command, mode }) => {
|
||||
VITE_SENTRY,
|
||||
} = viteEnv
|
||||
|
||||
// 调试代理配置
|
||||
console.log('=== Vite代理配置调试 ===')
|
||||
console.log('VITE_USE_PROXY:', VITE_USE_PROXY)
|
||||
console.log('VITE_BASE_API:', VITE_BASE_API)
|
||||
console.log('VITE_ADMIN_API:', VITE_ADMIN_API)
|
||||
console.log('PROXY_CONFIG:', PROXY_CONFIG)
|
||||
console.log('所有环境变量:', viteEnv)
|
||||
|
||||
return {
|
||||
base: VITE_PUBLIC_PATH || '/',
|
||||
resolve: {
|
||||
@@ -34,12 +42,12 @@ export default defineConfig(({ command, mode }) => {
|
||||
https: false,
|
||||
port: VITE_PORT,
|
||||
open: false,
|
||||
proxy: VITE_USE_PROXY
|
||||
? {
|
||||
[VITE_BASE_API]: PROXY_CONFIG[VITE_BASE_API],
|
||||
[VITE_ADMIN_API]: PROXY_CONFIG[VITE_ADMIN_API],
|
||||
}
|
||||
: undefined,
|
||||
proxy: {
|
||||
'/api1': PROXY_CONFIG['/api1'],
|
||||
'/api': PROXY_CONFIG['/api'],
|
||||
'/admin1': PROXY_CONFIG['/admin1'],
|
||||
'/admin': PROXY_CONFIG['/admin'],
|
||||
},
|
||||
},
|
||||
build: {
|
||||
target: 'es2015',
|
||||
|
||||
Reference in New Issue
Block a user