390 lines
8.1 KiB
Vue
390 lines
8.1 KiB
Vue
<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>
|