行政区划

This commit is contained in:
陈红丽 2024-11-29 10:34:18 +08:00
parent 14c63a176f
commit 0391204510
2 changed files with 595 additions and 0 deletions

View File

@ -0,0 +1,287 @@
<template>
<basicModal
@register="modalRegister"
ref="modalRef"
class="basicModal basicFormModal"
@on-ok="handleSubmit"
@on-close="handleClose"
>
<template #default>
<n-form ref="formRef" :model="formData" label-width="85px" label-placement="left">
<div class="flex">
<n-form-item
label="上级城市"
path="pid"
class="flex-1"
:rule="{ type: 'number', required: true, message: '请选择上级城市', trigger: 'change' }"
>
<n-tree-select
v-model:value="formData.pid"
:options="cityOptions"
:on-load="loadTree"
@update:value="handleNodeClick"
label-field="name"
key-field="id"
children-field="children"
:disabled="parentData?.areaCode || cityId ? true : false"
placeholder="上级城市"
/>
</n-form-item>
<n-form-item label="城市级别" path="level" class="flex-1">
<n-select
v-model:value="formData.level"
clearable
placeholder="请选择城市级别"
:options="optionData.cityTypeList"
label-field="name"
value-field="id"
/>
</n-form-item>
</div>
<div class="flex">
<n-form-item
label="城市名称"
path="name"
class="flex-1"
:rule="{ required: true, message: '请输入城市名称', trigger: 'blur' }"
>
<n-input v-model:value="formData.name" placeholder="请输入城市名称" clearable />
</n-form-item>
<n-form-item
label="城市简称"
path="shortName"
class="flex-1"
:rule="{ required: true, message: '城市简称称', trigger: 'blur' }"
>
<n-input v-model:value="formData.shortName" placeholder="请输入城市简称称" clearable />
</n-form-item>
</div>
<div class="flex">
<n-form-item
label="城市区号"
path="cityCode"
class="flex-1"
:rule="{ required: true, message: '城市区号', trigger: 'blur' }"
>
<n-input v-model:value="formData.cityCode" placeholder="请输入城市区号" clearable />
</n-form-item>
<n-form-item
label="行政编码"
path="areaCode"
class="flex-1"
:rule="{ required: true, message: '行政编码', trigger: 'blur' }"
>
<n-input v-model:value="formData.areaCode" placeholder="请输入行政编码" clearable />
</n-form-item>
</div>
<div class="flex">
<n-form-item
label="城市经度"
path="lng"
class="flex-1"
:rule="{ required: true, message: '城市经度', trigger: 'blur' }"
>
<n-input v-model:value="formData.lng" placeholder="请输入城市经度" clearable />
</n-form-item>
<n-form-item
label="城市纬度"
path="lat"
class="flex-1"
:rule="{ required: true, message: '城市纬度', trigger: 'blur' }"
>
<n-input v-model:value="formData.lat" placeholder="请输入城市纬度" clearable />
</n-form-item>
</div>
<div class="flex">
<n-form-item label="邮政编码" path="zipCode" class="flex-1">
<n-input v-model:value="formData.zipCode" placeholder="请输入邮政编码" clearable />
</n-form-item>
</div>
</n-form>
</template>
</basicModal>
</template>
<script lang="ts" setup>
import { cityAdd, cityUpdate, getCityDetail } from '@/api/data/city';
import { onMounted, reactive, ref, shallowRef, nextTick } from 'vue';
import { getCityByList } from '@/api/system/user';
import { useMessage, useDialog } from 'naive-ui';
import { useModal } from '@/components/Modal';
/**
* 定义参数变量
*/
const emit = defineEmits(['success', 'update:visible']);
const formRef = ref();
/**
* 定义接收的参数
*/
const props = defineProps({
visible: {
type: Boolean,
required: true,
default: false,
},
cityId: {
type: Number,
required: true,
default: 0,
},
parentData: {
type: Object,
default: () => {},
},
itemData: {
type: Object,
default: () => {},
},
});
/**
* 定义选项数据
*/
const optionData = reactive({
cityTypeList: [
{
id: 0,
name: '省份',
},
{
id: 1,
name: '城市',
},
{
id: 2,
name: '县区',
},
{
id: 3,
name: '街道',
},
{
id: 4,
name: '居委会',
},
],
});
/**
* 定义表单参数
*/
const formData = reactive({
id: '',
pid: 0,
level: '',
name: '',
cityCode: '',
areaCode: '',
parentCode: '',
zipCode: '',
shortName: '',
lng: '',
lat: '',
});
const [modalRegister, { openModal, setSubLoading }] = useModal({
title: props.cityId ? '编辑' : '添加',
subBtuText: '确定',
width: 700,
});
const message = useMessage();
const parentCode = ref({});
/**
* 关闭窗体
*/
const handleClose = () => {
formData.pid = 0;
emit('update:visible', false);
};
/**
* 加载树结构
*/
const cityOptions = ref([{ id: 0, name: '根目录', areaCode: 0, isLeaf: false }]);
const loadTree = async (treeNode) => {
console.log(treeNode);
return new Promise<void>(async (resolve) => {
const data = await getCityByList(treeNode.areaCode);
data.map((item) => {
item.isLeaf = item.level == 3 ? true : false;
});
treeNode.children = data;
resolve();
});
};
/**
* 执行节点点击事件
*/
const handleNodeClick = (data, node, extra) => {
parentCode.value = node;
formData.parentCode = node.areaCode;
};
/**
* 执行提交表单
*/
const handleSubmit = async () => {
formRef.value
.validate()
.then(async () => {
if (formData.pid == 0) {
formData.parentCode = '0';
}
props.cityId ? await cityUpdate(formData) : await cityAdd(formData);
message.success('操作成功');
setSubLoading(false);
emit('update:visible', false);
emit('success', parentCode.value);
})
.catch((error) => {
setSubLoading(false);
});
};
/**
* 设置表单数据
* @param data 参数
*/
const setFormData = (data: Record<any, any>) => {
for (const key in formData) {
if (data[key] != null && data[key] != undefined) {
formData[key] = data[key];
}
}
parentCode.value = { areaCode: formData.parentCode };
};
/**
* 获取详情
*/
const getDetail = async () => {
const data = await getCityDetail(props.cityId);
setFormData(data);
};
/**
* 钩子函数
*/
onMounted(() => {
if (props.parentData && Object.keys(props.parentData).length !== 0) {
cityOptions.value[0].children = [];
cityOptions.value[0].children.push(props.parentData);
}
if (props.cityId) {
getDetail();
formData.pid = props.itemData.pid;
} else if (props.itemData) {
formData.pid = props.itemData.id;
formData.parentCode = props.itemData.areaCode;
}
});
//
defineExpose({
openModal,
});
</script>

View File

@ -0,0 +1,308 @@
<template>
<div class="menu-index">
<n-card :bordered="false" class="pt-3 mb-3 proCard">
<div>
<n-button type="primary" @click="handleAdd()" v-perm="['sys:city:add']">
<template #icon>
<PlusOutlined />
</template>
新增
</n-button>
</div>
</n-card>
<n-card :bordered="false" class="pt-3 mb-3 proCard">
<n-spin :show="!showTable">
<n-data-table
:columns="columns"
@load="getChild"
:paginate-single-page="false"
:data="lists"
v-if="showTable"
:row-key="(row) => row.areaCode"
>
<template #description> 加载中 </template>
</n-data-table>
</n-spin>
</n-card>
<editDialog
ref="createModalRef"
v-if="editVisible"
:cityId="cityId"
:parentData="parentData"
:itemData="itemData"
v-model:visible="editVisible"
@success="refreshDataList"
/>
</div>
</template>
<script lang="ts" setup name="menu">
import { defineAsyncComponent, nextTick, onMounted, ref, reactive, h } from 'vue';
import { PlusOutlined, EditOutlined, DeleteOutlined, FormOutlined } from '@vicons/antd';
import { cityDelete } from '@/api/data/city';
import { getCityByList } from '@/api/system/user';
import editDialog from './edit.vue';
import { TableAction } from '@/components/Table';
import { renderIcon } from '@/utils';
import { useMessage, useDialog } from 'naive-ui';
/**
* 定义参数变量
*/
const columns = [
{
title: '城市名称',
key: 'name',
width: 100,
align: 'left',
},
{
title: '城市拼音',
key: 'pinyin',
width: 100,
},
{
title: '城市级别',
key: 'level',
width: 100,
customRender({ record }) {
let levelText = '';
switch (record.level) {
case 0:
levelText = '省份';
break;
case 1:
levelText = '城市';
break;
case 2:
levelText = '县区';
break;
case 3:
levelText = '街道';
break;
case 4:
levelText = '居委会';
break;
default:
break;
}
return h('span', levelText || '-');
},
},
{
title: '城市区号',
key: 'cityCode',
width: 100,
},
{
title: '行政编码',
key: 'areaCode',
width: 100,
},
{
title: '城市邮编',
key: 'zipCode',
width: 100,
},
{
title: '操作',
fixed: 'right',
key: 'action',
width: 100,
render(record) {
return h(TableAction as any, {
style: 'button',
actions: [
{
label: '新增',
type: 'info',
icon: renderIcon(PlusOutlined),
auth: ['sys:city:add'],
onclick: handleAdd.bind(null, record),
},
{
label: '编辑',
type: 'warning',
icon: renderIcon(FormOutlined),
auth: ['sys:city:update'],
onclick: handleEdit.bind(null, record),
},
{
label: '删除',
type: 'error',
icon: renderIcon(DeleteOutlined),
auth: ['sys:city:delete'],
onclick: handleDelete.bind(null, record),
},
],
});
},
},
];
const message = useMessage();
const dialog = useDialog();
const fwbHeight = document.body.clientHeight - 350;
const loading = ref(false);
const createModalRef = ref();
const editVisible = ref(false);
const showTable = ref(true);
const cityId = ref(0);
const parentData = ref();
const itemData = ref({});
const lists = ref([]);
const expandKeys = ref([]);
/**
* 获取行政区划列表
* @param code 参数
*/
const loadDataTable = async (code: any) => {
let res = await getCityByList(0);
let data = res.length > 0 ? res : [];
data.map((item) => {
item.isLeaf = false;
});
lists.value = data;
};
/**
* 刷新数据列表
*/
const refreshDataList = async (code) => {
showTable.value = false;
let res = await getCityByList(0);
let data = res.length > 0 ? res : [];
data.map((item) => {
item.isLeaf = false;
});
lists.value = data;
showTable.value = true;
};
const actionColumn = reactive({
width: 220,
title: '操作',
align: 'center',
key: 'action',
fixed: 'right',
render(record) {
return h(TableAction as any, {
style: 'button',
actions: [
{
label: '新增',
type: 'info',
icon: renderIcon(PlusOutlined),
auth: ['sys:city:add'],
onclick: handleAdd.bind(null, record),
},
{
label: '编辑',
type: 'warning',
icon: renderIcon(FormOutlined),
auth: ['sys:city:update'],
onclick: handleEdit.bind(null, record),
},
{
label: '删除',
type: 'error',
icon: renderIcon(DeleteOutlined),
auth: ['sys:city:delete'],
onclick: handleDelete.bind(null, record),
},
],
});
},
});
/**
* 执行添加
* @param data 参数
*/
const handleAdd = async (data: any) => {
cityId.value = 0;
if (data) {
itemData.value = data;
let datas = findParentNode(lists.value, data.pid);
if (datas) {
parentData.value = datas;
} else {
parentData.value = data;
}
} else {
parentData.value = {};
}
editVisible.value = true;
await nextTick();
createModalRef.value.openModal();
};
/**
* 执行编辑
* @param data 参数
*/
const handleEdit = async (data: any) => {
cityId.value = data.id;
parentData.value = findParentNode(lists.value, data.pid);
editVisible.value = true;
await nextTick();
createModalRef.value.openModal();
};
/**
* 执行删除
* @param row 参数
*/
const handleDelete = async (row: any) => {
dialog.warning({
title: '提示',
content: '确定要删除?',
positiveText: '确定',
negativeText: '取消',
onPositiveClick: async () => {
await cityDelete(row.id);
message.success('删除成功');
refreshDataList();
},
});
};
const getChild = async (row: Record<string, unknown>) => {
expandKeys.value.push(row.areaCode);
return new Promise<void>(async (resolve) => {
const data = await getCityByList(row.areaCode);
data.map((item) => {
item.isLeaf = item.level == 3 ? true : false;
});
row.children = data;
resolve();
});
};
/**
* 根据子节点找到父节点
* @param nodes 节点
* @param targetId 目标节点ID
*/
const findParentNode = (nodes, targetId) => {
for (let node of nodes) {
if (node.id == targetId) {
return node;
}
if (node.children && node.children.length > 0) {
const result = findParentNode(node.children, targetId);
if (result) {
return node;
}
}
}
};
/**
* 钩子函数
*/
onMounted(() => {
loadDataTable();
});
</script>
<style scoped></style>