😎调整菜单管理支持在列表直接修改状态

This commit is contained in:
zuohuaijun 2025-08-12 00:09:16 +08:00
parent 03978cccce
commit 87cbbba9ac
7 changed files with 124 additions and 16 deletions

View File

@ -21,7 +21,7 @@ public enum MenuTypeEnum
/// <summary>
/// 菜单
/// </summary>
[Description("菜单")]
[Description("菜单"), Theme("primary")]
Menu = 2,
/// <summary>

View File

@ -198,6 +198,19 @@ public class SysMenuService : IDynamicApiController, ITransient
await _sysUserMenuService.DeleteMenuList(menuIdList);
}
/// <summary>
/// 设置菜单状态 🔖
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("设置菜单状态")]
public async Task<int> SetStatus(BaseStatusInput input)
{
var menu = await _sysMenuRep.GetByIdAsync(input.Id) ?? throw Oops.Oh(ErrorCodeEnum.D1002);
menu.Status = input.Status;
return await _sysMenuRep.AsUpdateable(menu).UpdateColumns(u => new { u.Status }).ExecuteCommandAsync();
}
/// <summary>
/// 增加和编辑时检查菜单数据
/// </summary>

View File

@ -78,8 +78,8 @@
"vue-router": "^4.5.1",
"vue-signature-pad": "^3.0.2",
"vue3-tree-org": "^4.2.2",
"vxe-pc-ui": "^4.8.12",
"vxe-table": "^4.15.5",
"vxe-pc-ui": "^4.8.13",
"vxe-table": "^4.15.6",
"xe-utils": "^3.7.8",
"xlsx-js-style": "^1.2.0"
},
@ -95,7 +95,7 @@
"@vitejs/plugin-vue": "^6.0.1",
"@vitejs/plugin-vue-jsx": "^5.0.1",
"@vue/compiler-sfc": "^3.5.18",
"code-inspector-plugin": "^1.0.4",
"code-inspector-plugin": "^1.0.5",
"eslint": "^9.33.0",
"eslint-plugin-vue": "^10.4.0",
"globals": "^16.3.0",

View File

@ -18,8 +18,10 @@ import { Configuration } from '../configuration';
// @ts-ignore
import { BASE_PATH, COLLECTION_FORMATS, RequestArgs, BaseAPI, RequiredError } from '../base';
import { AddMenuInput } from '../models';
import { AdminNETResultInt32 } from '../models';
import { AdminNETResultListMenuOutput } from '../models';
import { AdminNETResultListSysMenu } from '../models';
import { BaseStatusInput } from '../models';
import { DeleteMenuInput } from '../models';
import { MenuTypeEnum } from '../models';
import { UpdateMenuInput } from '../models';
@ -231,6 +233,54 @@ export const SysMenuApiAxiosParamCreator = function (configuration?: Configurati
options: localVarRequestOptions,
};
},
/**
*
* @summary 🔖
* @param {BaseStatusInput} [body]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
apiSysMenuSetStatusPost: async (body?: BaseStatusInput, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
const localVarPath = `/api/sysMenu/setStatus`;
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, 'https://example.com');
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
// authentication Bearer required
// http bearer authentication required
if (configuration && configuration.accessToken) {
const accessToken = typeof configuration.accessToken === 'function'
? await configuration.accessToken()
: await configuration.accessToken;
localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
}
localVarHeaderParameter['Content-Type'] = 'application/json-patch+json';
const query = new URLSearchParams(localVarUrlObj.search);
for (const key in localVarQueryParameter) {
query.set(key, localVarQueryParameter[key]);
}
for (const key in options.params) {
query.set(key, options.params[key]);
}
localVarUrlObj.search = (new URLSearchParams(query)).toString();
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json';
localVarRequestOptions.data = needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || "");
return {
url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
options: localVarRequestOptions,
};
},
/**
*
* @summary 🔖
@ -346,6 +396,20 @@ export const SysMenuApiFp = function(configuration?: Configuration) {
return axios.request(axiosRequestArgs);
};
},
/**
*
* @summary 🔖
* @param {BaseStatusInput} [body]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async apiSysMenuSetStatusPost(body?: BaseStatusInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminNETResultInt32>>> {
const localVarAxiosArgs = await SysMenuApiAxiosParamCreator(configuration).apiSysMenuSetStatusPost(body, options);
return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
return axios.request(axiosRequestArgs);
};
},
/**
*
* @summary 🔖
@ -411,6 +475,16 @@ export const SysMenuApiFactory = function (configuration?: Configuration, basePa
async apiSysMenuLoginMenuTreeGet(options?: AxiosRequestConfig): Promise<AxiosResponse<AdminNETResultListMenuOutput>> {
return SysMenuApiFp(configuration).apiSysMenuLoginMenuTreeGet(options).then((request) => request(axios, basePath));
},
/**
*
* @summary 🔖
* @param {BaseStatusInput} [body]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async apiSysMenuSetStatusPost(body?: BaseStatusInput, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminNETResultInt32>> {
return SysMenuApiFp(configuration).apiSysMenuSetStatusPost(body, options).then((request) => request(axios, basePath));
},
/**
*
* @summary 🔖
@ -477,6 +551,17 @@ export class SysMenuApi extends BaseAPI {
public async apiSysMenuLoginMenuTreeGet(options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminNETResultListMenuOutput>> {
return SysMenuApiFp(this.configuration).apiSysMenuLoginMenuTreeGet(options).then((request) => request(this.axios, this.basePath));
}
/**
*
* @summary 🔖
* @param {BaseStatusInput} [body]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof SysMenuApi
*/
public async apiSysMenuSetStatusPost(body?: BaseStatusInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminNETResultInt32>> {
return SysMenuApiFp(this.configuration).apiSysMenuSetStatusPost(body, options).then((request) => request(this.axios, this.basePath));
}
/**
*
* @summary 🔖

View File

@ -1,5 +1,7 @@
export default {
list: {
operationSuccessful: 'Operation successful',
operationFailed: 'Operation failed',
menuName: 'Menu Name',
type: 'Type',
directory: 'Directory',

View File

@ -1,5 +1,7 @@
export default {
list: {
operationSuccessful: '操作成功',
operationFailed: '操作失败',
menuName: '菜单名称',
type: '类型',
directory: '目录',

View File

@ -37,8 +37,8 @@
<template #toolbar_buttons>
<el-button type="primary" icon="ele-Plus" @click="handleAdd" v-auth="'sysMenu/add'"> 新增 </el-button>
<el-button-group style="padding-left: 12px">
<el-button type="primary" icon="ele-Expand" @click="handleExpand"> 全部展开 </el-button>
<el-button type="primary" icon="ele-Fold" @click="handleFold"> 全部折叠 </el-button>
<el-button icon="ele-Expand" @click="handleExpand"> 全部展开 </el-button>
<el-button icon="ele-Fold" @click="handleFold"> 全部折叠 </el-button>
</el-button-group>
</template>
<template #toolbar_tools> </template>
@ -50,14 +50,10 @@
<span class="ml10">{{ $t(row.title) }}</span>
</template>
<template #row_type="{ row }">
<el-tag type="warning" v-if="row.type === 1">目录</el-tag>
<el-tag v-else-if="row.type === 2">菜单</el-tag>
<el-tag type="info" v-else>按钮</el-tag>
<g-sys-dict v-model="row.type" code="MenuTypeEnum" />
</template>
<template #row_status="{ row }">
<el-tag v-if="row.status === 1 && !row.isHide" type="success">启用</el-tag>
<el-tag v-if="row.status != 1" type="danger">禁用</el-tag>
<el-icon v-if="row.isHide" class="ml5"><Hide /></el-icon>
<el-switch v-model="row.status" :active-value="1" :inactive-value="2" size="small" @change="changeStatus(row)" />
</template>
<template #row_record="{ row }">
<ModifyRecord :data="row" />
@ -80,7 +76,6 @@ import { ElMessageBox, ElMessage } from 'element-plus';
import { VxeGridInstance, VxeGridListeners } from 'vxe-table';
import { useVxeTable } from '/@/hooks/useVxeTableOptionsHook';
import SvgIcon from '/@/components/svgIcon/index.vue';
import { Hide } from '@element-plus/icons-vue';
import { useI18n } from 'vue-i18n';
import { auth } from '/@/utils/authFunction';
@ -91,6 +86,7 @@ import { getAPI } from '/@/utils/axios-utils';
import { SysMenuApi } from '/@/api-services/api';
import { SysMenu, UpdateMenuInput } from '/@/api-services/models';
const i18n = useI18n();
const xGrid = ref<VxeGridInstance>();
const editMenuRef = ref<InstanceType<typeof EditMenu>>();
const state = reactive({
@ -102,8 +98,6 @@ const state = reactive({
title: '',
});
const i18n = useI18n();
//
const options = useVxeTable<SysMenu>(
{
@ -111,7 +105,7 @@ const options = useVxeTable<SysMenu>(
name: i18n.t('message.list.menuInfo'),
columns: [
// { type: 'checkbox', width: 40, fixed: 'left' },
{ field: 'seq', type: 'seq', title: '序号', width: 60, fixed: 'left' },
{ field: 'seq', type: 'seq', title: '序号', width: 80, fixed: 'left' },
{ field: 'title', title: '菜单名称', minWidth: 180, showOverflow: 'tooltip', treeNode: true, align: 'left', headerAlign: 'center', slots: { default: 'row_title' } },
{ field: 'type', title: '菜单类型', minWidth: 100, showOverflow: 'tooltip', slots: { default: 'row_type' } },
{ field: 'path', title: '路由路径', minWidth: 150, showOverflow: 'tooltip' },
@ -222,4 +216,16 @@ const handleExpand = () => {
const handleFold = () => {
xGrid.value?.clearTreeExpand();
};
//
const changeStatus = async (row: any) => {
await getAPI(SysMenuApi)
.apiSysMenuSetStatusPost({ id: row.id, status: row.status })
.then(() => {
ElMessage.success(i18n.t('message.list.operationSuccessful'));
})
.catch(() => {
row.status = row.status == 1 ? 2 : 1;
});
};
</script>