dev #8

Merged
hy merged 45 commits from dev into master 2025-08-26 19:03:29 +08:00
13 changed files with 236 additions and 305 deletions
Showing only changes of commit 3f354eae58 - Show all commits

View File

@ -30,3 +30,10 @@ export function MenuAll() {
export function Edit(info: MenuItem) {
return http.request<Res<MenuItem[]>>("post", `Menu/Edit`, { data: info });
}
/**
* @description
* @return {object}
*/
export function Del(ids: number[]) {
return http.request<Res<MenuItem[]>>("post", `Menu/Del`, { data: ids });
}

View File

@ -50,6 +50,8 @@ export interface ButtonCustomConfig {
export interface OperationButton {
/* 是否为头部按钮 */
topBtn: boolean;
/** 按钮权限码 */
perms?: string;
/* 是否显示 */
show?: boolean;
/* 按钮文本 */
@ -57,7 +59,7 @@ export interface OperationButton {
/* 按钮类型 */
btnType: "add" | "edit" | "del" | "custom";
/* 按钮样式 */
btnStyle?: "success" | "danger";
btnStyle?: "success" | "info" | "primary" | "danger" | "warning";
/* 自定义按钮配置 */
custom?: ButtonCustomConfig;
}
@ -112,6 +114,7 @@ export interface TableColumn {
multiple?: boolean;
/** 编辑时显示列 */
editShow?: boolean;
/**校验规则 */
rules?: any | Array<any>;
/** 显示列 */
show?: boolean;

View File

@ -116,8 +116,8 @@ function handleResetForm() {
function fetchInitData() {}
function fetchFormData() {
editData.value.loading = false;
handleResetForm();
if (editData.value.isedit) {
handleResetForm();
Api.Info({ id: props.id }).then(res => {
if (res.code === 200) {
editData.value.frorm = res.data;
@ -192,7 +192,7 @@ function fetchFormData() {
</div>
<div v-else-if="o.type.trim() == 'switch'">
<el-switch
v-model="o.valueE as string"
v-model="o.valueE as boolean"
active-text="启用"
inactive-text="禁用"
@change="o.change"

View File

@ -20,6 +20,9 @@ import { Dialog, TableColumn, TableConfig } from "./hTable";
import hTableEdit from "./hTableEdit.vue";
import { hTableAPI } from "@/api/hTable";
import { getenum } from "@/api/enum";
//
import { hasPerms } from "@/utils/auth";
const props = defineProps({
//** */
Row: {
@ -464,7 +467,9 @@ function fetchPagedData() {
<div v-if="table.operationTop" class="toolbar-container">
<!-- 头部按钮组 -->
<el-button
v-for="(e, i) in table.operationColumnData.filter(s => s.topBtn)"
v-for="(e, i) in table.operationColumnData.filter(
s => s.topBtn && hasPerms(s.perms)
)"
v-show="execute(e['show'], {}, e)"
:key="i"
:type="e.btnStyle || 'info'"
@ -495,7 +500,9 @@ function fetchPagedData() {
<template v-slot="scope">
<div class="columnTemplate">
<el-button
v-for="(e, i) in table.operationColumnData.filter(s => !s.topBtn)"
v-for="(e, i) in table.operationColumnData.filter(
s => !s.topBtn && hasPerms(s.perms)
)"
v-show="execute(e['show'], scope, e)"
:key="i"
class="btn"

View File

@ -84,8 +84,7 @@ export function setToken(data: DataInfo<Date>) {
permissions
});
}
if (data.userName && data.roles) {
if (data.userName && data.permissions) {
const { userName, roles } = data;
setUserKey({
avatar: data?.avatar ?? "",
@ -129,7 +128,7 @@ export const formatToken = (token: string): string => {
/** 是否有按钮级别的权限(根据登录接口返回的`permissions`字段进行判断)*/
export const hasPerms = (value: string | Array<string>): boolean => {
if (!value) return false;
if (!value) return true;
const allPerms = "*:*:*";
const { permissions } = useUserStoreHook();
if (!permissions) return false;

20
src/utils/roles.ts Normal file
View File

@ -0,0 +1,20 @@
export const ruleRequired = [
{ required: true, message: "不能为空", trigger: "blur" }
];
export const rulePassword = [
{ required: true, message: "不能为空", trigger: "blur" },
{ min: 6, message: "长度必须大于5", trigger: "blur" }
];
export const ruleAccount = [
{ required: true, message: "不能为空", trigger: "blur" },
{ min: 13, message: "长度必须大于12", trigger: "blur" }
];
export const rulePhone = [
{ required: true, message: "手机号不能为空", trigger: "blur" },
{
pattern: /^1[3-9]\d{9}$/,
message: "请输入正确的手机号",
trigger: "blur"
}
];

View File

@ -4,6 +4,12 @@ import { TableConfig } from "@/components/hTable/hTable";
import { onMounted, ref } from "vue";
import { fa } from "element-plus/es/locales.mjs";
import { hTableAPI } from "@/api/hTable";
import {
ruleAccount,
rulePassword,
rulePhone,
ruleRequired
} from "@/utils/roles";
const ControllerName = "Admin";
defineOptions({
@ -71,6 +77,7 @@ const tableData: TableConfig = {
name: {
label: "名称",
width: "180px",
rules: ruleRequired,
search: true,
searchType: "Like",
add: true, //
@ -78,6 +85,7 @@ const tableData: TableConfig = {
},
Phone: {
label: "手机号",
rules: rulePhone,
width: "200px",
search: true,
add: true, //
@ -85,6 +93,7 @@ const tableData: TableConfig = {
},
account: {
label: "账号",
rules: ruleAccount,
search: true,
add: true, //
edit: false //
@ -92,6 +101,8 @@ const tableData: TableConfig = {
password: {
label: "密码",
show: false,
/**长度必须大于6 */
rules: rulePassword,
search: false,
add: true, //
edit: false //
@ -100,13 +111,15 @@ const tableData: TableConfig = {
label: "启用",
type: "switch",
search: false,
add: false, //
add: true, //
edit: true, //
valueE: true //
},
roleId: {
label: "角色",
type: "dropdown",
rules: ruleRequired,
search: true,
add: true, //
edit: false, //

View File

@ -11,7 +11,6 @@ defineOptions({
});
function searchCallback(data) {}
const RoleApi = new hTableAPI("AdminRole");
const table = ref<{ initTable: (config: TableConfig) => void }>(null);
const tableData: TableConfig = {
apiUrl: ControllerName,
@ -29,6 +28,14 @@ const tableData: TableConfig = {
},
operationColumn: true, //
operationColumnData: [
{
topBtn: false, //
show: true,
label: "角色授权",
perms: "角色授权", //
btnType: "custom", // add edit del
btnStyle: "success" // topBtn: true success danger
},
{
//
topBtn: false, //
@ -71,7 +78,7 @@ const tableData: TableConfig = {
label: "启用",
type: "switch",
search: false,
add: false, //
add: true, //
edit: true, //
valueE: true //
},
@ -84,6 +91,8 @@ const tableData: TableConfig = {
},
remark: {
label: "备注",
type: "textarea",
editRows: 3,
search: false,
add: true, //
edit: true //

109
src/views/class/index.vue Normal file
View File

@ -0,0 +1,109 @@
<script setup lang="ts">
import ahTable from "@/components/hTable/index.vue";
import { TableConfig } from "@/components/hTable/hTable";
import { onMounted, ref } from "vue";
import { fa } from "element-plus/es/locales.mjs";
import { hTableAPI } from "@/api/hTable";
const ControllerName = "Class";
defineOptions({
name: ControllerName
});
function searchCallback(data) {}
const table = ref<{ initTable: (config: TableConfig) => void }>();
const tableData: TableConfig = {
apiUrl: ControllerName,
selectColumn: false, //
border: false, //
searchCallback: searchCallback,
search: {
//
show: true,
PageIndex: 0,
PageSize: 20,
OrderBy: "CreateTime", //
defaultConditions: [], //
Conditions: []
},
operationColumn: true, //
operationColumnData: [
{
//
topBtn: false, //
label: "修改",
btnType: "edit" // add edit del custom
},
{
//
topBtn: true, //
label: "添加",
btnStyle: "success",
btnType: "add" // add edit del custom
},
{
topBtn: false, //
show: true,
label: "删除",
btnType: "del", // add edit del
btnStyle: "danger" // topBtn: true success danger
}
],
column: {
//
id: {
label: "编号",
search: true,
add: false, //
edit: false, //
width: "150px"
},
name: {
label: "角色名称",
width: "180px",
search: true,
searchType: "Like",
add: true, //
edit: true //
},
enable: {
label: "启用",
type: "switch",
search: false,
add: false, //
edit: true, //
valueE: true //
},
createTime: {
label: "创建时间",
type: "datetime",
search: true,
add: false, //
edit: false //
},
remark: {
label: "备注",
search: false,
add: true, //
edit: true //
}
},
data: [],
pageData: {
total: 0
},
selectRows: []
};
const showTable = ref(false);
onMounted(async () => {
//
showTable.value = true;
});
</script>
<template>
<div><ahTable v-if="showTable" ref="table" :tableConfig="tableData" /></div>
</template>

View File

@ -17,6 +17,7 @@
class="menu-tree"
>
<template #default="{ node, data }">
<div v-if="!data.isButton" class="menu-node">
<div class="menu-node">
<i v-if="data.icon" :class="data.icon" class="menu-icon"></i>
<span class="menu-title">{{ data.title }}</span>
@ -36,7 +37,27 @@
<el-button type="primary" link @click="() => showDialog(data)">
编辑
</el-button>
<el-button type="danger" link @click="() => {}"> 删除 </el-button>
<el-button type="danger" link @click.stop="() => delMenu(data.id)">
删除
</el-button>
</div>
</div>
<div v-else class="menu-node">
<div>
<i v-if="data.icon" :class="data.icon" class="menu-icon"></i>
<span>菜单按钮权限: </span>
<el-button type="primary">
{{ data.title }}
</el-button>
</div>
<div style="display: flex; gap: 6px">
<el-button type="primary" link @click="() => showDialog(data)">
编辑
</el-button>
<el-button type="danger" link @click.stop="() => delMenu(data.id)">
删除
</el-button>
</div>
</div>
</template>
</el-tree>
@ -57,7 +78,7 @@
</template>
<script setup lang="ts">
import { MenuAll, MenuItem } from "@/api/menu";
import { MenuAll, MenuItem, Del } from "@/api/menu";
import { ElMessage, ElMessageBox } from "element-plus";
import MenuEdit from "./edit.vue";
@ -90,6 +111,23 @@ const handleClose = (done: () => void) => {
// });
};
async function delMenu(menuId: number) {
try {
await ElMessageBox.confirm("确定要删除此菜单?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
});
const res = await Del([menuId]);
if (res.code === 200) {
ElMessage.success(res.message);
await fetchInitData();
} else {
ElMessage.error(res.message);
}
} catch (error) {}
}
//
const convertToTree = (menus: MenuItem[]): MenuItem[] => {
const menuMap = new Map<number, MenuItem>();

View File

@ -1,99 +0,0 @@
<script setup lang="ts">
import { hasAuth, getAuths } from "@/router/utils";
defineOptions({
name: "PermissionButtonRouter"
});
</script>
<template>
<div>
<p class="mb-2!">当前拥有的code列表{{ getAuths() }}</p>
<el-card shadow="never" class="mb-2">
<template #header>
<div class="card-header">组件方式判断权限</div>
</template>
<el-space wrap>
<Auth value="permission:btn:add">
<el-button plain type="warning">
拥有code'permission:btn:add' 权限可见
</el-button>
</Auth>
<Auth :value="['permission:btn:edit']">
<el-button plain type="primary">
拥有code['permission:btn:edit'] 权限可见
</el-button>
</Auth>
<Auth
:value="[
'permission:btn:add',
'permission:btn:edit',
'permission:btn:delete'
]"
>
<el-button plain type="danger">
拥有code['permission:btn:add', 'permission:btn:edit',
'permission:btn:delete'] 权限可见
</el-button>
</Auth>
</el-space>
</el-card>
<el-card shadow="never" class="mb-2">
<template #header>
<div class="card-header">函数方式判断权限</div>
</template>
<el-space wrap>
<el-button v-if="hasAuth('permission:btn:add')" plain type="warning">
拥有code'permission:btn:add' 权限可见
</el-button>
<el-button v-if="hasAuth(['permission:btn:edit'])" plain type="primary">
拥有code['permission:btn:edit'] 权限可见
</el-button>
<el-button
v-if="
hasAuth([
'permission:btn:add',
'permission:btn:edit',
'permission:btn:delete'
])
"
plain
type="danger"
>
拥有code['permission:btn:add', 'permission:btn:edit',
'permission:btn:delete'] 权限可见
</el-button>
</el-space>
</el-card>
<el-card shadow="never">
<template #header>
<div class="card-header">
指令方式判断权限该方式不能动态修改权限
</div>
</template>
<el-space wrap>
<el-button v-auth="'permission:btn:add'" plain type="warning">
拥有code'permission:btn:add' 权限可见
</el-button>
<el-button v-auth="['permission:btn:edit']" plain type="primary">
拥有code['permission:btn:edit'] 权限可见
</el-button>
<el-button
v-auth="[
'permission:btn:add',
'permission:btn:edit',
'permission:btn:delete'
]"
plain
type="danger"
>
拥有code['permission:btn:add', 'permission:btn:edit',
'permission:btn:delete'] 权限可见
</el-button>
</el-space>
</el-card>
</div>
</template>

View File

@ -1,109 +0,0 @@
<script setup lang="ts">
import { hasPerms } from "@/utils/auth";
import { useUserStoreHook } from "@/store/modules/user";
const { permissions } = useUserStoreHook();
defineOptions({
name: "PermissionButtonLogin"
});
</script>
<template>
<div>
<p class="mb-2!">当前拥有的code列表{{ permissions }}</p>
<p v-show="permissions?.[0] === '*:*:*'" class="mb-2!">
*:*:* 代表拥有全部按钮级别权限
</p>
<el-card shadow="never" class="mb-2">
<template #header>
<div class="card-header">组件方式判断权限</div>
</template>
<el-space wrap>
<Perms value="permission:btn:add">
<el-button plain type="warning">
拥有code'permission:btn:add' 权限可见
</el-button>
</Perms>
<Perms :value="['permission:btn:edit']">
<el-button plain type="primary">
拥有code['permission:btn:edit'] 权限可见
</el-button>
</Perms>
<Perms
:value="[
'permission:btn:add',
'permission:btn:edit',
'permission:btn:delete'
]"
>
<el-button plain type="danger">
拥有code['permission:btn:add', 'permission:btn:edit',
'permission:btn:delete'] 权限可见
</el-button>
</Perms>
</el-space>
</el-card>
<el-card shadow="never" class="mb-2">
<template #header>
<div class="card-header">函数方式判断权限</div>
</template>
<el-space wrap>
<el-button v-if="hasPerms('permission:btn:add')" plain type="warning">
拥有code'permission:btn:add' 权限可见
</el-button>
<el-button
v-if="hasPerms(['permission:btn:edit'])"
plain
type="primary"
>
拥有code['permission:btn:edit'] 权限可见
</el-button>
<el-button
v-if="
hasPerms([
'permission:btn:add',
'permission:btn:edit',
'permission:btn:delete'
])
"
plain
type="danger"
>
拥有code['permission:btn:add', 'permission:btn:edit',
'permission:btn:delete'] 权限可见
</el-button>
</el-space>
</el-card>
<el-card shadow="never">
<template #header>
<div class="card-header">
指令方式判断权限该方式不能动态修改权限
</div>
</template>
<el-space wrap>
<el-button v-perms="'permission:btn:add'" plain type="warning">
拥有code'permission:btn:add' 权限可见
</el-button>
<el-button v-perms="['permission:btn:edit']" plain type="primary">
拥有code['permission:btn:edit'] 权限可见
</el-button>
<el-button
v-perms="[
'permission:btn:add',
'permission:btn:edit',
'permission:btn:delete'
]"
plain
type="danger"
>
拥有code['permission:btn:add', 'permission:btn:edit',
'permission:btn:delete'] 权限可见
</el-button>
</el-space>
</el-card>
</div>
</template>

View File

@ -1,66 +0,0 @@
<script setup lang="ts">
import { initRouter } from "@/router/utils";
import { storageLocal } from "@pureadmin/utils";
import { type CSSProperties, ref, computed } from "vue";
import { useUserStoreHook } from "@/store/modules/user";
import { usePermissionStoreHook } from "@/store/modules/permission";
defineOptions({
name: "PermissionPage"
});
const elStyle = computed((): CSSProperties => {
return {
width: "85vw",
justifyContent: "start"
};
});
const userName = ref(useUserStoreHook()?.userName);
const options = [
{
value: "admin",
label: "管理员角色"
},
{
value: "common",
label: "普通角色"
}
];
function onChange() {
useUserStoreHook()
.loginByUsername({ userName: userName.value, password: "admin123" })
.then(res => {
if (res.code === 200) {
storageLocal().removeItem("async-routes");
usePermissionStoreHook().clearAllCachePage();
initRouter();
}
});
}
</script>
<template>
<div>
<p class="mb-2!">
模拟后台根据不同角色返回对应路由观察左侧菜单变化管理员角色可查看系统管理菜单普通角色不可查看系统管理菜单
</p>
<el-card shadow="never" :style="elStyle">
<template #header>
<div class="card-header">
<span>当前角色{{ userName }}</span>
</div>
</template>
<el-select v-model="userName" class="w-[160px]!" @change="onChange">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-card>
</div>
</template>