web-权限修改

This commit is contained in:
2025-10-08 16:15:47 +08:00
parent 2ad39d403e
commit 1064560f29
12 changed files with 482 additions and 19 deletions

View File

@@ -43,7 +43,7 @@ export const TokenManager = {
* 创建axios实例 * 创建axios实例
*/ */
const request = axios.create({ const request = axios.create({
baseURL: process.env.VITE_API_BASE_URL || "/api", baseURL: import.meta.env.VITE_API_BASE_URL || "/api",
timeout: 30000, timeout: 30000,
headers: { headers: {
'Content-Type': 'application/json;charset=UTF-8', 'Content-Type': 'application/json;charset=UTF-8',
@@ -178,40 +178,54 @@ request.interceptors.response.use(
} }
); );
/**
* API封装
*/
/** /**
* API封装 * API封装
*/ */
export const api = { export const api = {
/** /**
* GET请求 * GET请求 - data参数会作为URL查询参数追加
*/ */
get<T = any>(url: string, config?: CustomAxiosRequestConfig): Promise<AxiosResponse<ResultDomain<T>>> { get<T = any>(url: string, data?: any, config?: CustomAxiosRequestConfig): Promise<AxiosResponse<ResultDomain<T>>> {
// 如果有data参数将其转换为URL查询参数
if (data) {
const params = new URLSearchParams();
Object.keys(data).forEach(key => {
if (data[key] !== undefined && data[key] !== null) {
params.append(key, String(data[key]));
}
});
const queryString = params.toString();
url += (url.includes('?') ? '&' : '?') + queryString;
}
return request.get<ResultDomain<T>>(url, config); return request.get<ResultDomain<T>>(url, config);
}, },
/** /**
* POST请求 * POST请求 - data参数放到请求体
*/ */
post<T = any>(url: string, data?: any, config?: CustomAxiosRequestConfig): Promise<AxiosResponse<ResultDomain<T>>> { post<T = any>(url: string, data?: any, config?: CustomAxiosRequestConfig): Promise<AxiosResponse<ResultDomain<T>>> {
return request.post<ResultDomain<T>>(url, data, config); return request.post<ResultDomain<T>>(url, data, config);
}, },
/** /**
* PUT请求 * PUT请求 - data参数放到请求体
*/ */
put<T = any>(url: string, data?: any, config?: CustomAxiosRequestConfig): Promise<AxiosResponse<ResultDomain<T>>> { put<T = any>(url: string, data?: any, config?: CustomAxiosRequestConfig): Promise<AxiosResponse<ResultDomain<T>>> {
return request.put<ResultDomain<T>>(url, data, config); return request.put<ResultDomain<T>>(url, data, config);
}, },
/** /**
* DELETE请求 * DELETE请求 - data参数放到请求体
*/ */
delete<T = any>(url: string, config?: CustomAxiosRequestConfig): Promise<AxiosResponse<ResultDomain<T>>> { delete<T = any>(url: string, data?: any, config?: CustomAxiosRequestConfig): Promise<AxiosResponse<ResultDomain<T>>> {
return request.delete<ResultDomain<T>>(url, config); return request.delete<ResultDomain<T>>(url, { ...config, data });
}, },
/** /**
* PATCH请求 * PATCH请求 - data参数放到请求体
*/ */
patch<T = any>(url: string, data?: any, config?: CustomAxiosRequestConfig): Promise<AxiosResponse<ResultDomain<T>>> { patch<T = any>(url: string, data?: any, config?: CustomAxiosRequestConfig): Promise<AxiosResponse<ResultDomain<T>>> {
return request.patch<ResultDomain<T>>(url, data, config); return request.patch<ResultDomain<T>>(url, data, config);

View File

@@ -4,7 +4,7 @@
* @since 2025-10-06 * @since 2025-10-06
*/ */
import { api } from './index'; import { api } from '@/apis/index';
import type { LoginParam, LoginDomain } from '@/types'; import type { LoginParam, LoginDomain } from '@/types';
/** /**

View File

@@ -4,7 +4,7 @@
* @since 2025-10-06 * @since 2025-10-06
*/ */
import { api } from './index'; import { api } from '@/apis/index';
import type { SysMenu, MenuTreeNode } from '@/types'; import type { SysMenu, MenuTreeNode } from '@/types';
/** /**

View File

@@ -0,0 +1,66 @@
/**
* @description 权限相关API
* @author yslg
* @since 2025-10-06
*/
import { api } from '@/apis/index';
import { SysPermission } from '@/types';
export const permissionApi = {
/**
* @description 获取权限信息
* @param permission 权限
* @author yslg
* @ since 2025-10-08
*/
async getPermission(permission: SysPermission|null=null): Promise<SysPermission[]> {
const response = await api.get<SysPermission>('/permissions/permission', permission);
return response.data.dataList!;
},
/**
* @description 添加权限
* @param permission 权限
* @author yslg
* @ since 2025-10-08
*/
async addPermission(permission: SysPermission): Promise<SysPermission[]> {
const response = await api.post<SysPermission>('/permissions/permission', permission);
return response.data.dataList!;
},
/**
* @description 更新权限
* @param permission 权限
* @author yslg
* @ since 2025-10-08
*/
async updatePermission(permission: SysPermission): Promise<SysPermission[]> {
const response = await api.put<SysPermission>('/permissions/permission', permission);
return response.data.dataList!;
},
/**
* @description 删除权限
* @param permission 权限
* @author yslg
* @ since 2025-10-08
*/
async deletePermission(permission: SysPermission): Promise<SysPermission[]> {
const response = await api.delete<SysPermission>('/permissions/permission', permission);
return response.data.dataList!;
},
/**
* @description 获取权限列表
* @param permission 权限
* @author yslg
* @ since 2025-10-08
*/
async getPermissionList(permission: SysPermission|null=null): Promise<SysPermission[]> {
const response = await api.post<SysPermission>('/permissions/list', permission);
return response.data.dataList!;
}
}

View File

@@ -4,7 +4,7 @@
* @since 2025-10-06 * @since 2025-10-06
*/ */
import { api } from './index'; import { api } from '@/apis/index';
import type { SysUser, SysUserInfo, UserVO, PageDomain, PageParam } from '@/types'; import type { SysUser, SysUserInfo, UserVO, PageDomain, PageParam } from '@/types';
/** /**

View File

@@ -18,15 +18,10 @@ async function initApp() {
// 在路由初始化前,尝试恢复登录状态并生成动态路由 // 在路由初始化前,尝试恢复登录状态并生成动态路由
const authState = (store.state as any).auth; const authState = (store.state as any).auth;
console.log('[应用初始化] 检查登录状态...');
console.log('[应用初始化] Token:', !!authState.token);
console.log('[应用初始化] 菜单数量:', authState.menus?.length || 0);
if (authState.token && authState.menus && authState.menus.length > 0) { if (authState.token && authState.menus && authState.menus.length > 0) {
try { try {
console.log('[应用初始化] 开始生成动态路由...');
await store.dispatch('auth/generateRoutes'); await store.dispatch('auth/generateRoutes');
console.log('[应用初始化] 动态路由生成成功');
} catch (error) { } catch (error) {
console.error('[应用初始化] 动态路由生成失败:', error); console.error('[应用初始化] 动态路由生成失败:', error);
} }

View File

@@ -6,7 +6,7 @@
import { Module } from 'vuex'; import { Module } from 'vuex';
import { LoginDomain, SysMenu, SysPermission } from '@/types'; import { LoginDomain, SysMenu, SysPermission } from '@/types';
import { authApi } from '@/apis/auth'; import { authApi } from '@/apis/system/auth';
import router from '@/router'; import router from '@/router';
import { getFirstAccessibleMenuUrl, buildMenuTree } from '@/utils/route-generator'; import { getFirstAccessibleMenuUrl, buildMenuTree } from '@/utils/route-generator';

View File

@@ -10,6 +10,8 @@ import { BaseDTO } from '../base';
* 系统权限 * 系统权限
*/ */
export interface SysPermission extends BaseDTO { export interface SysPermission extends BaseDTO {
/** 主键ID */
id?:string;
/** 权限ID */ /** 权限ID */
permissionID?: string; permissionID?: string;
/** 权限名称 */ /** 权限名称 */
@@ -20,7 +22,9 @@ export interface SysPermission extends BaseDTO {
code?: string; code?: string;
/** 创建人 */ /** 创建人 */
creator?: string; creator?: string;
creatorName?: string;
/** 更新人 */ /** 更新人 */
updater?: string; updater?: string;
updaterName?: string;
} }

View File

@@ -92,7 +92,7 @@ import { useRouter, useRoute } from 'vue-router';
import { useStore } from 'vuex'; import { useStore } from 'vuex';
import { ElMessage, type FormInstance, type FormRules } from 'element-plus'; import { ElMessage, type FormInstance, type FormRules } from 'element-plus';
import type { LoginParam } from '@/types'; import type { LoginParam } from '@/types';
import { authApi } from '@/apis/auth'; import { authApi } from '@/apis/system/auth';
// 响应式引用 // 响应式引用
const loginFormRef = ref<FormInstance>(); const loginFormRef = ref<FormInstance>();

View File

@@ -0,0 +1,13 @@
<template>
<div>
菜单管理
</div>
</template>
<script setup lang="ts">
</script>
<style scoped lang="scss">
</style>

View File

@@ -0,0 +1,358 @@
<template>
<div class="permission-manage">
<div class="header">
<h2>权限管理</h2>
<el-button type="primary" @click="handleAdd">
<el-icon><Plus /></el-icon>
新增权限
</el-button>
</div>
<el-table
:data="permissionList"
style="width: 100%"
v-loading="loading"
border
stripe
>
<el-table-column prop="name" label="权限名称" min-width="150" />
<el-table-column prop="code" label="权限编码" min-width="200" />
<el-table-column prop="description" label="权限描述" min-width="200" show-overflow-tooltip />
<el-table-column prop="creatorName" label="创建人" width="120" />
<el-table-column prop="createTime" label="创建时间" width="180" />
<el-table-column label="操作" width="180" fixed="right">
<template #default="{ row }">
<el-button
type="primary"
size="small"
@click="handleEdit(row)"
link
>
编辑
</el-button>
<el-button
type="danger"
size="small"
@click="handleBindMenu(row)"
link
>
绑定菜单
</el-button>
<el-button
type="danger"
size="small"
@click="handleBindRole(row)"
link
>
绑定角色
</el-button>
<el-button
type="danger"
size="small"
@click="handleDelete(row)"
link
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 新增/编辑对话框 -->
<el-dialog
v-model="dialogVisible"
:title="isEdit ? '编辑权限' : '新增权限'"
width="500px"
@close="resetForm"
>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
>
<el-form-item label="权限名称" prop="name">
<el-input
v-model="formData.name"
placeholder="请输入权限名称"
clearable
/>
</el-form-item>
<el-form-item label="权限编码" prop="code">
<el-input
v-model="formData.code"
placeholder="请输入权限编码"
clearable
/>
</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>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button
type="primary"
@click="handleSubmit"
:loading="submitting"
>
确定
</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { permissionApi } from '@/apis/system/permission';
import { SysPermission } from '@/types';
import { ref, onMounted, reactive } from 'vue';
import { ElMessage, ElMessageBox, FormInstance, FormRules } from 'element-plus';
import { Plus } from '@element-plus/icons-vue';
// 数据状态
const permissionList = ref<SysPermission[]>([]);
const loading = ref(false);
const submitting = ref(false);
// 对话框状态
const dialogVisible = ref(false);
const isEdit = ref(false);
const formRef = ref<FormInstance>();
const queryFilter = ref<SysPermission>({
name: '',
code: '',
description: ''
});
// 表单数据
const formData = reactive<SysPermission>({
name: '',
code: '',
description: ''
});
// 表单验证规则
const formRules: FormRules = {
name: [
{ required: true, message: '请输入权限名称', trigger: 'blur' },
{ min: 2, max: 50, message: '权限名称长度在 2 到 50 个字符', trigger: 'blur' }
],
code: [
{ required: true, message: '请输入权限编码', trigger: 'blur' },
{
pattern: /^[a-zA-Z][a-zA-Z0-9:_-]*$/,
message: '权限编码只能包含字母、数字、冒号、下划线和横线,且必须以字母开头',
trigger: 'blur'
},
{ min: 2, max: 100, message: '权限编码长度在 2 到 100 个字符', trigger: 'blur' }
],
description: [
{ max: 200, message: '权限描述不能超过 200 个字符', trigger: 'blur' }
]
};
// 加载权限列表
async function loadPermissionList() {
try {
loading.value = true;
permissionList.value = await permissionApi.getPermissionList(queryFilter.value);
} catch (error) {
console.error('加载权限列表失败:', error);
ElMessage.error('加载权限列表失败');
} finally {
loading.value = false;
}
}
// 新增权限
function handleAdd() {
isEdit.value = false;
dialogVisible.value = true;
}
// 编辑权限
function handleEdit(row: SysPermission) {
isEdit.value = true;
Object.assign(formData, row);
dialogVisible.value = true;
}
// 查看绑定菜单
function handleBindMenu(row: SysPermission) {
console.log(row);
}
// 查看绑定角色
function handleBindRole(row: SysPermission) {
console.log(row);
}
// 删除权限
async function handleDelete(row: SysPermission) {
try {
await ElMessageBox.confirm(
`确定要删除权限 "${row.name}" 吗?`,
'删除确认',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
);
await permissionApi.deletePermission(row);
ElMessage.success('删除成功');
await loadPermissionList();
} catch (error) {
if (error !== 'cancel') {
console.error('删除权限失败:', error);
ElMessage.error('删除权限失败');
}
}
}
// 提交表单
async function handleSubmit() {
if (!formRef.value) return;
try {
await formRef.value.validate();
submitting.value = true;
if (isEdit.value) {
await permissionApi.updatePermission(formData);
ElMessage.success('更新成功');
} else {
await permissionApi.addPermission(formData);
ElMessage.success('新增成功');
}
dialogVisible.value = false;
await loadPermissionList();
} catch (error) {
if (error !== false) { // 表单验证失败时error为false
console.error('提交失败:', error);
ElMessage.error(isEdit.value ? '更新失败' : '新增失败');
}
} finally {
submitting.value = false;
}
}
// 重置表单
function resetForm() {
if (formRef.value) {
formRef.value.resetFields();
}
Object.assign(formData, {
name: '',
code: '',
description: ''
});
}
// 页面加载时获取权限列表
onMounted(() => {
loadPermissionList();
});
</script>
<style scoped lang="scss">
.permission-manage {
padding: 20px;
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
h2 {
margin: 0;
color: #303133;
font-size: 20px;
font-weight: 600;
}
}
.el-table {
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
border-radius: 4px;
}
}
// 对话框样式优化
:deep(.el-dialog) {
.el-dialog__header {
padding: 20px 20px 10px;
border-bottom: 1px solid #ebeef5;
.el-dialog__title {
font-size: 16px;
font-weight: 600;
color: #303133;
}
}
.el-dialog__body {
padding: 20px;
}
.el-dialog__footer {
padding: 10px 20px 20px;
border-top: 1px solid #ebeef5;
}
}
// 表单样式优化
:deep(.el-form) {
.el-form-item__label {
font-weight: 500;
color: #606266;
}
.el-input__wrapper {
box-shadow: 0 0 0 1px #dcdfe6 inset;
&:hover {
box-shadow: 0 0 0 1px #c0c4cc inset;
}
&.is-focus {
box-shadow: 0 0 0 1px #409eff inset;
}
}
.el-textarea__inner {
box-shadow: 0 0 0 1px #dcdfe6 inset;
&:hover {
box-shadow: 0 0 0 1px #c0c4cc inset;
}
&:focus {
box-shadow: 0 0 0 1px #409eff inset;
}
}
}
// 按钮样式优化
:deep(.el-button) {
&.is-link {
padding: 0;
margin-right: 12px;
&:last-child {
margin-right: 0;
}
}
}
</style>

View File

@@ -0,0 +1,13 @@
<template>
<div>
角色管理
</div>
</template>
<script setup lang="ts">
</script>
<style scoped lang="scss">
</style>