| | |
| | | import { useRoute, useRouter } from 'vue-router' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { ArrowLeft, Edit, Plus } from '@element-plus/icons-vue' |
| | | import { createGuideTeam, getGuideTeamDetail, updateGuideTeam } from '@/api/guideTeams' |
| | | import { |
| | | createGuideTeam, |
| | | getGuideTeamCollectionGuideOptions, |
| | | getGuideTeamDetail, |
| | | updateGuideTeam, |
| | | } from '@/api/guideTeams' |
| | | import { getScenicSpotDetail, getScenicSpots } from '@/api/scenicSpots' |
| | | |
| | | const route = useRoute() |
| | | const router = useRouter() |
| | | const loading = ref(false) |
| | | const saving = ref(false) |
| | | const collectionGuideLoading = ref(false) |
| | | const collectionGuideOptions = ref([]) |
| | | const formRef = ref() |
| | | const scenicFormRef = ref() |
| | | const scenicDialogVisible = ref(false) |
| | | const scenicEditingIndex = ref(-1) |
| | | const scenicOptionsLoading = ref(false) |
| | | const scenicDetailLoading = ref(false) |
| | | const scenicOptions = ref([]) |
| | | const meetingPointOptions = ref([]) |
| | | const isCreate = computed(() => route.name === 'guide-team-create') |
| | | const isEdit = computed(() => route.name === 'guide-team-edit') |
| | | const readonly = computed(() => route.name === 'guide-team-detail') |
| | | const pageTitle = computed(() => isCreate.value ? '新增讲解队' : isEdit.value ? '编辑讲解队' : '讲解队详情') |
| | | const form = reactive({ |
| | | id: null, teamName: '', contactPhone: '', createdAt: '', companyName: '', |
| | | unifiedSocialCreditCode: '', commissionRatio: null, wechatOpenId: '', merchantId: '', scenicConfigs: [], |
| | | unifiedSocialCreditCode: '', commissionRatio: null, wechatOpenId: '', merchantId: '', |
| | | balance: 0, collectionGuideId: null, scenicConfigs: [], |
| | | }) |
| | | const scenicForm = reactive({ scenicName: '', cityName: '', meetingPoints: '' }) |
| | | const scenicForm = reactive({ scenicSpotId: null, scenicName: '', cityName: '', meetingPointNames: [] }) |
| | | const rules = { |
| | | teamName: [ |
| | | { required: true, message: '请输入讲解队名称', trigger: 'blur' }, |
| | |
| | | ], |
| | | } |
| | | const scenicRules = { |
| | | scenicName: [{ required: true, message: '请输入景区名称', trigger: 'blur' }], |
| | | cityName: [{ required: true, message: '请输入城市', trigger: 'blur' }], |
| | | meetingPoints: [{ required: true, message: '请输入汇合点', trigger: 'blur' }], |
| | | scenicSpotId: [{ required: true, message: '请选择景区', trigger: 'change' }], |
| | | meetingPointNames: [{ required: true, type: 'array', min: 1, message: '请选择汇合点', trigger: 'change' }], |
| | | } |
| | | |
| | | const formatDateTime = (value) => value ? value.replace('T', ' ').slice(0, 19) : '-' |
| | | const formatMoney = (value) => `¥${Number(value || 0).toFixed(2)}` |
| | | const assignDetail = (detail) => Object.assign(form, { |
| | | id: detail.id, |
| | | teamName: detail.teamName || '', |
| | |
| | | commissionRatio: detail.commissionRatio ?? 0, |
| | | wechatOpenId: detail.wechatOpenId || '', |
| | | merchantId: detail.merchantId || '', |
| | | balance: detail.balance ?? 0, |
| | | collectionGuideId: detail.collectionGuideId ?? null, |
| | | scenicConfigs: (detail.scenicConfigs || []).map((item) => ({ ...item })), |
| | | }) |
| | | const loadCollectionGuideOptions = async () => { |
| | | if (isCreate.value) return |
| | | collectionGuideLoading.value = true |
| | | try { collectionGuideOptions.value = await getGuideTeamCollectionGuideOptions(route.params.id) } |
| | | finally { collectionGuideLoading.value = false } |
| | | } |
| | | const loadDetail = async () => { |
| | | if (isCreate.value) { form.commissionRatio = 0; return } |
| | | loading.value = true |
| | | try { assignDetail(await getGuideTeamDetail(route.params.id)) } |
| | | try { |
| | | assignDetail(await getGuideTeamDetail(route.params.id)) |
| | | await loadCollectionGuideOptions() |
| | | } |
| | | finally { loading.value = false } |
| | | } |
| | | const loadScenicOptions = async () => { |
| | | scenicOptionsLoading.value = true |
| | | try { |
| | | const result = await getScenicSpots({ page: 1, size: 100, status: 1 }) |
| | | scenicOptions.value = result.records || [] |
| | | } finally { scenicOptionsLoading.value = false } |
| | | } |
| | | const selectScenic = async (scenicSpotId, preserveMeetingPoints = false) => { |
| | | const selected = scenicOptions.value.find((item) => item.id === scenicSpotId) |
| | | scenicForm.scenicName = selected?.scenicName || '' |
| | | scenicForm.cityName = selected?.cityName || '' |
| | | if (!preserveMeetingPoints) scenicForm.meetingPointNames = [] |
| | | meetingPointOptions.value = [] |
| | | if (!scenicSpotId) return |
| | | scenicDetailLoading.value = true |
| | | try { |
| | | const detail = await getScenicSpotDetail(scenicSpotId) |
| | | meetingPointOptions.value = (detail.meetingPoints || []).filter((point) => point.status === 1) |
| | | } finally { scenicDetailLoading.value = false } |
| | | } |
| | | const findScenicOption = (row) => scenicOptions.value.find((item) => ( |
| | | item.scenicName === row.scenicName && item.cityName === row.cityName |
| | | )) |
| | | const splitMeetingPoints = (value) => String(value || '').split(/[,,]/).map((item) => item.trim()).filter(Boolean) |
| | | const openScenicCreate = () => { |
| | | scenicEditingIndex.value = -1 |
| | | Object.assign(scenicForm, { scenicName: '', cityName: '', meetingPoints: '' }) |
| | | Object.assign(scenicForm, { scenicSpotId: null, scenicName: '', cityName: '', meetingPointNames: [] }) |
| | | meetingPointOptions.value = [] |
| | | scenicDialogVisible.value = true |
| | | } |
| | | const openScenicEdit = (row, index) => { |
| | | const openScenicEdit = async (row, index) => { |
| | | scenicEditingIndex.value = index |
| | | Object.assign(scenicForm, { scenicName: row.scenicName, cityName: row.cityName, meetingPoints: row.meetingPoints }) |
| | | const selected = findScenicOption(row) |
| | | Object.assign(scenicForm, { |
| | | scenicSpotId: selected?.id || null, |
| | | scenicName: row.scenicName, |
| | | cityName: row.cityName, |
| | | meetingPointNames: splitMeetingPoints(row.meetingPoints), |
| | | }) |
| | | meetingPointOptions.value = [] |
| | | scenicDialogVisible.value = true |
| | | if (selected) await selectScenic(selected.id, true) |
| | | } |
| | | const saveScenic = async () => { |
| | | await scenicFormRef.value.validate() |
| | | const value = { |
| | | scenicName: scenicForm.scenicName.trim(), |
| | | cityName: scenicForm.cityName.trim(), |
| | | meetingPoints: scenicForm.meetingPoints.trim(), |
| | | scenicName: scenicForm.scenicName, |
| | | cityName: scenicForm.cityName, |
| | | meetingPoints: scenicForm.meetingPointNames.join(','), |
| | | } |
| | | const duplicated = form.scenicConfigs.some((item, index) => ( |
| | | index !== scenicEditingIndex.value && item.scenicName.trim() === value.scenicName |
| | |
| | | } |
| | | const save = async () => { |
| | | await formRef.value.validate() |
| | | if (!form.scenicConfigs.length) { |
| | | ElMessage.warning('请至少配置一个景区') |
| | | return |
| | | } |
| | | if (!form.wechatOpenId.trim() && !form.merchantId.trim()) { |
| | | ElMessage.warning('openID和商户ID至少配置一个') |
| | | return |
| | |
| | | commissionRatio: form.commissionRatio, |
| | | wechatOpenId: form.wechatOpenId.trim() || null, |
| | | merchantId: form.merchantId.trim() || null, |
| | | collectionGuideId: form.collectionGuideId, |
| | | scenicConfigs: form.scenicConfigs, |
| | | } |
| | | const result = isCreate.value ? await createGuideTeam(payload) : await updateGuideTeam(route.params.id, payload) |
| | |
| | | } finally { saving.value = false } |
| | | } |
| | | |
| | | onMounted(loadDetail) |
| | | onMounted(() => Promise.all([loadDetail(), loadScenicOptions()])) |
| | | </script> |
| | | |
| | | <template> |
| | |
| | | </el-form-item> |
| | | <el-form-item label="注册时间"> |
| | | <el-input :model-value="formatDateTime(form.createdAt)" disabled /> |
| | | </el-form-item> |
| | | <el-form-item label="讲解队余额"> |
| | | <el-input :model-value="formatMoney(form.balance)" disabled /> |
| | | </el-form-item> |
| | | <el-form-item label="企业名称"> |
| | | <el-input v-model="form.companyName" :disabled="readonly" maxlength="200" /> |
| | |
| | | <span>openID和商户ID至少配置一个</span> |
| | | </div> |
| | | <div class="collection-grid"> |
| | | <el-form-item label="收款导游"> |
| | | <el-select |
| | | v-model="form.collectionGuideId" |
| | | :disabled="readonly || isCreate" |
| | | :loading="collectionGuideLoading" |
| | | clearable |
| | | placeholder="请选择收款导游" |
| | | > |
| | | <el-option |
| | | v-for="guide in collectionGuideOptions" |
| | | :key="guide.id" |
| | | :label="`${guide.realName || '-'} ${guide.mobile || ''}${guide.unavailableReason ? `(${guide.unavailableReason})` : ''}`" |
| | | :value="guide.id" |
| | | :disabled="!guide.configurable && guide.id !== form.collectionGuideId" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="openID"> |
| | | <el-input v-model="form.wechatOpenId" :disabled="readonly" maxlength="128" /> |
| | | </el-form-item> |
| | |
| | | |
| | | <el-dialog v-model="scenicDialogVisible" :title="scenicEditingIndex >= 0 ? '编辑景区配置' : '添加景区配置'" width="560px" destroy-on-close> |
| | | <el-form ref="scenicFormRef" :model="scenicForm" :rules="scenicRules" label-width="86px"> |
| | | <el-form-item label="景区名称" prop="scenicName"> |
| | | <el-input v-model="scenicForm.scenicName" maxlength="128" /> |
| | | <el-form-item label="景区名称" prop="scenicSpotId"> |
| | | <el-select |
| | | v-model="scenicForm.scenicSpotId" |
| | | :loading="scenicOptionsLoading" |
| | | filterable |
| | | placeholder="请选择景区" |
| | | @change="selectScenic" |
| | | > |
| | | <el-option |
| | | v-for="scenic in scenicOptions" |
| | | :key="scenic.id" |
| | | :label="`${scenic.scenicName}(${scenic.cityName})`" |
| | | :value="scenic.id" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="城市" prop="cityName"> |
| | | <el-input v-model="scenicForm.cityName" maxlength="64" /> |
| | | <el-form-item label="城市"> |
| | | <el-input :model-value="scenicForm.cityName" disabled placeholder="选择景区后自动带出" /> |
| | | </el-form-item> |
| | | <el-form-item label="汇合点" prop="meetingPoints"> |
| | | <el-input v-model="scenicForm.meetingPoints" type="textarea" :rows="4" maxlength="500" show-word-limit placeholder="多个汇合点可使用逗号分隔" /> |
| | | <el-form-item label="汇合点" prop="meetingPointNames"> |
| | | <el-select |
| | | v-model="scenicForm.meetingPointNames" |
| | | :disabled="!scenicForm.scenicSpotId" |
| | | :loading="scenicDetailLoading" |
| | | multiple |
| | | collapse-tags |
| | | collapse-tags-tooltip |
| | | placeholder="请选择汇合点" |
| | | > |
| | | <el-option |
| | | v-for="point in meetingPointOptions" |
| | | :key="point.id" |
| | | :label="point.pointName" |
| | | :value="point.pointName" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-form> |
| | | <template #footer> |
| | |
| | | .collection-grid :deep(.el-form-item__label) { width: auto !important; color: #53657d; font-weight: 600; } |
| | | .save-row { padding: 28px 36px; border-top: 10px solid #f4f7fb; } |
| | | .save-row :deep(.el-button) { width: 138px; } |
| | | .team-detail-page :deep(.el-dialog .el-select) { width: 100%; } |
| | | @media (max-width: 1400px) { |
| | | .basic-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } |
| | | } |