<script setup lang="tsx">
|
import { useBoolean } from '@/hooks'
|
import { addOpenCity, editOpenCity, getRegionCascader, getRegionCascaderNoArea } from '@/service/api/operations'
|
|
interface Props {
|
modalName?: string
|
}
|
|
const {
|
modalName = '',
|
} = defineProps<Props>()
|
const emit = defineEmits<{
|
success: []
|
open: []
|
close: []
|
}>()
|
|
const isRoleSelectOpen = ref(false)
|
|
const dialog = useDialog()
|
const { bool: modalVisible, setTrue: showModal, setFalse: hiddenModal } = useBoolean(false)
|
const { bool: submitLoading, setTrue: startLoading, setFalse: endLoading } = useBoolean(false)
|
|
const formDefault = {
|
id: undefined,
|
pca: null as string | null,
|
provinceCode: '',
|
provinceName: '',
|
cityCode: '',
|
cityName: '',
|
districtCode: '',
|
districtName: '',
|
status: 1,
|
}
|
const formModel = ref({ ...formDefault })
|
|
type ModalType = 'add' | 'view' | 'edit'
|
const modalType = shallowRef<ModalType>('add')
|
const modalTitle = computed(() => {
|
const titleMap: Record<ModalType, string> = {
|
add: '新增',
|
view: '查看',
|
edit: '编辑',
|
}
|
return `${titleMap[modalType.value]}${modalName}`
|
})
|
|
const initialFormModel = ref<any>(null)
|
|
async function openModal(type: ModalType = 'add', data: any) {
|
emit('open')
|
modalType.value = type
|
showModal()
|
fetchCascaderData()
|
|
if (type === 'add') {
|
formModel.value = { ...formDefault }
|
}
|
else {
|
// For edit/view, we need to reconstruct the pca value (districtCode)
|
if (data.cityCode === data.provinceCode) {
|
data.cityCode = `${data.provinceCode}_city`
|
}
|
formModel.value = {
|
...data,
|
pca: data.districtCode || data.cityCode || data.provinceCode,
|
}
|
}
|
// 保存初始状态
|
initialFormModel.value = JSON.parse(JSON.stringify(formModel.value))
|
}
|
|
function closeModal() {
|
// 比较当前表单数据和初始数据
|
const currentFormStr = JSON.stringify(formModel.value)
|
const initialFormStr = JSON.stringify(initialFormModel.value)
|
|
if (currentFormStr !== initialFormStr) {
|
dialog.create({
|
title: '当前配置未保存,确定取消吗?',
|
content: '',
|
positiveText: '确定',
|
negativeText: '取消',
|
maskClosable: false,
|
autoFocus: false,
|
onPositiveClick: () => {
|
hiddenModal()
|
endLoading()
|
emit('close')
|
},
|
})
|
}
|
else {
|
hiddenModal()
|
endLoading()
|
emit('close')
|
}
|
}
|
|
defineExpose({
|
openModal,
|
})
|
|
const formRef = ref()
|
|
function handlePcaChange(value: string, option: any, path: any[]) {
|
if (path && path.length > 0) {
|
formModel.value.provinceCode = path[0]?.code || ''
|
formModel.value.provinceName = path[0]?.name || ''
|
|
// 还原特殊的code处理
|
const cityCode = path[1]?.code || ''
|
formModel.value.cityCode = cityCode.endsWith('_city') ? cityCode.replace('_city', '') : cityCode
|
|
formModel.value.cityName = path[1]?.name || ''
|
formModel.value.districtCode = path[2]?.code || ''
|
formModel.value.districtName = path[2]?.name || ''
|
}
|
}
|
|
async function submitModal() {
|
await formRef.value?.validate()
|
startLoading()
|
|
const params = {
|
provinceCode: formModel.value.provinceCode,
|
provinceName: formModel.value.provinceName,
|
cityCode: formModel.value.cityCode,
|
cityName: formModel.value.cityName,
|
districtCode: formModel.value.districtCode,
|
districtName: formModel.value.districtName,
|
status: formModel.value.status,
|
}
|
|
let res
|
if (modalType.value === 'add') {
|
res = await addOpenCity(params)
|
}
|
else {
|
res = await editOpenCity({ ...params, id: formModel.value.id })
|
}
|
|
endLoading()
|
|
if (res.code === 200) {
|
window.$message.success('开通城市配置成功')
|
emit('success')
|
hiddenModal()
|
}
|
}
|
|
const rules = {
|
pca: {
|
required: true,
|
validator: (_rule: any, _value: any) => {
|
if (!formModel.value.provinceCode || !formModel.value.cityCode) {
|
return new Error('请选择开通城市,至少选择到市级')
|
}
|
return true
|
},
|
trigger: ['blur', 'change'],
|
},
|
}
|
|
const pcaOptions = ref<any[]>([])
|
|
/**
|
* 格式化级联数据,如果 children 为空则删除该属性,避免级联组件渲染空菜单
|
* @param list 原始级联数据列表
|
*/
|
function formatCascaderData(list: any[]) {
|
return list.map((item) => {
|
const newItem = { ...item }
|
|
// 特殊处理香港澳门:如果父子code相同(如澳门820000),修改子code以区分
|
if (newItem.children && newItem.children.length > 0) {
|
newItem.children = newItem.children.map((child: any) => {
|
if (child.code === newItem.code) {
|
return { ...child, code: `${child.code}_city` } // 修改子级code避免冲突
|
}
|
return child
|
})
|
newItem.children = formatCascaderData(newItem.children)
|
}
|
else {
|
delete newItem.children
|
}
|
return newItem
|
})
|
}
|
|
async function fetchCascaderData() {
|
const res = await getRegionCascaderNoArea()
|
if (res.code === 200 && res.data) {
|
pcaOptions.value = formatCascaderData(res.data)
|
}
|
}
|
</script>
|
|
<template>
|
<n-modal
|
v-model:show="modalVisible" :segmented="{
|
content: true,
|
action: true,
|
}" :content-style="{
|
padding: '16px 24px 0px 24px',
|
}" :mask-closable="false" preset="card" title="开通城市" class="w-400px table-modal"
|
>
|
<n-form
|
ref="formRef" :rules="rules" :model="formModel" :disabled="modalType === 'view'"
|
require-mark-placement="left"
|
>
|
<n-form-item label="开通城市" path="pca">
|
<n-cascader
|
v-model:value="formModel.pca" :options="pcaOptions" value-field="code" label-field="name"
|
check-strategy="all" @update:value="handlePcaChange" @update:show="isRoleSelectOpen = $event"
|
>
|
<template #arrow>
|
<img
|
src="https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/e120f81c-9f15-4e1a-9938-22252898edf9.png"
|
class="arrow-icon" :class="{ 'arrow-icon--open': isRoleSelectOpen }" style="width: 16px;height: 16px;"
|
alt="arrow" @error="(e: any) => { e.target.src = 'https://huishou-1323682843.cos.ap-nanjing.myqcloud.com/images/9bed493b-a6aa-4ef6-ac10-7f67f5c6adc5.png' }"
|
>
|
</template>
|
</n-cascader>
|
</n-form-item>
|
<n-form-item label="启用禁用状态" path="status" required label-placement="left">
|
<n-switch v-model:value="formModel.status" :checked-value="1" :unchecked-value="0" />
|
</n-form-item>
|
</n-form>
|
<template #action>
|
<n-space justify="end">
|
<n-button @click="closeModal">
|
取消
|
</n-button>
|
<n-button type="primary" :loading="submitLoading" @click="submitModal">
|
保存
|
</n-button>
|
</n-space>
|
</template>
|
</n-modal>
|
</template>
|