更新架构
This commit is contained in:
@@ -0,0 +1,472 @@
|
||||
<template>
|
||||
<div class="container-box">
|
||||
<div class="header-bar">
|
||||
<h2>模块管理</h2>
|
||||
<div class="header-actions">
|
||||
<el-button type="primary" @click="handleAdd">
|
||||
<el-icon><Plus /></el-icon>
|
||||
添加模块
|
||||
</el-button>
|
||||
<el-button @click="refresh">
|
||||
<el-icon><Refresh /></el-icon>
|
||||
刷新
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-divider></el-divider>
|
||||
|
||||
<el-table
|
||||
:data="modules"
|
||||
style="width: 100%"
|
||||
v-loading="loading"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column prop="id" label="ID" width="80" align="center" />
|
||||
<el-table-column
|
||||
prop="name"
|
||||
label="模块名称"
|
||||
min-width="150"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<div class="module-name">
|
||||
<span class="module-icon" v-html="row.icon"></span>
|
||||
<span>{{ row.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="code"
|
||||
label="模块编码"
|
||||
min-width="120"
|
||||
align="center"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="path"
|
||||
label="路由路径"
|
||||
min-width="150"
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-tag size="small" type="info">{{ row.path || "-" }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="description"
|
||||
label="描述"
|
||||
min-width="200"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column prop="sort" label="排序" width="100" align="center" />
|
||||
<el-table-column prop="is_show" label="显示" width="80" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-switch
|
||||
v-model="row.is_show"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
@change="handleShowChange(row)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" label="状态" width="80" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.status === 1 ? 'success' : 'danger'">
|
||||
{{ row.status === 1 ? "启用" : "禁用" }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="200" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button size="small" @click="handleEdit(row)">编辑</el-button>
|
||||
<el-button size="small" type="danger" @click="handleDelete(row)"
|
||||
>删除</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="table-footer" v-if="selectedModules.length > 0">
|
||||
<span>已选择 {{ selectedModules.length }} 项</span>
|
||||
<el-button type="danger" size="small" @click="handleBatchDelete"
|
||||
>批量删除</el-button
|
||||
>
|
||||
</div>
|
||||
|
||||
<el-divider></el-divider>
|
||||
|
||||
<div class="tips-section">
|
||||
<el-alert title="模块管理说明" type="info" :closable="false" show-icon>
|
||||
<template #default>
|
||||
<p>1. 模块用于管理系统功能单元,每个模块包含独立的路由、图标和描述</p>
|
||||
<p>2. 模块编码用于程序识别,请确保唯一性</p>
|
||||
<p>3. 禁用状态的模块将不会在菜单中显示</p>
|
||||
</template>
|
||||
</el-alert>
|
||||
</div>
|
||||
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:title="dialogType === 'add' ? '添加模块' : '编辑模块'"
|
||||
width="600px"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
label-width="100px"
|
||||
>
|
||||
<el-form-item label="模块名称" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入模块名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="模块编码" prop="code">
|
||||
<el-input
|
||||
v-model="formData.code"
|
||||
placeholder="请输入模块编码(英文)"
|
||||
:disabled="dialogType === 'edit'"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="路由路径" prop="path">
|
||||
<el-input
|
||||
v-model="formData.path"
|
||||
placeholder="请输入路由路径,如 /system/modules"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="图标" prop="icon">
|
||||
<el-input
|
||||
v-model="formData.icon"
|
||||
placeholder="请输入图标类名,如 Grid"
|
||||
>
|
||||
<template #append>
|
||||
<el-popover placement="bottom-end" :width="400" trigger="click">
|
||||
<template #reference>
|
||||
<el-button link>选择图标</el-button>
|
||||
</template>
|
||||
<div class="icon-grid">
|
||||
<div
|
||||
v-for="icon in iconList"
|
||||
:key="icon"
|
||||
class="icon-item"
|
||||
:class="{ active: formData.icon === icon }"
|
||||
@click="formData.icon = icon"
|
||||
>
|
||||
<el-icon :size="20"><component :is="icon" /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="描述" prop="description">
|
||||
<el-input
|
||||
v-model="formData.description"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请输入模块描述"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="排序" prop="sort">
|
||||
<el-input-number
|
||||
v-model="formData.sort"
|
||||
:min="0"
|
||||
:max="999"
|
||||
controls-position="right"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否显示" prop="is_show">
|
||||
<el-switch
|
||||
v-model="formData.is_show"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="formData.status">
|
||||
<el-radio :label="1">启用</el-radio>
|
||||
<el-radio :label="0">禁用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit" :loading="submitLoading"
|
||||
>确定</el-button
|
||||
>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, shallowRef } from "vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import {
|
||||
getModulesList,
|
||||
getModuleDetail,
|
||||
addModule,
|
||||
editModule,
|
||||
deleteModule,
|
||||
batchDeleteModules,
|
||||
changeModuleStatus,
|
||||
} from "@/api/modules";
|
||||
|
||||
const loading = ref(false);
|
||||
const modules = ref([]);
|
||||
const selectedModules = ref([]);
|
||||
const dialogVisible = ref(false);
|
||||
const dialogType = ref("add");
|
||||
const formRef = ref(null);
|
||||
const submitLoading = ref(false);
|
||||
|
||||
const formData = ref({
|
||||
name: "",
|
||||
code: "",
|
||||
path: "",
|
||||
icon: "",
|
||||
description: "",
|
||||
sort: 0,
|
||||
is_show: 1,
|
||||
status: 1,
|
||||
});
|
||||
|
||||
const rules = {
|
||||
name: [{ required: true, message: "请输入模块名称", trigger: "blur" }],
|
||||
code: [{ required: true, message: "请输入模块编码", trigger: "blur" }],
|
||||
};
|
||||
|
||||
function getIconComponent(iconName) {
|
||||
return iconComponents.value[iconName] || Grid;
|
||||
}
|
||||
|
||||
async function fetchModules() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await getModulesList();
|
||||
if (res.code === 200 && res.data) {
|
||||
modules.value = res.data.list || [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取模块列表失败:", error);
|
||||
ElMessage.error("获取模块列表失败");
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
fetchModules();
|
||||
}
|
||||
|
||||
function handleSelectionChange(selection) {
|
||||
selectedModules.value = selection;
|
||||
}
|
||||
|
||||
function handleAdd() {
|
||||
dialogType.value = "add";
|
||||
formData.value = {
|
||||
name: "",
|
||||
code: "",
|
||||
path: "",
|
||||
icon: "",
|
||||
description: "",
|
||||
sort: 0,
|
||||
is_show: 1,
|
||||
status: 1,
|
||||
};
|
||||
dialogVisible.value = true;
|
||||
}
|
||||
|
||||
async function handleEdit(row) {
|
||||
dialogType.value = "edit";
|
||||
try {
|
||||
const res = await getModuleDetail(row.id);
|
||||
if (res.code === 200 && res.data) {
|
||||
formData.value = { ...res.data };
|
||||
dialogVisible.value = true;
|
||||
} else {
|
||||
ElMessage.error(res.msg || "获取模块详情失败");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("获取模块详情失败:", error);
|
||||
ElMessage.error("获取模块详情失败");
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
await formRef.value.validate();
|
||||
submitLoading.value = true;
|
||||
|
||||
if (dialogType.value === "add") {
|
||||
const res = await addModule(formData.value);
|
||||
if (res.code === 200) {
|
||||
ElMessage.success("添加成功");
|
||||
dialogVisible.value = false;
|
||||
fetchModules();
|
||||
} else {
|
||||
ElMessage.error(res.msg || "添加失败");
|
||||
}
|
||||
} else {
|
||||
const res = await editModule(formData.value.id, formData.value);
|
||||
if (res.code === 200) {
|
||||
ElMessage.success("编辑成功");
|
||||
dialogVisible.value = false;
|
||||
fetchModules();
|
||||
} else {
|
||||
ElMessage.error(res.msg || "编辑失败");
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("提交失败:", error);
|
||||
} finally {
|
||||
submitLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleDelete(row) {
|
||||
try {
|
||||
await ElMessageBox.confirm("确定要删除该模块吗?", "提示", {
|
||||
type: "warning",
|
||||
});
|
||||
const res = await deleteModule(row.id);
|
||||
if (res.code === 200) {
|
||||
ElMessage.success("删除成功");
|
||||
fetchModules();
|
||||
} else {
|
||||
ElMessage.error(res.msg || "删除失败");
|
||||
}
|
||||
} catch (error) {
|
||||
if (error !== "cancel") {
|
||||
console.error("删除失败:", error);
|
||||
ElMessage.error("删除失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function handleBatchDelete() {
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`确定要删除选中的 ${selectedModules.value.length} 个模块吗?`,
|
||||
"提示",
|
||||
{
|
||||
type: "warning",
|
||||
},
|
||||
);
|
||||
const ids = selectedModules.value.map((item) => item.id);
|
||||
const res = await batchDeleteModules(ids);
|
||||
if (res.code === 200) {
|
||||
ElMessage.success("批量删除成功");
|
||||
fetchModules();
|
||||
} else {
|
||||
ElMessage.error(res.msg || "批量删除失败");
|
||||
}
|
||||
} catch (error) {
|
||||
if (error !== "cancel") {
|
||||
console.error("批量删除失败:", error);
|
||||
ElMessage.error("批量删除失败");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function handleShowChange(row) {
|
||||
try {
|
||||
const res = await changeModuleStatus(row.id, row.is_show);
|
||||
if (res.code !== 200) {
|
||||
ElMessage.error(res.msg || "状态修改失败");
|
||||
row.is_show = row.is_show ? 0 : 1;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("状态修改失败:", error);
|
||||
row.is_show = row.is_show ? 0 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchModules();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.container-box {
|
||||
padding: 20px;
|
||||
background: var(--el-bg-color);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.header-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
h2 {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.module-name {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
|
||||
.module-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 16px;
|
||||
color: #667eea;
|
||||
}
|
||||
}
|
||||
|
||||
.table-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 12px 0;
|
||||
}
|
||||
|
||||
.tips-section {
|
||||
margin-top: 20px;
|
||||
|
||||
p {
|
||||
margin: 4px 0;
|
||||
font-size: 13px;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
.icon-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(8, 1fr);
|
||||
gap: 8px;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
|
||||
.icon-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
|
||||
&:hover,
|
||||
&.active {
|
||||
background: var(--el-color-primary-light-9);
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user