diff --git a/src/utils/config.js b/src/utils/config.js
index c3b681f..a2e80e9 100644
--- a/src/utils/config.js
+++ b/src/utils/config.js
@@ -25,34 +25,33 @@ export const { baseURL, uploadURL } = config[env]
* @returns {string} 完整的图片URL
*/
export const getImageUrl = (imagePath) => {
- const cleanBaseURL = baseURL.replace(/\/$/, '')
+ // console.log('getImageUrl called with:', imagePath)
if (!imagePath) return ''
if (imagePath.startsWith('http')) return imagePath
+ console.log(imagePath,'imagePath');
- // 在开发环境下,使用代理路径
+ // 如果图片路径以/uploads开头,直接返回原路径
+ if (imagePath.startsWith('/uploads')) {
+ const cleanBaseURL = baseURL.replace(/\/$/, '')
+ // console.log('Image starts with /uploads, returning original path:', imagePath)
+ return `${imagePath}`
+ }
+
+ // 在开发环境下,也需要根据路径前缀处理
if (env === 'development') {
- // 如果路径已经包含uploads,直接使用
- if (imagePath.startsWith('/uploads/') || imagePath.startsWith('uploads/')) {
- const cleanPath = imagePath.startsWith('/') ? imagePath : `/${imagePath}`
- return cleanPath
- }
- // 否则添加uploads前缀
- return `${cleanBaseURL}${imagePath}`
+ const cleanBaseURL = baseURL.replace(/\/$/, '')
+ const cleanImagePath = imagePath.startsWith('/') ? imagePath : `/${imagePath}`
+ const fullUrl = `${cleanBaseURL}${cleanImagePath}`
+ // console.log('Development environment, returning:', fullUrl)
+ return fullUrl
}
// 生产环境下使用完整URL
-
- let cleanImagePath
+ const cleanBaseURL = baseURL.replace(/\/$/, '')
+ const cleanImagePath = imagePath.startsWith('/') ? imagePath : `/${imagePath}`
+ const fullUrl = `${cleanBaseURL}${cleanImagePath}`
- // 如果路径已经包含uploads,直接使用
- if (imagePath.startsWith('/uploads/') || imagePath.startsWith('uploads/')) {
- cleanImagePath = imagePath.startsWith('/') ? imagePath : `/${imagePath}`
- } else {
- // 否则添加uploads前缀
- cleanImagePath = `/uploads/${imagePath}`
- }
-
- return `${cleanBaseURL}${cleanImagePath}`
+ return fullUrl
}
/**
diff --git a/src/views/Users.vue b/src/views/Users.vue
index 33dd0d4..0a59652 100644
--- a/src/views/Users.vue
+++ b/src/views/Users.vue
@@ -55,39 +55,17 @@
-
-
+
-
-
-
-
-
-
-
-
-
+ @change="handleSearchRegionChange"
+ />
@@ -146,17 +124,11 @@
-
-
-
-
+
-
- {{ row.role === 'admin' ? '管理员' : '用户' }}
-
+ {{ row.province_name || '-' }}
-
{{ row.city_name || '-' }}
@@ -379,38 +351,18 @@
-
-
-
+
+
-
-
-
-
-
-
-
-
-
+ @change="handleUserFormRegionChange"
+ />
@@ -581,13 +533,13 @@ const dialogVisible = ref(false)
const isEdit = ref(false)
const users = ref([])
const regions = ref([])
-const cities = ref([])
-const districts = ref([])
+const regionOptions = ref([])
// 搜索表单
const searchForm = reactive({
keyword: '',
role: '',
+ region: [],
city: '',
district: '',
sort: 'created_at_desc'
@@ -600,33 +552,11 @@ const pagination = reactive({
total: 0
})
-// 地区相关计算属性
-const availableCities = computed(() => {
- const citySet = new Set()
- ;(regions.value || []).forEach(region => {
- if (region.city_name) {
- citySet.add(region.city_name)
- }
- })
- return Array.from(citySet).sort()
-})
-
-const availableDistricts = computed(() => {
- if (!userForm.city) return []
- return (regions.value || []).filter(region => region.city_name === userForm.city)
-})
-
-const searchDistricts = computed(() => {
- if (!searchForm.city) return []
- return (regions.value || []).filter(region => region.city_name === searchForm.city)
-})
-
// 用户表单
const userFormRef = ref()
const userForm = reactive({
id: null,
username: '',
-
password: '',
role: 'user',
accountType: 'normal', // 账户类型:normal-普通商户,virtual-虚拟商户
@@ -638,6 +568,7 @@ const userForm = reactive({
alipayQr: '',
bankCard: '',
unionpayQr: '',
+ region: [],
city: '',
districtId: null,
idCardFront: '',
@@ -670,6 +601,9 @@ const userRules = computed(() => ({
],
phone: [
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号格式', trigger: 'blur' }
+ ],
+ region: [
+ { required: true, message: '请选择省市区', trigger: 'change' }
]
}))
@@ -704,19 +638,114 @@ const getRegions = async () => {
}
}
+// 加载级联选择器数据
+const loadRegionOptions = async () => {
+ try {
+ // 获取所有省份
+ const provincesResponse = await api.get('/regions/provinces')
+ console.log('获取省份数据:', provincesResponse)
+ if (!provincesResponse.data.success) {
+ throw new Error(provincesResponse.data.message || '获取省份数据失败')
+ }
+
+ regionOptions.value = provincesResponse.data.data || []
+
+ } catch (error) {
+ console.error('获取省市区数据失败:', error)
+ ElMessage.error(error.message || '获取省市区数据失败')
+
+ // 如果API获取失败,使用浙江省数据作为默认数据
+ await loadFallbackRegionData()
+ }
+}
+
+// 回退方案:加载浙江省数据
+const loadFallbackRegionData = async () => {
+ try {
+ const zhejiangResponse = await axios.get('/api/regions/zhejiang')
+ if (zhejiangResponse.data.success) {
+ const zhejiangData = zhejiangResponse.data.data || []
+ // 将浙江省数据转换为级联选择器格式
+ const cityMap = new Map()
+
+ zhejiangData.forEach(item => {
+ if (!cityMap.has(item.city_name)) {
+ cityMap.set(item.city_name, {
+ value: item.city_name,
+ label: item.city_name,
+ children: []
+ })
+ }
+
+ // 添加区县数据
+ if (item.district_name) {
+ cityMap.get(item.city_name).children.push({
+ value: item.district_name,
+ label: item.district_name
+ })
+ }
+ })
+
+ regionOptions.value = [{
+ value: '浙江省',
+ label: '浙江省',
+ children: Array.from(cityMap.values())
+ }]
+ console.log('已加载浙江省地区数据作为默认选项')
+ } else {
+ throw new Error('获取浙江省数据也失败')
+ }
+ } catch (fallbackError) {
+ console.error('浙江省数据获取失败,使用硬编码数据:', fallbackError)
+ // 最终回退到硬编码数据
+ regionOptions.value = [
+ {
+ value: '浙江省',
+ label: '浙江省',
+ children: [
+ {
+ value: '宁波市',
+ label: '宁波市',
+ children: [
+ { value: '鄞州区', label: '鄞州区' },
+ { value: '海曙区', label: '海曙区' },
+ { value: '江北区', label: '江北区' },
+ { value: '北仑区', label: '北仑区' }
+ ]
+ },
+ {
+ value: '杭州市',
+ label: '杭州市',
+ children: [
+ { value: '西湖区', label: '西湖区' },
+ { value: '上城区', label: '上城区' },
+ { value: '拱墅区', label: '拱墅区' },
+ { value: '余杭区', label: '余杭区' }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+}
+
// 获取用户列表
const fetchUsers = async () => {
loading.value = true
try {
const [sortField, sortOrder] = searchForm.sort.split('_')
+ // 从级联选择器中提取省市区信息
+ const [provinceName, cityName, districtName] = searchForm.region || []
+
const params = {
page: pagination.page,
limit: pagination.limit,
search: searchForm.keyword,
role: searchForm.role,
- city: searchForm.city,
- district: searchForm.district,
+ province: provinceName || '',
+ city: cityName || '',
+ district: districtName || '',
sort: sortField,
order: sortOrder
}
@@ -746,6 +775,7 @@ const handleSearch = () => {
const handleReset = () => {
searchForm.keyword = ''
searchForm.role = ''
+ searchForm.region = []
searchForm.city = ''
searchForm.district = ''
searchForm.sort = 'created_at_desc'
@@ -753,13 +783,36 @@ const handleReset = () => {
fetchUsers()
}
-// 搜索表单城市变化处理
-const onCityChange = () => {
- searchForm.district = ''
- // 不再自动触发搜索,需要用户点击搜索按钮
+// 搜索表单地区变化处理
+const handleSearchRegionChange = (value) => {
+ console.log('搜索地区变化:', value)
+ if (value && value.length >= 2) {
+ searchForm.city = value[1] // 城市
+ searchForm.district = value.length >= 3 ? value[2] : '' // 区县
+ } else {
+ searchForm.city = ''
+ searchForm.district = ''
+ }
}
-// 用户表单城市变化处理
+// 用户表单地区变化处理
+const handleUserFormRegionChange = (value) => {
+ console.log('用户表单地区变化:', value)
+ if (value && value.length === 3) {
+ userForm.city = value[1]
+ userForm.districtId = value[2]
+ } else {
+ userForm.city = ''
+ userForm.districtId = null
+ }
+}
+
+// 搜索表单城市变化处理(保留兼容性)
+const onCityChange = () => {
+ searchForm.district = ''
+}
+
+// 用户表单城市变化处理(保留兼容性)
const onUserFormCityChange = () => {
userForm.districtId = null
}
@@ -793,13 +846,21 @@ const showCreateDialog = () => {
dialogVisible.value = true
}
+
+
// 显示编辑对话框
const showEditDialog = (user) => {
isEdit.value = true
+
+ // 构建级联选择器的值 - 直接使用中文名称
+ const regionValue = []
+ if (user.province_name) regionValue.push(user.province_name)
+ if (user.city_name) regionValue.push(user.city_name)
+ if (user.district_name) regionValue.push(user.district_name)
+
Object.assign(userForm, {
id: user.id,
username: user.username,
-
password: '',
role: user.role,
accountType: user.is_system_account ? 'virtual' : 'normal',
@@ -811,6 +872,7 @@ const showEditDialog = (user) => {
alipayQr: user.alipay_qr || '',
bankCard: user.bank_card || '',
unionpayQr: user.unionpay_qr || '',
+ region: user.region,
city: user.city || '',
districtId: user.district_id || null,
idCardFront: user.id_card_front || '',
@@ -827,9 +889,11 @@ const handleSubmit = async () => {
submitting.value = true
+ // 从级联选择器中提取省市区信息
+ const [provinceName, cityName, districtName] = userForm.region || []
+
const userData = {
username: userForm.username,
-
role: userForm.role,
isSystemAccount: userForm.accountType === 'virtual', // 转换为后端字段
avatar: userForm.avatar,
@@ -840,6 +904,8 @@ const handleSubmit = async () => {
alipayQr: userForm.alipayQr,
bankCard: userForm.bankCard,
unionpayQr: userForm.unionpayQr,
+ province: provinceName || '',
+ districtName: districtName || '',
city: userForm.city,
districtId: userForm.districtId,
idCardFront: userForm.idCardFront,
@@ -914,6 +980,7 @@ const resetUserForm = () => {
alipayQr: '',
bankCard: '',
unionpayQr: '',
+ region: [],
city: '',
districtId: null,
idCardFront: '',
@@ -1028,6 +1095,7 @@ const formatPoints = (points) => {
onMounted(() => {
fetchUsers()
getRegions()
+ loadRegionOptions()
})
diff --git a/vite.config.js b/vite.config.js
index 125e8ff..4ffd7ec 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -4,8 +4,8 @@ import { resolve } from 'path'
// https://vitejs.dev/config/
export default defineConfig({
- // base: '/admin',
- base: '/',
+ base: '/admin',
+ // base: '/',
plugins: [vue()],
resolve: {
alias: {
@@ -19,10 +19,10 @@ export default defineConfig({
target: 'http://localhost:3000',
changeOrigin: true
},
- // '/admin': {
- // target: 'http://localhost:3000',
- // changeOrigin: true
- // },
+ '/admin': {
+ target: 'http://localhost:3000',
+ changeOrigin: true
+ },
'/uploads': {
target: 'http://localhost:3000',
changeOrigin: true