Compare commits

...

28 Commits

Author SHA1 Message Date
c2b0e49f7b feat(custom): 多API版本
All checks were successful
continuous-integration/drone/push Build is passing
2025-09-23 18:31:11 +08:00
e791fef430 refactor(custom): 地图服务商更换 2025-09-23 18:31:10 +08:00
3b9cee3a65 refactor(custom): 地图Key变更 2025-09-23 18:31:10 +08:00
68e02ecc84 refactor(custom): 地图服务商更换
All checks were successful
continuous-integration/drone/push Build is passing
2025-06-16 18:02:55 +08:00
646ba7165f refactor(custom): 地图Key变更
All checks were successful
continuous-integration/drone/push Build is passing
2025-05-29 15:57:46 +08:00
6c69a1b296 Merge branch 'test'
All checks were successful
continuous-integration/drone/push Build is passing
2024-10-18 19:49:16 +08:00
64d1923336 Merge branch 'test'
All checks were successful
continuous-integration/drone/push Build is passing
2024-09-12 01:43:43 +08:00
c5f299d4aa Merge branch 'test'
All checks were successful
continuous-integration/drone/push Build is passing
2024-09-06 21:39:30 +08:00
0c47b09064 Merge branch 'test'
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-05 18:10:50 +08:00
630dad47dc Merge branch 'test'
All checks were successful
continuous-integration/drone/push Build is passing
2024-08-05 05:35:10 +08:00
ca60223245 Merge branch 'test'
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-31 21:04:43 +08:00
537e155050 Merge branch 'test'
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-28 10:47:13 +08:00
627d618b65 Merge branch 'test'
All checks were successful
continuous-integration/drone/push Build is passing
2024-07-22 18:23:19 +08:00
85b592adec Merge branch 'test'
All checks were successful
continuous-integration/drone/push Build is passing
2024-05-20 16:08:28 +08:00
8751b78d8d Merge branch 'test'
All checks were successful
continuous-integration/drone/push Build is passing
2024-05-17 18:49:08 +08:00
e7e050d107 Merge branch 'test'
All checks were successful
continuous-integration/drone/push Build is passing
2024-05-17 18:16:09 +08:00
c563bf8cd6 Merge branch 'test'
All checks were successful
continuous-integration/drone/push Build is passing
2024-03-15 16:09:37 +08:00
528c405f63 Merge branch 'test' 2024-01-27 14:13:47 +08:00
aa3220186e Merge branch 'test' 2023-12-25 17:05:52 +08:00
936fbca87e Merge branch 'test' 2023-12-15 20:48:25 +08:00
462047d560 Merge branch 'test' 2023-12-13 13:21:21 +08:00
5cf8469009 Merge branch 'test' 2023-12-03 22:24:39 +08:00
66589f52b3 Merge branch 'test' 2023-12-03 21:37:54 +08:00
7d1cc49a6d mod(custom): merge branch 'test' 2023-11-29 15:38:06 +08:00
8bf7cc8a31 Merge branch 'test' into 'main'
Merge branch 'dev' into 'test'

See merge request xinling/jdt-mer!5
2023-10-10 18:06:39 +08:00
1b2a0d7bc6 Revert "Merge branch 'dev' into 'main'"
This reverts merge request !3
2023-10-10 18:05:21 +08:00
58deecbf36 Merge branch 'dev' into 'main'
fix(custom): 隐藏添加/编辑商品的log打印

See merge request xinling/jdt-mer!3
2023-10-10 18:03:41 +08:00
0a4169d750 Merge branch 'test' into 'main'
fix(custom): 修复Upload组件上传成功但未取到值的问题

See merge request xinling/jdt-mer!2
2023-10-10 16:03:15 +08:00
20 changed files with 886 additions and 97 deletions

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

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>

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

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