This commit is contained in:
2025-09-08 11:53:34 +08:00
parent 70add452c7
commit 55c855936c
3 changed files with 196 additions and 129 deletions

View File

@@ -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
}
/**

View File

@@ -55,39 +55,17 @@
</el-col>
<!-- 第二行地区筛选 -->
<el-col :xs="24" :sm="12" :md="8" :lg="6">
<el-select
v-model="searchForm.city"
placeholder="选择城市"
clearable
<el-col :xs="24" :sm="12" :md="8" :lg="6" style="margin-top: 10px;">
<el-cascader
v-model="searchForm.region"
:options="regionOptions"
placeholder="请选择省市区"
style="width: 100%"
@change="onCityChange"
>
<el-option label="全部" value="" />
<el-option
v-for="city in availableCities"
:key="city"
:label="city"
:value="city"
/>
</el-select>
</el-col>
<el-col :xs="24" :sm="12" :md="8" :lg="6">
<el-select
v-model="searchForm.district"
placeholder="选择地区"
:props="{ expandTrigger: 'hover'}"
:show-all-levels="true"
clearable
style="width: 100%"
:disabled="!searchForm.city"
>
<el-option label="全部" value="" />
<el-option
v-for="district in searchDistricts"
:key="district.id"
:label="district.district_name"
:value="district.id"
/>
</el-select>
@change="handleSearchRegionChange"
/>
</el-col>
</el-row>
</el-card>
@@ -146,17 +124,11 @@
</div>
</template>
</el-table-column>
<el-table-column label="角色" width="100">
<el-table-column label="省份" width="100">
<template #default="{ row }">
<el-tag :type="row.role === 'admin' ? 'danger' : 'primary'" size="small">
{{ row.role === 'admin' ? '管理员' : '用户' }}
</el-tag>
<span>{{ row.province_name || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="城市" width="100">
<template #default="{ row }">
<span>{{ row.city_name || '-' }}</span>
@@ -379,38 +351,18 @@
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="城市" prop="city">
<el-select
v-model="userForm.city"
placeholder="请选择城市"
<el-col :span="24">
<el-form-item label="省市区" prop="region">
<el-cascader
v-model="userForm.region"
:options="regionOptions"
placeholder="请选择省市区"
style="width: 100%"
:props="{ value:'code'}"
:show-all-levels="true"
clearable
@change="onUserFormCityChange"
>
<el-option
v-for="city in availableCities"
:key="city"
:label="city"
:value="city"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="地区" prop="districtId">
<el-select
v-model="userForm.districtId"
placeholder="请选择地区"
clearable
:disabled="!userForm.city"
>
<el-option
v-for="district in availableDistricts"
:key="district.id"
:label="district.district_name"
:value="district.id"
/>
</el-select>
@change="handleUserFormRegionChange"
/>
</el-form-item>
</el-col>
</el-row>
@@ -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()
})
</script>

View File

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