5 Commits
main ... dev

Author SHA1 Message Date
ec20dc87aa ci(other): ci脚本换源 2025-11-05 11:48:59 +08:00
23df45af5d ci(other): ci update 2025-10-31 18:07:45 +08:00
9a7b4dc1bf feat(custom): 多API版本
All checks were successful
continuous-integration/drone/push Build is passing
2025-09-23 18:25:27 +08:00
aa8e3100a2 refactor(custom): 地图服务商更换
All checks were successful
continuous-integration/drone/push Build is passing
2025-06-16 17:56:39 +08:00
41ee5a03dd refactor(custom): 地图Key变更
All checks were successful
continuous-integration/drone/push Build is passing
2025-05-29 15:57:07 +08:00
31 changed files with 4553 additions and 6721 deletions

View File

@@ -1,158 +0,0 @@
kind: pipeline
type: docker
name: default
platform:
os: linux
arch: amd64
steps:
- name: 测试服-依赖安装&&编译打包
pull: if-not-exists
image: node:20-alpine
when:
branch:
- test
commands:
- npm config set registry https://registry.npmmirror.com/
- npm install -g pnpm
- pnpm install
- pnpm build:test
- rm -rf dist.tar
- rm -rf node_modules
- tar -zcvf dist.tar ./dist ./default.conf ./Dockerfile
- name: 正式服-依赖安装&&编译打包
pull: if-not-exists
image: node:20-alpine
when:
branch:
- main
commands:
- npm config set registry https://registry.npmmirror.com/
- npm install -g pnpm
- pnpm install
- pnpm build:prod
- rm -rf dist.tar
- rm -rf node_modules
- tar -zcvf dist.tar ./dist ./default.conf ./Dockerfile
- name: 测试服-产物上传
pull: if-not-exists
image: appleboy/drone-scp
when:
branch:
- test
settings:
host:
from_secret: HOST_DEV
username:
from_secret: USER_DEV
password:
from_secret: PWD_DEV
port: 22
strip_components: 1
target: /www/builder
source:
- ./dist.tar
- name: 测试服-部署
pull: if-not-exists
image: appleboy/drone-ssh
when:
branch:
- test
settings:
host:
from_secret: HOST_DEV
username:
from_secret: USER_DEV
password:
from_secret: PWD_DEV
port: 22
script:
- cd /www/builder
- mkdir jdt-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
- docker rm jdt-mer-dev
- docker run -d -p 8083:80 --restart=always --name jdt-mer-dev jdt-mer-dev
- cd ..
- rm -rf jdt-mer-dev
- name: 正式服-产物上传
pull: if-not-exists
image: appleboy/drone-scp
when:
branch:
- main
settings:
host:
from_secret: HOST_PROD
username:
from_secret: USER_PROD
password:
from_secret: PWD_PROD
port: 22
strip_components: 1
target: /www/builder
source:
- ./dist.tar
- name: 正式服-部署
pull: if-not-exists
image: appleboy/drone-ssh
when:
branch:
- main
settings:
host:
from_secret: HOST_PROD
username:
from_secret: USER_PROD
password:
from_secret: PWD_PROD
port: 22
script:
- cd /www/builder
- mkdir jdt-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
- docker rm jdt-mer-prod
- docker run -d -p 8083:80 --restart=always --name jdt-mer-prod jdt-mer-prod
- cd ..
- rm -rf jdt-mer-prod
- name: 企业微信通知
pull: if-not-exists
image: plugins/webhook
when:
branch:
- test
- main
status:
- success
- failure
settings:
urls: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=a2065e21-4f92-4f5b-a432-2c0cd1d965b5
content_type: application/json
template: |
{
"msgtype": "markdown",
"markdown": {
"content": "{{#success build.status}}✅{{else}}❌{{/success}}**{{ repo.owner }}/{{ repo.name }}** (Build #{{build.number}})\n
>**构建结果**: {{ build.status }}
>**构建详情**: [点击查看]({{ build.link }})
>**代码分支**: {{ build.branch }}
>**提交标识**: {{ build.commit }}
>**提交发起**: {{ build.author }}
>**提交信息**: {{ build.message }}
"
}
}

View File

@@ -8,6 +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_ADMIN_API_1='/admin1'

View File

@@ -6,8 +6,10 @@ 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

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

@@ -0,0 +1,162 @@
name: CI Build & Deploy
on:
push:
branches:
- test
- main
jobs:
build-and-deploy-dev:
if: gitea.ref_name == 'test'
runs-on: gitea_act_runner
container:
image: node:24-alpine
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 8
- name: Install deps
run: |
npm config set registry https://registry.npmmirror.com/
pnpm install
- name: Build (test)
run: pnpm build:test
- name: Pack artifacts
run: |
rm -rf dist.tar
tar -zcvf dist.tar ./dist ./default.conf ./Dockerfile
- name: Upload artifacts to server
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.HOST_DEV }}
username: ${{ secrets.USER_DEV }}
password: ${{ secrets.PWD_DEV }}
port: 22
source: 'dist.tar'
target: '/www/builder'
strip_components: 0
- name: Deploy over SSH
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.HOST_DEV }}
username: ${{ secrets.USER_DEV }}
password: ${{ secrets.PWD_DEV }}
port: 22
script: |
set -e
cd /www/builder
rm -rf jdt-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
View File

@@ -1,5 +0,0 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/

View File

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

12
.idea/jdt-mer.iml generated
View File

@@ -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
View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/jdt-mer.iml" filepath="$PROJECT_DIR$/.idea/jdt-mer.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated
View File

@@ -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
View File

@@ -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.

View File

@@ -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'), ''),
// },
}

View File

@@ -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),
},
}
},
},
]

View File

@@ -1,5 +0,0 @@
import auth from './auth'
import user from './user'
import post from './post'
export default [...auth, ...user, ...post]

View File

@@ -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,
},
}
},
},
]

View File

@@ -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),
},
}
},
},
]

View File

@@ -1,6 +0,0 @@
import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer'
import api from './api'
export function setupProdMockServer() {
createProdMockServer(api)
}

View File

@@ -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 ''
}

View File

@@ -33,50 +33,50 @@
},
"dependencies": {
"@unocss/eslint-config": "^0.55.7",
"@vueuse/core": "^10.9.0",
"@vueuse/core": "^10.11.1",
"@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "5.1.12",
"axios": "^1.6.8",
"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.13.0",
"md-editor-v3": "^4.21.3",
"mockjs": "^1.1.0",
"pinia": "^2.1.7",
"vite": "^4.5.3",
"pinia": "^2.3.1",
"vite": "^4.5.14",
"vue": "3.3.4",
"vue-echarts": "^6.6.9",
"vue-router": "^4.3.0",
"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.199",
"@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.6.2",
"@vue/compiler-sfc": "^3.4.21",
"@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.4.5",
"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.38.1",
"rollup-plugin-visualizer": "^5.12.0",
"sass": "^1.75.0",
"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.2",
"vite-plugin-mkcert": "^1.17.5",
"vite-plugin-mkcert": "^1.17.9",
"vite-plugin-mock": "2.9.6",
"vite-plugin-svg-icons": "^2.0.1"
},

9668
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View 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>

View File

@@ -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()

View File

@@ -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
})

View File

@@ -42,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
}
}

View File

@@ -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
View 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
}
}

View File

@@ -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(),
}

View File

@@ -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

View File

@@ -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,
}),
}

View File

@@ -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

View File

@@ -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>

View File

@@ -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',