| | |
| | | import { useRoute, useRouter } from 'vue-router' |
| | | import { ArrowLeft, Check, Plus, Upload } from '@element-plus/icons-vue' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { getGuideUserDetail, updateGuideUserDetail, updateGuideWhitelist } from '@/api/users' |
| | | import { |
| | | addGuideScenicConfig, |
| | | deleteGuideScenicConfig, |
| | | getGuideUserDetail, |
| | | updateGuideUserDetail, |
| | | updateGuideWhitelist, |
| | | } from '@/api/users' |
| | | import { getScenicSpots } from '@/api/scenicSpots' |
| | | |
| | | const route = useRoute() |
| | | const router = useRouter() |
| | | const loading = ref(false) |
| | | const saving = ref(false) |
| | | const user = ref(null) |
| | | const scenicOptions = ref([]) |
| | | const scenicPopoverVisible = ref(false) |
| | | const selectedScenicSpotId = ref(null) |
| | | const scenicSaving = ref(false) |
| | | const deletingScenicConfigId = ref(null) |
| | | |
| | | const documentImages = computed(() => [ |
| | | { key: 'idCardFrontUrl', label: '身份证(正)*' }, |
| | |
| | | const formatDateTime = (value) => value ? value.replace('T', ' ').slice(0, 19) : '-' |
| | | const displayValue = (value) => value === null || value === undefined || value === '' ? '-' : value |
| | | const guideStatusLabel = (status) => ({ 0: '禁用', 1: '启用', 2: '待审核', 3: '已注销' }[status] || '未知') |
| | | const availableScenicOptions = computed(() => { |
| | | const configuredNames = new Set((user.value?.scenicConfigs || []).map((item) => item.scenicName)) |
| | | return scenicOptions.value.filter((item) => !configuredNames.has(item.scenicName)) |
| | | }) |
| | | |
| | | const loadDetail = async () => { |
| | | loading.value = true |
| | |
| | | } |
| | | } |
| | | |
| | | const loadScenicOptions = async () => { |
| | | const result = await getScenicSpots({ page: 1, size: 100, status: 1 }) |
| | | scenicOptions.value = result.records || [] |
| | | } |
| | | |
| | | const updateWhitelist = async (enabled) => { |
| | | await updateGuideWhitelist(user.value.id, enabled) |
| | | ElMessage.success(enabled ? '已加入自动禁用白名单' : '已移出自动禁用白名单') |
| | | } |
| | | |
| | | const addScenic = async () => { |
| | | const openScenicSelector = () => { |
| | | selectedScenicSpotId.value = null |
| | | if (!availableScenicOptions.value.length) { |
| | | ElMessage.info('暂无可添加的启用景区') |
| | | return |
| | | } |
| | | scenicPopoverVisible.value = true |
| | | } |
| | | |
| | | const saveSelectedScenic = async (scenicSpotId) => { |
| | | if (!scenicSpotId || scenicSaving.value) return |
| | | scenicSaving.value = true |
| | | try { |
| | | const { value: scenicName } = await ElMessageBox.prompt('请输入景区名称', '添加景区配置', { inputValidator: (value) => !!value?.trim() || '景区名称不能为空' }) |
| | | const { value: cityName } = await ElMessageBox.prompt('请输入城市名称', '添加景区配置') |
| | | user.value.scenicConfigs.push({ scenicName: scenicName.trim(), cityName: cityName?.trim() || '' }) |
| | | user.value.scenicConfigs = await addGuideScenicConfig(user.value.id, scenicSpotId) |
| | | scenicPopoverVisible.value = false |
| | | selectedScenicSpotId.value = null |
| | | ElMessage.success('景区配置已保存') |
| | | } catch (error) { |
| | | selectedScenicSpotId.value = null |
| | | throw error |
| | | } finally { |
| | | scenicSaving.value = false |
| | | } |
| | | } |
| | | |
| | | const removeScenic = async (config) => { |
| | | try { |
| | | await ElMessageBox.confirm(`确认删除“${config.scenicName}”配置吗?`, '删除景区配置', { |
| | | type: 'warning', |
| | | confirmButtonText: '删除', |
| | | cancelButtonText: '取消', |
| | | }) |
| | | deletingScenicConfigId.value = config.id |
| | | try { |
| | | user.value.scenicConfigs = await deleteGuideScenicConfig(user.value.id, config.id) |
| | | ElMessage.success('景区配置已删除') |
| | | } finally { |
| | | deletingScenicConfigId.value = null |
| | | } |
| | | } catch (error) { |
| | | if (error !== 'cancel' && error !== 'close') throw error |
| | | } |
| | |
| | | qualificationCertificateUrl: user.value.qualificationCertificateUrl, |
| | | teamId: user.value.teamId, |
| | | wechatOpenId: user.value.wechatOpenId, |
| | | scenicConfigs: user.value.scenicConfigs, |
| | | }) |
| | | ElMessage.success('导游详情保存成功') |
| | | } finally { |
| | |
| | | query: { guideId: user.value.id, guideName: user.value.realName }, |
| | | }) |
| | | |
| | | onMounted(loadDetail) |
| | | onMounted(() => Promise.all([loadDetail(), loadScenicOptions()])) |
| | | </script> |
| | | |
| | | <template> |
| | |
| | | <el-table-column prop="scenicName" label="景区名称" align="center" /> |
| | | <el-table-column prop="cityName" label="城市" align="center" /> |
| | | <el-table-column label="操作" align="center"> |
| | | <template #default="{ $index }"><el-button link type="danger" @click="user.scenicConfigs.splice($index, 1)">删除</el-button></template> |
| | | <template #default="{ row }"> |
| | | <el-button |
| | | link |
| | | type="danger" |
| | | :loading="deletingScenicConfigId === row.id" |
| | | @click="removeScenic(row)" |
| | | >删除</el-button> |
| | | </template> |
| | | </el-table-column> |
| | | <template #empty><span class="empty-text">暂无景区配置</span></template> |
| | | </el-table> |
| | | <el-button class="add-scenic" type="primary" :icon="Plus" @click="addScenic">添加</el-button> |
| | | <el-popover v-model:visible="scenicPopoverVisible" placement="bottom-end" :width="320" trigger="manual"> |
| | | <el-select |
| | | v-model="selectedScenicSpotId" |
| | | class="scenic-selector" |
| | | filterable |
| | | placeholder="请选择景区,选择后自动保存" |
| | | :loading="scenicSaving" |
| | | @change="saveSelectedScenic" |
| | | > |
| | | <el-option |
| | | v-for="item in availableScenicOptions" |
| | | :key="item.id" |
| | | :label="`${item.scenicName}(${item.cityName || '未配置城市'})`" |
| | | :value="item.id" |
| | | /> |
| | | </el-select> |
| | | <template #reference> |
| | | <el-button class="add-scenic" type="primary" :icon="Plus" @click="openScenicSelector">添加</el-button> |
| | | </template> |
| | | </el-popover> |
| | | </div> |
| | | |
| | | <div class="whitelist-row"> |
| | |
| | | .scenic-table { margin-left: 86px; } |
| | | .empty-text { color: #9aa5b1; } |
| | | .add-scenic { display: block; margin: 12px 0 0 auto; } |
| | | .scenic-selector { width: 100%; } |
| | | .whitelist-row { display: flex; align-items: center; gap: 18px; margin-top: 22px; } |
| | | .collection-section { min-height: 150px; } |
| | | .section-heading { display: flex; align-items: baseline; gap: 24px; } |