添加系统管理、登录模块的接口、标准化开发流程
This commit is contained in:
166
src/views/system/menus/index.vue
Normal file
166
src/views/system/menus/index.vue
Normal file
@@ -0,0 +1,166 @@
|
||||
<template>
|
||||
<div class="page-container">
|
||||
<!-- 操作栏 -->
|
||||
<a-card class="table-card" :bordered="false">
|
||||
<template #title>
|
||||
<a-button type="primary" @click="handleAdd()">
|
||||
<PlusOutlined /> 新增菜单
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<!-- 表格(树形) -->
|
||||
<a-table
|
||||
:columns="columns"
|
||||
:data-source="treeData"
|
||||
:loading="loading"
|
||||
:pagination="false"
|
||||
row-key="id"
|
||||
:default-expand-all-rows="true"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'icon'">
|
||||
<span v-if="record.icon" class="menu-icon"><component :is="record.icon" /> {{ record.icon }}</span>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'type'">
|
||||
<a-tag :color="getTypeColor(record.type)">{{ getTypeName(record.type) }}</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'status'">
|
||||
<a-tag :color="record.status === 1 ? 'green' : 'red'">
|
||||
{{ record.status === 1 ? '正常' : '禁用' }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'hidden'">
|
||||
<a-tag :color="record.hidden === 1 ? 'orange' : 'default'">
|
||||
{{ record.hidden === 1 ? '隐藏' : '显示' }}
|
||||
</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'action'">
|
||||
<a-space>
|
||||
<a-button type="link" size="small" @click="handleAdd(record.id)">添加子菜单</a-button>
|
||||
<a-button type="link" size="small" @click="handleEdit(record as MenuRecord)">编辑</a-button>
|
||||
<a-popconfirm title="确定删除该菜单吗?" @confirm="handleDelete(record.id)">
|
||||
<a-button type="link" size="small" danger>删除</a-button>
|
||||
</a-popconfirm>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-card>
|
||||
|
||||
<!-- 新增/编辑弹窗 -->
|
||||
<MenuFormModal
|
||||
v-model:visible="modalVisible"
|
||||
:record="currentRecord"
|
||||
:menu-tree="treeData"
|
||||
@success="loadData"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { PlusOutlined } from '@ant-design/icons-vue'
|
||||
import type { MenuRecord, MenuFormData } from '@/types/system/menu'
|
||||
import { getMenuList, deleteMenu } from '@/api/system/menu'
|
||||
import MenuFormModal from '@/components/system/menu/MenuFormModal.vue'
|
||||
import { buildMenuTree } from '@/utils/route'
|
||||
// Note: buildMenuTree expects SysMenu, MenuRecord extends SysMenu so it's fine.
|
||||
// Using 'any' cast if strict checking fails due to optional properties.
|
||||
|
||||
const loading = ref(false)
|
||||
const treeData = ref<MenuRecord[]>([])
|
||||
|
||||
// 表格列定义
|
||||
const columns = [
|
||||
{ title: '菜单名称', dataIndex: 'name', key: 'name', width: 200 },
|
||||
{ title: '编码', dataIndex: 'code', key: 'code', width: 150 },
|
||||
{ title: '图标', dataIndex: 'icon', key: 'icon', width: 120 },
|
||||
{ title: '类型', dataIndex: 'type', key: 'type', width: 100 },
|
||||
{ title: '路径', dataIndex: 'path', key: 'path' },
|
||||
{ title: '排序', dataIndex: 'sort', key: 'sort', width: 80 },
|
||||
{ title: '状态', dataIndex: 'status', key: 'status', width: 80 },
|
||||
{ title: '显示', dataIndex: 'hidden', key: 'hidden', width: 80 },
|
||||
{ title: '操作', key: 'action', width: 220, fixed: 'right' as const }
|
||||
]
|
||||
|
||||
// 弹窗相关
|
||||
const modalVisible = ref(false)
|
||||
const currentRecord = ref<MenuFormData | undefined>(undefined)
|
||||
|
||||
function getTypeColor(type: string): string {
|
||||
const colorMap: Record<string, string> = {
|
||||
directory: 'blue',
|
||||
menu: 'green',
|
||||
button: 'orange'
|
||||
}
|
||||
return colorMap[type] || 'default'
|
||||
}
|
||||
|
||||
function getTypeName(type: string): string {
|
||||
const nameMap: Record<string, string> = {
|
||||
directory: '目录',
|
||||
menu: '菜单',
|
||||
button: '按钮'
|
||||
}
|
||||
return nameMap[type] || type
|
||||
}
|
||||
|
||||
// 加载数据
|
||||
async function loadData() {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await getMenuList()
|
||||
// res.data.data is the array
|
||||
const list = res.data.data || []
|
||||
// Cast to any because buildMenuTree typings might be strict about SysMenu properties
|
||||
treeData.value = buildMenuTree(list as any[]) as unknown as MenuRecord[]
|
||||
} catch (error) {
|
||||
console.error('加载数据失败:', error)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handleAdd(parentId?: number) {
|
||||
currentRecord.value = {
|
||||
parentId: parentId || 0
|
||||
} as MenuFormData
|
||||
modalVisible.value = true
|
||||
}
|
||||
|
||||
function handleEdit(record: MenuRecord) {
|
||||
currentRecord.value = { ...record }
|
||||
modalVisible.value = true
|
||||
}
|
||||
|
||||
async function handleDelete(id: number) {
|
||||
try {
|
||||
await deleteMenu(id)
|
||||
message.success('删除成功')
|
||||
loadData()
|
||||
} catch (error) {
|
||||
console.error('删除失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-container {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.table-card {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.menu-icon {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user