fix: 🐛 菜单管理和字典管理

1.修复菜单管理问题;
2.修复字典管理问题;
This commit is contained in:
猿小天
2023-05-09 00:05:41 +08:00
parent 9c2dea9db5
commit 8d7d775164
8 changed files with 101 additions and 71 deletions

View File

@@ -34,6 +34,19 @@ class DictionaryCreateUpdateSerializer(CustomModelSerializer):
""" """
字典管理 创建/更新时的列化器 字典管理 创建/更新时的列化器
""" """
value = serializers.CharField(max_length=100)
def validate_value(self, value):
"""
在父级的字典编号验证重复性
"""
initial_data = self.initial_data
parent = initial_data.get('parent',None)
if parent is None:
unique = Dictionary.objects.filter(value=value).exists()
if unique:
raise serializers.ValidationError("字典编号不能重复")
return value
class Meta: class Meta:
model = Dictionary model = Dictionary
@@ -51,20 +64,24 @@ class DictionaryViewSet(CustomModelViewSet):
""" """
queryset = Dictionary.objects.all() queryset = Dictionary.objects.all()
serializer_class = DictionarySerializer serializer_class = DictionarySerializer
create_serializer_class = DictionaryCreateUpdateSerializer
extra_filter_class = [] extra_filter_class = []
search_fields = ['label'] search_fields = ['label']
def get_queryset(self): def get_queryset(self):
if self.action =='list':
params = self.request.query_params params = self.request.query_params
parent = params.get('parent', None) parent = params.get('parent', None)
if params: if params:
if parent: if parent:
queryset = self.queryset.filter(status=1, parent=parent) queryset = self.queryset.filter(parent=parent)
else: else:
queryset = self.queryset.filter(status=1, parent__isnull=True) queryset = self.queryset.filter(parent__isnull=True)
else: else:
queryset = self.queryset.filter(status=1, parent__isnull=True) queryset = self.queryset.filter(parent__isnull=True)
return queryset return queryset
else:
return self.queryset
class InitDictionaryViewSet(APIView): class InitDictionaryViewSet(APIView):

View File

@@ -63,10 +63,10 @@
<el-col class="center"> <el-col class="center">
<el-divider> <el-divider>
<el-button @click="saveMenu()" type="primary" round>保存</el-button> <el-button @click="saveMenu()" type="primary" round>保存</el-button>
<el-button @click="newMenu()" type="success" round>新建</el-button> <el-button @click="newMenu()" type="success" round :disabled="!form.id">新建</el-button>
<el-button @click="addChildMenu()" type="info" round>添加子级</el-button> <el-button @click="addChildMenu()" type="info" round :disabled="!form.id">添加子级</el-button>
<el-button @click="addSameLevelMenu()" type="warning" round>添加同级</el-button> <el-button @click="addSameLevelMenu()" type="warning" round :disabled="!form.id">添加同级</el-button>
<el-button @click="deleteMenu()" type="danger" round>删除部门</el-button> <el-button @click="deleteMenu()" type="danger" round :disabled="!form.id">删除部门</el-button>
</el-divider> </el-divider>
</el-col> </el-col>
</el-row> </el-row>
@@ -79,7 +79,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import * as api from './api'; import * as api from './api';
import { ElForm, ElTree, FormRules } from 'element-plus'; import {ElForm, ElMessageBox, ElTree, FormRules} from 'element-plus';
import { ref, onMounted, reactive, toRaw, watch } from 'vue'; import { ref, onMounted, reactive, toRaw, watch } from 'vue';
import XEUtils from 'xe-utils'; import XEUtils from 'xe-utils';
import { errorMessage, successMessage } from '../../../utils/message'; import { errorMessage, successMessage } from '../../../utils/message';
@@ -175,7 +175,6 @@ const formRef = ref<InstanceType<typeof ElForm>>();
const rules = reactive<FormRules>({ const rules = reactive<FormRules>({
// @ts-ignore // @ts-ignore
parent: [{ required: true, message: '父级部门名称必填', trigger: 'blur' }],
name: [{ required: true, message: '部门名称必填', trigger: 'blur' }], name: [{ required: true, message: '部门名称必填', trigger: 'blur' }],
key: [{ required: true, message: '部门标识必填', trigger: 'blur' }], key: [{ required: true, message: '部门标识必填', trigger: 'blur' }],
}); });
@@ -236,10 +235,21 @@ const addSameLevelMenu = () => {
}; };
const deleteMenu = () => { const deleteMenu = () => {
ElMessageBox.confirm(
'您确认删除该菜单项吗?',
'温馨提示',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
}
).then(() => {
api.DelObj(form).then((res: APIResponseData) => { api.DelObj(form).then((res: APIResponseData) => {
successMessage(res.msg as string); successMessage(res.msg as string);
getData(); getData();
}); });
})
}; };
const handleNodeClick = (data: any, node: any, prop: any) => { const handleNodeClick = (data: any, node: any, prop: any) => {

View File

@@ -11,7 +11,7 @@ export function GetList(query: UserPageQuery) {
} }
export function GetObj(id: InfoReq) { export function GetObj(id: InfoReq) {
return request({ return request({
url: apiPrefix + id, url: apiPrefix + id+'/',
method: 'get', method: 'get',
}); });
} }
@@ -36,6 +36,6 @@ export function DelObj(id: DelReq) {
return request({ return request({
url: apiPrefix + id + '/', url: apiPrefix + id + '/',
method: 'delete', method: 'delete',
data: { id }, data: { },
}); });
} }

View File

@@ -14,7 +14,15 @@ export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOp
return await api.DelObj(row.id); return await api.DelObj(row.id);
}; };
const addRequest = async ({ form }: AddReq) => { const addRequest = async ({ form }: AddReq) => {
const data = crudExpose.getSearchFormData()
const parent = data.parent
form.parent = parent
if(parent){
return await api.AddObj(form); return await api.AddObj(form);
}else{
return undefined
}
}; };
return { return {

View File

@@ -16,7 +16,12 @@ import { request } from '/@/utils/service';
//此处为crudOptions配置 //此处为crudOptions配置
export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { export const createCrudOptions = function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const pageRequest = async (query: UserPageQuery) => { const pageRequest = async (query: UserPageQuery) => {
if(context!.selectOptions.value.id){
return await api.GetList({ menu: context!.selectOptions.value.id } as any); return await api.GetList({ menu: context!.selectOptions.value.id } as any);
}else{
return undefined
}
}; };
const editRequest = async ({ form, row }: EditReq) => { const editRequest = async ({ form, row }: EditReq) => {
form.id = row.id; form.id = row.id;

View File

@@ -11,16 +11,19 @@ let selectOptions: any = ref({ name: null });
const props = defineProps<{ const props = defineProps<{
selectMenu: object; selectMenu: object;
}>(); }>();
const { crudRef, crudBinding, crudExpose, context } = useFs({ createCrudOptions, context: { selectOptions } });
const { doRefresh,setTableData } = crudExpose;
watch(props.selectMenu, (val: any) => { watch(props.selectMenu, (val: any) => {
if (!val.is_catalog) { if (!val.is_catalog && val.id) {
selectOptions.value = val; selectOptions.value = val;
doRefresh(); doRefresh();
}else{
//清空表格数据
setTableData([])
} }
}); });
const { crudRef, crudBinding, crudExpose, context } = useFs({ createCrudOptions, context: { selectOptions } });
const { doRefresh } = crudExpose;
defineExpose({ selectOptions }); defineExpose({ selectOptions });
</script> </script>

View File

@@ -27,81 +27,57 @@
</el-tree> </el-tree>
</el-card> </el-card>
</el-col> </el-col>
<el-col :span="20" class="p-1"> <el-col :span="6" class="p-1">
<el-card :body-style="{ height: '100%' }"> <el-card :body-style="{ height: '100%' }">
<el-form ref="formRef" :rules="rules" :model="form" label-width="80px" label-position="right"> <el-form ref="formRef" :rules="rules" :model="form" label-width="80px" label-position="right">
<el-alert :title="content" type="success" effect="dark" :closable="false" center /> <el-alert :title="content" type="success" effect="dark" :closable="false" center />
<el-divider> <el-divider>
<strong>菜单配置</strong> <strong>菜单配置</strong>
</el-divider> </el-divider>
<el-row>
<el-col :span="10">
<el-form-item label="菜单ID" prop="id"> <el-input v-model="form.id" disabled /> <el-form-item label="菜单ID" prop="id"> <el-input v-model="form.id" disabled />
</el-form-item> </el-form-item>
</el-col>
<el-col :span="10">
<el-form-item label="父级ID" prop="parent"> <el-input v-model="form.parent" /> </el-form-item> <el-form-item label="父级ID" prop="parent"> <el-input v-model="form.parent" /> </el-form-item>
</el-col>
<el-col :span="10">
<el-form-item required label="菜单名称" prop="name"> <el-input v-model="form.name" /> <el-form-item required label="菜单名称" prop="name"> <el-input v-model="form.name" />
</el-form-item> </el-form-item>
</el-col>
<el-col :span="10">
<el-form-item label="组件地址" prop="component"> <el-form-item label="组件地址" prop="component">
<el-autocomplete class="w-full" v-model="form.component" <el-autocomplete class="w-full" v-model="form.component"
:fetch-suggestions="querySearch" :trigger-on-focus="false" clearable debounce="100" :fetch-suggestions="querySearch" :trigger-on-focus="false" clearable debounce="100"
placeholder="输入组件地址" /> placeholder="输入组件地址" />
</el-form-item> </el-form-item>
</el-col>
<el-col :span="10">
<el-form-item required label="Url" prop="web_path"> <el-input v-model="form.web_path" /> <el-form-item required label="Url" prop="web_path"> <el-input v-model="form.web_path" />
</el-form-item> </el-form-item>
</el-col>
<el-col :span="10">
<el-form-item label="排序" prop="sort"> <el-form-item label="排序" prop="sort">
<el-input-number v-model="form.sort" controls-position="right" /> <el-input-number v-model="form.sort" controls-position="right" />
</el-form-item> </el-form-item>
</el-col>
<el-col :span="10">
<el-form-item label="状态"> <el-form-item label="状态">
<el-radio-group v-model="form.status"> <el-radio-group v-model="form.status">
<el-radio :label="true">启用</el-radio> <el-radio :label="true">启用</el-radio>
<el-radio :label="false">禁用</el-radio> <el-radio :label="false">禁用</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
</el-col>
<el-col :span="10">
<el-form-item label="侧边可见"> <el-form-item label="侧边可见">
<el-radio-group v-model="form.visible"> <el-radio-group v-model="form.visible">
<el-radio :label="true">启用</el-radio> <el-radio :label="true">启用</el-radio>
<el-radio :label="false">禁用</el-radio> <el-radio :label="false">禁用</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
</el-col>
<el-col :span="20">
<el-form-item label="图标" prop="icon"> <el-form-item label="图标" prop="icon">
<IconSelector clearable v-model="form.icon" /> <IconSelector clearable v-model="form.icon" />
</el-form-item> </el-form-item>
</el-col> </el-form>
<el-col class="center"> <el-divider></el-divider>
<el-divider> <div>
<el-button @click="saveMenu()" type="primary" round>保存</el-button> <el-button @click="saveMenu()" type="primary" round>保存</el-button>
<el-button @click="newMenu()" type="success" round>新建</el-button> <el-button @click="newMenu()" type="success" round :disabled="!form.id">新建</el-button>
<el-button @click="addChildMenu()" type="info" round>添加子级</el-button> <el-button @click="addChildMenu()" type="warning" round :disabled="!form.id">添加子级</el-button>
<el-button @click="addSameLevelMenu()" type="warning" round>添加同级</el-button> <!-- <el-button @click="addSameLevelMenu()" type="warning" round>添加同级</el-button>-->
<el-button @click="deleteMenu()" type="danger" round>删除菜单</el-button> <el-button @click="deleteMenu()" type="danger" round :disabled="!form.id">删除菜单</el-button>
</el-divider> </div>
</el-col>
</el-row>
<el-row>
<el-col class="h-full">
<el-card :body-style="{ height: '100%' }" class="mt-10">
<menuButton :select-menu="form" />
</el-card> </el-card>
</el-col> </el-col>
</el-row> <el-col :span="14" class="p-1">
</el-form> <el-card :body-style="{ height: '100%' }" >
<menuButton :select-menu="form" />
</el-card> </el-card>
</el-col> </el-col>
</el-row> </el-row>
@@ -111,7 +87,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import * as api from './api'; import * as api from './api';
import * as menuButoonApi from './components/menuButton/api'; import * as menuButoonApi from './components/menuButton/api';
import { ElForm, ElTree, FormRules } from 'element-plus'; import { ElForm, ElTree, FormRules,ElMessageBox } from 'element-plus';
import { ref, onMounted, watch, reactive, toRaw, defineAsyncComponent, nextTick, shallowRef } from 'vue'; import { ref, onMounted, watch, reactive, toRaw, defineAsyncComponent, nextTick, shallowRef } from 'vue';
import XEUtils from 'xe-utils'; import XEUtils from 'xe-utils';
import { errorMessage, successMessage } from '../../../utils/message'; import { errorMessage, successMessage } from '../../../utils/message';
@@ -340,10 +316,21 @@ const addSameLevelMenu = () => {
}; };
const deleteMenu = () => { const deleteMenu = () => {
ElMessageBox.confirm(
'您确认删除该菜单项吗?',
'温馨提示',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
}
).then(() => {
api.DelObj(form).then((res: APIResponseData) => { api.DelObj(form).then((res: APIResponseData) => {
successMessage(res.msg as string); successMessage(res.msg as string);
getData(); getData();
}); });
})
}; };
const handleNodeClick = (data: any, node: any, prop: any) => { const handleNodeClick = (data: any, node: any, prop: any) => {
@@ -355,7 +342,7 @@ const handleNodeClick = (data: any, node: any, prop: any) => {
isAddNewMenu.value = false; isAddNewMenu.value = false;
// 点击tree node时加载对应的权限菜单 // 点击tree node时加载对应的权限菜单
getPermissions({ menu: form.id }); // getPermissions({ menu: form.id });
}; };
const addPermission = () => { const addPermission = () => {

View File

@@ -84,7 +84,7 @@ export const createCrudOptions = function ({ crudExpose, rolePermission }: { cru
title: '序号', title: '序号',
form: { show: false }, form: { show: false },
column: { column: {
//type: 'index', type: 'index',
align: 'center', align: 'center',
width: '70px', width: '70px',
columnSetDisabled: true, //禁止在列设置中选择 columnSetDisabled: true, //禁止在列设置中选择