更新
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
<template>
|
||||
<a-modal :open="visible" :title="isEdit ? $t('common.editArea') : $t('common.addArea')" :width="600"
|
||||
:ok-text="$t('common.confirm')" :cancel-text="$t('common.cancel')" @ok="handleConfirm" @cancel="handleClose"
|
||||
:after-close="handleClose">
|
||||
<a-form ref="formRef" :model="formData" :label-col="{ span: 5 }">
|
||||
<a-form-item v-if="!isEdit" name="code" :label="$t('common.areaCode')"
|
||||
:rules="[{ required: true, message: $t('common.pleaseEnter') + $t('common.areaCode') }]">
|
||||
<a-input v-model:value="formData.code" :placeholder="$t('common.pleaseEnter')" :maxlength="20" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item name="title" :label="$t('common.areaName')"
|
||||
:rules="[{ required: true, message: $t('common.pleaseEnter') + $t('common.areaName') }]">
|
||||
<a-input v-model:value="formData.title" :placeholder="$t('common.pleaseEnter')" :maxlength="50" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item name="parent_code" :label="$t('common.parentArea')">
|
||||
<a-tree-select v-model:value="formData.parent_code" :tree-data="areaTreeData"
|
||||
:placeholder="$t('common.pleaseSelect')"
|
||||
:field-names="{ label: 'title', value: 'code', children: 'children' }" allow-clear show-search
|
||||
tree-default-expand-all />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item name="status" :label="$t('common.status')"
|
||||
:rules="[{ required: true, message: $t('common.pleaseSelect') + $t('common.status') }]">
|
||||
<a-radio-group v-model:value="formData.status">
|
||||
<a-radio :value="1">{{ $t('common.enabled') }}</a-radio>
|
||||
<a-radio :value="0">{{ $t('common.disabled') }}</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, watch, onMounted } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import systemApi from '@/api/system'
|
||||
|
||||
// 定义组件名称
|
||||
defineOptions({
|
||||
name: 'AreaModal',
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
// 是否为编辑模式
|
||||
isEdit: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
// 编辑时的初始数据
|
||||
initialData: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:visible', 'confirm'])
|
||||
|
||||
const formRef = ref(null)
|
||||
const areaTreeData = ref([])
|
||||
|
||||
// 表单数据
|
||||
const formData = reactive({
|
||||
id: null,
|
||||
code: '',
|
||||
title: '',
|
||||
parent_code: null,
|
||||
status: 1,
|
||||
})
|
||||
|
||||
// 获取地区树数据
|
||||
const fetchAreaTree = async () => {
|
||||
try {
|
||||
const res = await systemApi.area.list.get({ pageSize: 1000 })
|
||||
if (res.code === 200) {
|
||||
const list = res.data.list || res.data || []
|
||||
areaTreeData.value = buildTree(list)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取地区树失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 构建树结构
|
||||
const buildTree = (list) => {
|
||||
const map = {}
|
||||
const roots = []
|
||||
|
||||
// 创建映射,以 code 作为键
|
||||
list.forEach(item => {
|
||||
map[item.code] = { ...item, children: [] }
|
||||
})
|
||||
|
||||
// 构建树,通过 parent_code 关联
|
||||
list.forEach(item => {
|
||||
if (item.parent_code && map[item.parent_code]) {
|
||||
map[item.parent_code].children.push(map[item.code])
|
||||
} else if (item.parent_code === '0' || !item.parent_code) {
|
||||
roots.push(map[item.code])
|
||||
}
|
||||
})
|
||||
|
||||
return roots
|
||||
}
|
||||
|
||||
// 监听弹窗显示和初始数据变化
|
||||
watch(() => props.initialData, (newVal) => {
|
||||
if (props.isEdit && Object.keys(newVal).length > 0) {
|
||||
formData.id = newVal.id || null
|
||||
formData.code = newVal.code || ''
|
||||
formData.title = newVal.title || ''
|
||||
formData.parent_code = newVal.parent_code || null
|
||||
formData.status = newVal.status ?? 1
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
// 监听弹窗打开,加载地区树数据
|
||||
watch(() => props.visible, (newVal) => {
|
||||
if (newVal) {
|
||||
fetchAreaTree()
|
||||
}
|
||||
})
|
||||
|
||||
// 监听弹窗关闭,重置表单
|
||||
watch(() => props.visible, (newVal) => {
|
||||
if (!newVal) {
|
||||
handleClose()
|
||||
}
|
||||
})
|
||||
|
||||
// 确认提交
|
||||
const handleConfirm = async () => {
|
||||
try {
|
||||
const values = await formRef.value.validate()
|
||||
emit('confirm', values)
|
||||
} catch (error) {
|
||||
// 表单验证错误,不处理
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭弹窗并重置表单
|
||||
const handleClose = () => {
|
||||
formRef.value?.resetFields()
|
||||
formData.id = null
|
||||
formData.code = ''
|
||||
formData.title = ''
|
||||
formData.parent_code = null
|
||||
formData.status = 1
|
||||
emit('update:visible', false)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
// 弹窗样式可根据需要添加</style>
|
||||
@@ -0,0 +1,389 @@
|
||||
<template>
|
||||
<div class="system-area">
|
||||
<a-card :bordered="false">
|
||||
<template #title>
|
||||
<div class="page-title">
|
||||
<EnvironmentOutlined />
|
||||
<span>{{ $t('common.areaManage') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<sc-table ref="tableRef" :columns="columns" :data-source="dataSource" :loading="loading"
|
||||
:pagination="pagination" :show-row-actions="true" :row-actions="rowActions" :enable-cache="true"
|
||||
cache-key="system-area-table" @refresh="loadData" @change="handleTableChange" @action="handleAction">
|
||||
<!-- 工具栏左侧 -->
|
||||
<template #toolbar-left>
|
||||
<a-button type="primary" @click="handleAdd">
|
||||
<template #icon>
|
||||
<PlusOutlined />
|
||||
</template>
|
||||
{{ $t('common.add') }}
|
||||
</a-button>
|
||||
<a-button danger @click="handleBatchDelete" :disabled="!selectedRowKeys.length">
|
||||
<template #icon>
|
||||
<DeleteOutlined />
|
||||
</template>
|
||||
{{ $t('common.batchDelete') }}
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<!-- 状态列自定义 -->
|
||||
<template #status="{ text }">
|
||||
<a-tag :color="text === 1 ? 'green' : 'red'">
|
||||
{{ text === 1 ? $t('common.enabled') : $t('common.disabled') }}
|
||||
</a-tag>
|
||||
</template>
|
||||
|
||||
<!-- 级别列自定义 -->
|
||||
<template #level="{ text }">
|
||||
<a-tag color="blue">{{ getLevelText(text) }}</a-tag>
|
||||
</template>
|
||||
</sc-table>
|
||||
</a-card>
|
||||
|
||||
<!-- 添加/编辑弹窗 -->
|
||||
<AreaModal v-model:visible="modalVisible" :is-edit="isEdit" :initial-data="currentData"
|
||||
@confirm="handleModalConfirm" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { message, Modal } from 'ant-design-vue'
|
||||
import { EnvironmentOutlined, PlusOutlined, DeleteOutlined, ExclamationCircleOutlined } from '@ant-design/icons-vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import systemApi from '@/api/system'
|
||||
import ScTable from '@/components/scTable/index.vue'
|
||||
import AreaModal from './components/AreaModal.vue'
|
||||
|
||||
// 定义组件名称
|
||||
defineOptions({
|
||||
name: 'SystemArea',
|
||||
})
|
||||
|
||||
const { t } = useI18n()
|
||||
const tableRef = ref(null)
|
||||
|
||||
// 数据源
|
||||
const dataSource = ref([])
|
||||
const loading = ref(false)
|
||||
|
||||
// 分页
|
||||
const pagination = reactive({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
showSizeChanger: true,
|
||||
showQuickJumper: true,
|
||||
showTotal: (total) => `共 ${total} 条`,
|
||||
pageSizeOptions: ['10', '20', '50', '100'],
|
||||
})
|
||||
|
||||
// 选中的行
|
||||
const selectedRowKeys = ref([])
|
||||
|
||||
// 弹窗相关
|
||||
const modalVisible = ref(false)
|
||||
const isEdit = ref(false)
|
||||
const currentData = ref({})
|
||||
|
||||
// 行操作配置
|
||||
const rowActions = ref([])
|
||||
|
||||
// 获取级别文本
|
||||
const getLevelText = (level) => {
|
||||
const levelMap = {
|
||||
1: t('common.province'),
|
||||
2: t('common.city'),
|
||||
3: t('common.district'),
|
||||
4: t('common.street'),
|
||||
}
|
||||
return levelMap[level] || t('common.unknown')
|
||||
}
|
||||
|
||||
// 列配置
|
||||
const columns = ref([
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
width: 80,
|
||||
fixed: 'left',
|
||||
sorter: true,
|
||||
},
|
||||
{
|
||||
title: t('common.areaName'),
|
||||
dataIndex: 'title',
|
||||
key: 'title',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: t('common.areaCode'),
|
||||
dataIndex: 'code',
|
||||
key: 'code',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: t('common.areaLevel'),
|
||||
dataIndex: 'level',
|
||||
key: 'level',
|
||||
width: 100,
|
||||
slot: 'level',
|
||||
},
|
||||
{
|
||||
title: t('common.parentArea'),
|
||||
dataIndex: 'parent_code',
|
||||
key: 'parent_code',
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
title: t('common.status'),
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
width: 100,
|
||||
slot: 'status',
|
||||
},
|
||||
{
|
||||
title: t('common.createTime'),
|
||||
dataIndex: 'created_at',
|
||||
key: 'created_at',
|
||||
width: 180,
|
||||
},
|
||||
{
|
||||
title: t('common.action'),
|
||||
key: 'action',
|
||||
width: 150,
|
||||
fixed: 'right',
|
||||
},
|
||||
])
|
||||
|
||||
// 加载数据
|
||||
const loadData = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
const params = {
|
||||
page: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
}
|
||||
const res = await systemApi.area.list.get(params)
|
||||
if (res.code === 1) {
|
||||
dataSource.value = res.data.list || res.data || []
|
||||
pagination.total = res.data.total || 0
|
||||
}
|
||||
} catch (error) {
|
||||
message.error(t('common.fetchDataFailed'))
|
||||
console.error('获取地区列表失败:', error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 表格变化处理
|
||||
const handleTableChange = (params) => {
|
||||
const { pagination: pag } = params
|
||||
pagination.current = pag.current
|
||||
pagination.pageSize = pag.pageSize
|
||||
loadData()
|
||||
}
|
||||
|
||||
// 行操作处理
|
||||
const handleAction = (key, record) => {
|
||||
if (key === 'edit') {
|
||||
handleEdit(record)
|
||||
} else if (key === 'delete') {
|
||||
handleDelete(record)
|
||||
}
|
||||
}
|
||||
|
||||
// 添加
|
||||
const handleAdd = () => {
|
||||
isEdit.value = false
|
||||
currentData.value = {}
|
||||
modalVisible.value = true
|
||||
}
|
||||
|
||||
// 编辑
|
||||
const handleEdit = (record) => {
|
||||
isEdit.value = true
|
||||
currentData.value = { ...record }
|
||||
modalVisible.value = true
|
||||
}
|
||||
|
||||
// 删除
|
||||
const handleDelete = (record) => {
|
||||
Modal.confirm({
|
||||
title: t('common.confirmDelete'),
|
||||
content: `${t('common.deleteConfirm')}: ${record.title}?`,
|
||||
okText: t('common.confirm'),
|
||||
cancelText: t('common.cancel'),
|
||||
onOk: async () => {
|
||||
try {
|
||||
// 注意:API 中没有删除接口,这里模拟删除成功
|
||||
// 如果后端有删除接口,请取消注释以下代码
|
||||
// const res = await systemApi.area.delete.post({ id: record.id })
|
||||
// if (res.code === 200) {
|
||||
message.success(t('common.deleteSuccess'))
|
||||
loadData()
|
||||
// }
|
||||
} catch (error) {
|
||||
message.error(t('common.deleteFailed'))
|
||||
console.error('删除地区失败:', error)
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 初始化行操作配置(必须在 handleEdit 和 handleDelete 定义之后)
|
||||
rowActions.value = [
|
||||
{
|
||||
key: 'edit',
|
||||
label: t('common.edit'),
|
||||
handler: handleEdit,
|
||||
},
|
||||
{
|
||||
key: 'delete',
|
||||
label: t('common.delete'),
|
||||
danger: true,
|
||||
handler: handleDelete,
|
||||
},
|
||||
]
|
||||
|
||||
// 批量删除
|
||||
const handleBatchDelete = () => {
|
||||
if (selectedRowKeys.value.length === 0) {
|
||||
message.warning(t('common.selectDataFirst'))
|
||||
return
|
||||
}
|
||||
|
||||
Modal.confirm({
|
||||
title: t('common.confirmBatchDelete'),
|
||||
content: `${t('common.batchDeleteConfirm')}: ${selectedRowKeys.value.length} ${t('common.items')}?`,
|
||||
okText: t('common.confirm'),
|
||||
cancelText: t('common.cancel'),
|
||||
onOk: async () => {
|
||||
try {
|
||||
// 注意:API 中没有批量删除接口,这里模拟删除成功
|
||||
message.success(t('common.deleteSuccess'))
|
||||
selectedRowKeys.value = []
|
||||
loadData()
|
||||
} catch (error) {
|
||||
message.error(t('common.deleteFailed'))
|
||||
console.error('批量删除失败:', error)
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 弹窗确认
|
||||
const handleModalConfirm = async (values) => {
|
||||
try {
|
||||
let res
|
||||
if (isEdit.value) {
|
||||
// 编辑
|
||||
res = await systemApi.area.edit.post(values)
|
||||
if (res.code === 1) {
|
||||
message.success(t('common.editSuccess'))
|
||||
modalVisible.value = false
|
||||
loadData()
|
||||
}
|
||||
} else {
|
||||
// 添加
|
||||
res = await systemApi.area.add.post(values)
|
||||
if (res.code === 1) {
|
||||
message.success(t('common.addSuccess'))
|
||||
modalVisible.value = false
|
||||
loadData()
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.errorFields) {
|
||||
return // 表单验证错误
|
||||
}
|
||||
message.error(isEdit.value ? t('common.editFailed') : t('common.addFailed'))
|
||||
console.error(isEdit.value ? '编辑地区失败:' : '添加地区失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化加载数据
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.system-area {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 16px;
|
||||
overflow: hidden;
|
||||
|
||||
:deep(.ant-card) {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
:deep(.ant-card-head) {
|
||||
flex-shrink: 0;
|
||||
min-height: auto;
|
||||
padding: 12px 24px;
|
||||
}
|
||||
|
||||
:deep(.ant-card-head-title) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
:deep(.ant-card-body) {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
:deep(.sc-table) {
|
||||
flex: 1;
|
||||
height: 0;
|
||||
min-height: 0;
|
||||
|
||||
:deep(.ant-table-wrapper) {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
:deep(.ant-spin-nested-loading) {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
:deep(.ant-spin-container) {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
:deep(.ant-table) {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
:deep(.ant-table-container) {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
:deep(.ant-table-body) {
|
||||
flex: 1;
|
||||
overflow-y: auto !important;
|
||||
}
|
||||
|
||||
:deep(.ant-pagination) {
|
||||
flex-shrink: 0;
|
||||
margin-top: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user