fix:调整Table组件 完善权限管理-字典管理
This commit is contained in:
@@ -22,7 +22,7 @@
|
||||
<slot></slot>
|
||||
</div>
|
||||
</el-popover>
|
||||
<div class="mj-icon-item" title="下载" @click="$emit('download')" v-if="download">
|
||||
<div class="mj-icon-item" title="下载" @click="onDownload" v-if="download">
|
||||
<el-icon><Download /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
@@ -53,6 +53,12 @@ defineExpose({
|
||||
filterPopover.value?.hide()
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
const onDownload = () => {
|
||||
ElMessage.warning("功能开发中...");
|
||||
emit("download");
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -31,8 +31,9 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 空数据 -->
|
||||
<template #empty>
|
||||
<el-empty description="暂无相关数据" :image-size="100" />
|
||||
<el-empty description="暂无数据" :image-size="100" />
|
||||
</template>
|
||||
</el-table-v2>
|
||||
</div>
|
||||
@@ -40,7 +41,7 @@
|
||||
|
||||
<script setup lang="tsx">
|
||||
import { ref, computed, onMounted, onUnmounted, nextTick } from "vue";
|
||||
import { ElTag, ElButton, ElText,ElTooltip } from "element-plus";
|
||||
import { ElTag, ElButton, ElText, ElTooltip } from "element-plus";
|
||||
import { debounce } from "lodash-es";
|
||||
import dayjs from "dayjs";
|
||||
import { DictManage } from "@/dict";
|
||||
@@ -64,10 +65,50 @@ const total = ref(0);
|
||||
const pageNo = ref(1);
|
||||
const tableSize = ref({ width: 0, height: 400 });
|
||||
|
||||
const noMore = computed(
|
||||
() => innerData.value.length >= total.value && total.value > 0
|
||||
);
|
||||
const noMore = computed(() => {
|
||||
if (total.value <= 0) return false;
|
||||
const isReachTotal = innerData.value.length >= total.value;
|
||||
return isReachTotal;
|
||||
});
|
||||
// --- 1. 核心渲染工厂 (内置常用业务组件) ---
|
||||
// 定义一个内部小组件处理溢出逻辑
|
||||
const OverflowTooltip = defineComponent({
|
||||
props: {
|
||||
val: { type: String, default: "" }
|
||||
},
|
||||
setup(props) {
|
||||
const isOverflow = ref(false);
|
||||
const textRef = ref<HTMLElement | null>(null);
|
||||
|
||||
const checkOverflow = () => {
|
||||
if (textRef.value) {
|
||||
// 计算溢出
|
||||
isOverflow.value = textRef.value.scrollWidth > textRef.value.clientWidth;
|
||||
}
|
||||
};
|
||||
|
||||
return () => (
|
||||
<ElTooltip
|
||||
content={props.val}
|
||||
placement="top"
|
||||
effect="dark"
|
||||
disabled={!isOverflow.value}
|
||||
>
|
||||
{{
|
||||
default: () => (
|
||||
<div
|
||||
ref={textRef}
|
||||
class="mj-ellipsis-cell"
|
||||
onMouseenter={checkOverflow}
|
||||
>
|
||||
{props.val}
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
</ElTooltip>
|
||||
);
|
||||
}
|
||||
});
|
||||
const RenderFactory = {
|
||||
// 状态点/标签
|
||||
status: (scope: any, col: any) => {
|
||||
@@ -114,21 +155,8 @@ const RenderFactory = {
|
||||
// 文本自动省略
|
||||
ellipsis: (scope: any, col: any) => {
|
||||
const val = scope.rowData[col.prop] ?? "-";
|
||||
return (
|
||||
<ElTooltip
|
||||
effect="dark"
|
||||
content={String(val)}
|
||||
placement="top"
|
||||
// 只有当内容不是 "-" 且达到截断条件时才显示(可选优化)
|
||||
fallback-placements={['bottom', 'top']}
|
||||
enterable={false}
|
||||
>
|
||||
<div class="mj-ellipsis-cell">
|
||||
{val}
|
||||
</div>
|
||||
</ElTooltip>
|
||||
);
|
||||
},
|
||||
return <OverflowTooltip val={val} />;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -149,7 +177,7 @@ const updateRow = (id: string | number, rowData: object) => {
|
||||
* @param id 唯一标识
|
||||
*/
|
||||
const removeRow = (id: string | number) => {
|
||||
const index = innerData.value.findIndex(item => item.id == id);
|
||||
const index = innerData.value.findIndex((item) => item.id == id);
|
||||
if (index !== -1) {
|
||||
innerData.value.splice(index, 1);
|
||||
total.value = Math.max(0, total.value - 1);
|
||||
@@ -190,9 +218,10 @@ const adaptedColumns = computed(() => {
|
||||
// 4. 计算剩余可用宽度
|
||||
const remainingWidth = Math.max(containerWidth - fixedWidthTotal, 0);
|
||||
// 5. 计算每个自适应列应该分配到的平均宽度
|
||||
const perAutoWidth = autoColumns.length > 0
|
||||
? Math.floor(remainingWidth / autoColumns.length)
|
||||
: 0;
|
||||
const perAutoWidth =
|
||||
autoColumns.length > 0
|
||||
? Math.floor(remainingWidth / autoColumns.length)
|
||||
: 0;
|
||||
return props.columns.map((col: any) => {
|
||||
const isAuto = !col.width;
|
||||
const finalWidth = isAuto ? Math.max(perAutoWidth, 100) : Number(col.width);
|
||||
@@ -206,14 +235,14 @@ const adaptedColumns = computed(() => {
|
||||
fixed: col.fixed,
|
||||
align: col.align || "left",
|
||||
cellRenderer: (scope: any) => {
|
||||
const isOp = col.prop === 'actions' || col.valueType === 'actions';
|
||||
const cellClass = isOp ? 'operation-column-cell' : '';
|
||||
const isOp = col.prop === "actions" || col.valueType === "actions";
|
||||
const cellClass = isOp ? "operation-column-cell" : "";
|
||||
// 2. 如果是操作列,包裹一层带 class 的 div
|
||||
if (isOp && col.actions) {
|
||||
return (
|
||||
<div class={cellClass}>
|
||||
<div class="v2-operation-btns">
|
||||
{col.actions.map(btn => {
|
||||
{col.actions.map((btn) => {
|
||||
// --- 权限校验开始 ---
|
||||
if (btn.permission) {
|
||||
const hasAuth = checkPermission(btn.permission);
|
||||
@@ -221,12 +250,27 @@ const adaptedColumns = computed(() => {
|
||||
}
|
||||
|
||||
// 2. 现有的 show 逻辑判断(业务逻辑显隐)
|
||||
const isShow = typeof btn.show === 'function' ? btn.show(scope.rowData) : true;
|
||||
const isShow =
|
||||
typeof btn.show === "function"
|
||||
? btn.show(scope.rowData)
|
||||
: true;
|
||||
const isDisabled =
|
||||
typeof btn.disabled === "function"
|
||||
? btn.disabled(scope.rowData)
|
||||
: false;
|
||||
if (!isShow) return null;
|
||||
const { onClick, label, permission, show, ...otherProps } = btn;
|
||||
const {
|
||||
onClick,
|
||||
label,
|
||||
permission,
|
||||
show,
|
||||
disabled,
|
||||
...otherProps
|
||||
} = btn;
|
||||
return (
|
||||
<ElButton
|
||||
{...otherProps}
|
||||
disabled={isDisabled}
|
||||
onClick={() => btn.onClick(scope.rowData)}
|
||||
>
|
||||
{btn.label}
|
||||
@@ -241,7 +285,9 @@ const adaptedColumns = computed(() => {
|
||||
if (col.valueType && RenderFactory[col.valueType]) {
|
||||
return RenderFactory[col.valueType](scope, col);
|
||||
}
|
||||
return <span class="v2-cell-text">{scope.rowData[col.prop] ?? "-"}</span>;
|
||||
return (
|
||||
<span class="v2-cell-text">{scope.rowData[col.prop] ?? "-"}</span>
|
||||
);
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -254,8 +300,7 @@ const fetchTableData = async (isReset = false) => {
|
||||
|
||||
if (isReset) {
|
||||
pageNo.value = 1;
|
||||
noMore.value = false;
|
||||
// 重置时不立即清空数据,防止闪烁
|
||||
innerData.value = [];
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
@@ -270,9 +315,6 @@ const fetchTableData = async (isReset = false) => {
|
||||
|
||||
innerData.value = isReset ? records : [...innerData.value, ...records];
|
||||
total.value = res?.total || 0;
|
||||
if (innerData.value.length >= total.value || records.length < props.pageSize) {
|
||||
noMore.value = true;
|
||||
}
|
||||
pageNo.value++;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
@@ -321,9 +363,9 @@ defineExpose({
|
||||
getCurrentParams: () => ({
|
||||
pageNo: pageNo.value - 1, // 因为 fetch 完后 pageNo 会自增,所以要减 1 才是当前页
|
||||
pageSize: props.pageSize,
|
||||
...props.initParam
|
||||
...props.initParam,
|
||||
}),
|
||||
innerData
|
||||
innerData,
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -334,7 +376,9 @@ defineExpose({
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
/* 移除外边框,让样式更融入页面 */
|
||||
|
||||
.el-table-v2 {
|
||||
box-shadow: 0 0 6px #eef1f6;
|
||||
}
|
||||
// 1. 初始全屏 Loading
|
||||
.v2-initial-loading {
|
||||
position: absolute;
|
||||
@@ -394,6 +438,7 @@ defineExpose({
|
||||
// 适配 el-table-v2 核心样式
|
||||
:deep(.el-table-v2__main) {
|
||||
background: #fff;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:deep(.el-table-v2__header-wrapper) {
|
||||
@@ -420,7 +465,9 @@ defineExpose({
|
||||
transition: opacity 0.2s;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
.el-button + .el-button {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
<div class="mj-standard-menu">
|
||||
<el-menu
|
||||
:default-active="activeIndex"
|
||||
:active-text-color="mode === 'horizontal' ? '#409EFF' : undefined"
|
||||
class="mj-menu"
|
||||
:class="['mj-menu',menuClass]"
|
||||
:mode="mode"
|
||||
:collapse="isCollapse"
|
||||
@select="handleMenuSelect"
|
||||
@@ -20,7 +19,7 @@
|
||||
<template #title>{{ row.meta.title }}</template>
|
||||
</el-menu-item>
|
||||
</el-sub-menu>
|
||||
<el-menu-item v-else :index="getFirstChildPath(item)">
|
||||
<el-menu-item v-else :index="getFirstChildPath(item)" :disabled="item.meta.disabled">
|
||||
<el-icon v-if="item.meta?.icon"><component :is="item.meta.icon" /></el-icon>
|
||||
<template #title>{{ item.meta.title }}</template>
|
||||
</el-menu-item>
|
||||
@@ -32,11 +31,12 @@
|
||||
defineOptions({ name: "standardMenu" })
|
||||
const route = useRoute();
|
||||
|
||||
const {mode="vertical",menuList,isCollapse,activeMenu} = defineProps<{
|
||||
const {mode="vertical",menuList,isCollapse,activeMenu,menuClass} = defineProps<{
|
||||
mode?: 'vertical' | 'horizontal'
|
||||
menuList: any[]
|
||||
isCollapse?:boolean
|
||||
activeMenu?: string
|
||||
menuClass?: string
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -112,7 +112,7 @@ const handleMenuSelect = (index: string) => {
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
width: auto;
|
||||
max-width: 1000px; // 设置一个足够大的值
|
||||
max-width: 1000px;
|
||||
overflow: hidden;
|
||||
transition: max-width 0.25s, opacity 0.25s, width 0.25s;
|
||||
opacity: 1;
|
||||
@@ -150,5 +150,43 @@ const handleMenuSelect = (index: string) => {
|
||||
flex-shrink: 0;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
// 菜单样式类
|
||||
.mj-top_menu{
|
||||
--el-menu-hover-bg-color:transparent;
|
||||
--el-menu-active-color:var(--el-color-primary);
|
||||
--el-menu-text-color:#62748E;
|
||||
}
|
||||
|
||||
.mj-aside_menu{
|
||||
--el-menu-hover-bg-color:#F8FAFC;
|
||||
--el-menu-item-height:46px;
|
||||
--el-menu-item-padding:20px;
|
||||
--el-menu-item-border-color:#DBEAFE;
|
||||
--el-menu-item-active-color:#EFF6FF;
|
||||
padding: 0 12px;
|
||||
.el-menu-item{
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
.el-menu-item.is-active{
|
||||
background-color: var(--el-menu-item-active-color);
|
||||
border: 1px solid var(--el-menu-item-border-color);
|
||||
border-radius: var(--el-menu-item-padding);
|
||||
}
|
||||
.el-menu-item:hover{
|
||||
border-radius: var(--el-menu-item-padding);
|
||||
transition: transform .12s ease;
|
||||
.el-icon{
|
||||
transform: scale(1.08);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 收缩菜单样式
|
||||
.el-menu--collapse{
|
||||
--el-menu-hover-bg-color:transparent;
|
||||
--el-menu-item-border-color:transparent;
|
||||
--el-menu-item-active-color:transparent;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -12,7 +12,7 @@
|
||||
{{ topTitle }}
|
||||
</div>
|
||||
<standardMenu
|
||||
class="mj-aside_menu"
|
||||
menu-class="mj-aside_menu"
|
||||
:isCollapse="isCollapse"
|
||||
:menuList="sideMenuList"
|
||||
:active-menu="selectedActiveMenu"
|
||||
@@ -27,14 +27,15 @@
|
||||
</el-aside>
|
||||
<el-container>
|
||||
<el-header class="mj-header-content">
|
||||
<!-- 左侧的菜单展示 -->
|
||||
<!-- 顶部左侧的菜单展示 -->
|
||||
<standardMenu
|
||||
menu-class="mj-top_menu"
|
||||
:menuList="topLevelMenuList"
|
||||
mode="horizontal"
|
||||
:active-menu="selectedTopMenu"
|
||||
@menu-select="handleTopMenuSelect"
|
||||
/>
|
||||
<!-- 右侧用户的内容 -->
|
||||
<!-- 顶部右侧用户的内容 -->
|
||||
<rightMenuGroup @on-stage-manage="(path)=>handleTopMenuSelect(path)" />
|
||||
</el-header>
|
||||
<el-main class="mj-main-backend-content">
|
||||
@@ -96,7 +97,15 @@ const menuList = computed(() => {
|
||||
const topLevelMenuList = computed(() => {
|
||||
return menuList.value.map((item) => {
|
||||
const { children, ...rest } = item;
|
||||
return rest;
|
||||
const hasChildren = children && Array.isArray(children) && children.length > 0;
|
||||
const enhancedMeta = {
|
||||
...rest.meta,
|
||||
disabled: !hasChildren
|
||||
};
|
||||
return {
|
||||
...rest,
|
||||
meta: enhancedMeta
|
||||
};
|
||||
}).filter(itv=>itv.name !== 'stage');
|
||||
});
|
||||
|
||||
@@ -272,7 +281,7 @@ onUnmounted(() => {
|
||||
.mj-aside-title {
|
||||
font-size: 10px;
|
||||
color: #888;
|
||||
padding: 10px var(--el-menu-base-level-padding);
|
||||
padding: 10px calc(var(--el-menu-base-level-padding) + 16px);
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
placeholder="搜索字段名称..."
|
||||
class="custom-search-input auto-expand-input"
|
||||
:prefix-icon="Search"
|
||||
@keyup.enter="onConfirmSuccess"
|
||||
@keyup.enter="onSearchData"
|
||||
/>
|
||||
</div>
|
||||
<!-- 状态筛选的内容 -->
|
||||
@@ -55,7 +55,7 @@
|
||||
<el-option
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
v-for="(item, index) in DictManage.statusOptions"
|
||||
v-for="(item, index) in dicts.permission_list_enable_disable"
|
||||
:key="index"
|
||||
/>
|
||||
</el-select>
|
||||
@@ -64,7 +64,7 @@
|
||||
<el-button
|
||||
type="primary"
|
||||
class="apply-btn"
|
||||
@click="onConfirmSuccess"
|
||||
@click="onSearchData"
|
||||
>应用筛选</el-button
|
||||
>
|
||||
</div>
|
||||
@@ -86,19 +86,9 @@
|
||||
<!-- Table列表 -->
|
||||
<CommonTable
|
||||
ref="tableRef"
|
||||
:data="list"
|
||||
:columns="columns"
|
||||
:total="total"
|
||||
:request-api="fetchData"
|
||||
>
|
||||
<!-- 名称点击 -->
|
||||
<!-- <template #labelName="{ row }">
|
||||
<el-button link type="primary" @click="onLevelNext(row)" v-if="!hasChild">{{
|
||||
row.label
|
||||
}}</el-button>
|
||||
|
||||
<span v-else>{{ row.label }}</span>
|
||||
</template> -->
|
||||
</CommonTable>
|
||||
<!-- 新增字段 -->
|
||||
<dictFieldLevelManage
|
||||
@@ -126,6 +116,7 @@ import dictFieldLevelManage from "./dictFieldLevelManage.vue";
|
||||
import { DictManage } from "@/dict";
|
||||
import { useTableAction } from "@/hooks/useTableAction";
|
||||
import { useDict } from "@/hooks/useDictData";
|
||||
// import { formatIndex } from "@/utils/utils";
|
||||
const { dicts, refresh } = useDict("permission_list_enable_disable");
|
||||
|
||||
import {
|
||||
@@ -149,8 +140,6 @@ const tableRef = ref(null);
|
||||
const { handleAction, handleDelete: runDelete } = useTableAction(tableRef);
|
||||
const visible = ref<boolean>(false);
|
||||
const parentId = ref<string>("");
|
||||
const total = ref(0);
|
||||
const list = ref([]);
|
||||
const hasChild = ref<boolean>(false); //是否是子级弹窗
|
||||
const childId = ref<string | number>(""); // 子集的id
|
||||
const childModals = ref([]); //子弹窗的列表
|
||||
@@ -160,7 +149,7 @@ const columns = computed(() => [
|
||||
{
|
||||
prop: "id",
|
||||
align:'center',
|
||||
label: "字典编码",
|
||||
label: "字典编码"
|
||||
},
|
||||
{
|
||||
prop: "label",
|
||||
@@ -341,10 +330,15 @@ const onCloseFilter = () => {
|
||||
filterForm.status = "";
|
||||
};
|
||||
|
||||
// 筛选
|
||||
const onSearchData = () =>{
|
||||
tableRef.value?.refresh();
|
||||
}
|
||||
|
||||
// 筛选重置
|
||||
const onReset = () => {
|
||||
onCloseFilter();
|
||||
onConfirmSuccess();
|
||||
onSearchData();
|
||||
};
|
||||
|
||||
// 添加二级字段
|
||||
|
||||
@@ -66,8 +66,6 @@
|
||||
<CommonTable
|
||||
ref="dictTableRef"
|
||||
:columns="columns"
|
||||
:data="dataValue"
|
||||
:total="total"
|
||||
:request-api="getTableData"
|
||||
>
|
||||
</CommonTable>
|
||||
@@ -109,7 +107,6 @@ const dictTableRef = ref(null);
|
||||
const { handleAction, handleDelete: runDelete } = useTableAction(dictTableRef);
|
||||
const dictVisible = ref<boolean>(false);
|
||||
const searchVal = ref<string>("");
|
||||
const total = ref<number>(0);
|
||||
|
||||
const filterForm = reactive({
|
||||
status: "",
|
||||
@@ -208,9 +205,6 @@ const columns = [
|
||||
},
|
||||
];
|
||||
|
||||
// 返回的data数据信息
|
||||
const dataValue = ref([]);
|
||||
|
||||
// popover关闭事件
|
||||
const onPopoverHide = () => {
|
||||
filterForm.status = "";
|
||||
@@ -275,14 +269,14 @@ const onConfirmSuccess = async () => {
|
||||
Promise.resolve(true),
|
||||
selectItem.id,
|
||||
getTableData,
|
||||
{ showMsg: false, silentUpdate: true }
|
||||
{ showMsg: false }
|
||||
);
|
||||
}
|
||||
else {
|
||||
dictTableRef.value?.refresh();
|
||||
}
|
||||
};
|
||||
// TODO:字段配置
|
||||
// 字段配置
|
||||
const handlefieldsConfig = (ite) => {
|
||||
fieldsConfigRef.value.open({ ...ite, parentId: ite.id });
|
||||
};
|
||||
|
||||
@@ -31,39 +31,8 @@
|
||||
<CommonTable
|
||||
ref="tableRef"
|
||||
:columns="tableColumns"
|
||||
v-model:data="dataList"
|
||||
v-model:total="total"
|
||||
pagination
|
||||
:request-api="getTableData"
|
||||
>
|
||||
<!-- 状态 -->
|
||||
<template #status="{ row }">
|
||||
<div
|
||||
class="mj-status-dot"
|
||||
:style="{
|
||||
'--data-status-color': DictManage.statusDictColor[row.status],
|
||||
}"
|
||||
@click="handleDictStatus(row)"
|
||||
>
|
||||
{{ dicts.permission_list_enable_disable.find(item=>item.value == row.status)?.label }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 类型 -->
|
||||
<template #type="{ row }">
|
||||
<el-tag type="primary">{{ row.type }}</el-tag>
|
||||
</template>
|
||||
|
||||
<!-- 成员数量 -->
|
||||
<template #member="{ row }">
|
||||
<el-button
|
||||
link
|
||||
icon="UserFilled"
|
||||
type="primary"
|
||||
@click.stop="addMember(row)"
|
||||
>{{ row.memberCount }}</el-button
|
||||
>
|
||||
</template>
|
||||
</CommonTable>
|
||||
</div>
|
||||
|
||||
@@ -82,16 +51,19 @@
|
||||
/>
|
||||
|
||||
<!-- 权限抽屉 -->
|
||||
<permission-drawer v-model:visible="showPermission" :checkAuth="selectMember"/>
|
||||
<permission-drawer
|
||||
v-model:visible="showPermission"
|
||||
:checkAuth="selectMember"
|
||||
/>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import dayjs from "dayjs";
|
||||
import CommonTable from "@/components/proTable/index.vue";
|
||||
import CommonTable from "@/components/proTable/proTablev2.vue";
|
||||
import memberSelector from "@/components/memberSelector/index.vue";
|
||||
import addRoles from "./addRoles.vue";
|
||||
import permissionDrawer from "./permissionDrawer.vue";
|
||||
import { DictManage } from "@/dict";
|
||||
import { useDict } from '@/hooks/useDictData';
|
||||
import { useDict } from "@/hooks/useDictData";
|
||||
import { formatIndex } from "@/utils/utils";
|
||||
import {
|
||||
getRoleList,
|
||||
@@ -103,15 +75,16 @@ import {
|
||||
disableRole,
|
||||
getRoleMemberList,
|
||||
copyRolePermission,
|
||||
batchSaveRole
|
||||
batchSaveRole,
|
||||
} from "@/api/stage/permission/index.ts";
|
||||
import { useLocalManager } from "@/hooks/useLocalManager";
|
||||
import { useTableAction } from "@/hooks/useTableAction";
|
||||
defineOptions({ name: "PermissionManagement" });
|
||||
const { dicts,refresh } = useDict('permission_list_enable_disable');
|
||||
const { dicts, refresh } = useDict("permission_list_enable_disable");
|
||||
const activeTab = ref(1);
|
||||
const tableRef = ref(null);
|
||||
const { handleAction, handleDelete } = useTableAction(tableRef);
|
||||
const searchVal = ref("");
|
||||
const dataList = ref([]);
|
||||
const memberList = ref([]); // 获取成员角色列表
|
||||
const total = ref(0);
|
||||
const showMember = ref(false);
|
||||
@@ -134,61 +107,78 @@ const roleColumns = [
|
||||
label: "编号",
|
||||
width: "80",
|
||||
align: "center",
|
||||
formatter: (val) => {
|
||||
return `#${formatIndex(val.id)}`;
|
||||
render: ({ rowData, rowIndex }) => {
|
||||
return h("span", `#${formatIndex(rowIndex)}`);
|
||||
},
|
||||
},
|
||||
{
|
||||
prop: "name",
|
||||
label: "角色名称",
|
||||
align: "center",
|
||||
showOverflowTooltip: true,
|
||||
valueType: "ellipsis",
|
||||
width: 150,
|
||||
},
|
||||
{
|
||||
prop: "memberCount",
|
||||
label: "成员数量",
|
||||
align: "center",
|
||||
slot: "member",
|
||||
render: ({ rowData }: any) => {
|
||||
return h(
|
||||
ElButton,
|
||||
{
|
||||
type: "primary",
|
||||
link: true,
|
||||
icon: "UserFilled",
|
||||
onClick: (e) => {
|
||||
e.stopPropagation();
|
||||
addMember(rowData);
|
||||
},
|
||||
},
|
||||
() => rowData.memberCount
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
prop: "permissionCount",
|
||||
label: "权限数量",
|
||||
align: "center",
|
||||
formatter: (val) => {
|
||||
return val.permissionCount ? `${val.permissionCount}项` : "-";
|
||||
},
|
||||
render:({rowData})=>{
|
||||
return h('span',rowData.permissionCount ? `${rowData.permissionCount}项` : "-")
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: "status",
|
||||
label: "状态",
|
||||
align: "center",
|
||||
slot: "status",
|
||||
valueType: "status",
|
||||
options: computed(() => dicts.value.permission_list_enable_disable),
|
||||
onClick: ({ cellValue, rowData }) => {
|
||||
handleDictStatus(rowData);
|
||||
},
|
||||
},
|
||||
{
|
||||
prop: "type",
|
||||
label: "角色类型",
|
||||
align: "center",
|
||||
slot: "type",
|
||||
render: ({ rowData }: any) => {
|
||||
return h(ElTag, { size: "small" }, () => rowData.type);
|
||||
},
|
||||
},
|
||||
{
|
||||
prop: "positionIds",
|
||||
label: "关联岗位",
|
||||
align: "center",
|
||||
showOverflowTooltip: true,
|
||||
formatter: (val) => {
|
||||
return val.positionIds ? val.positionIds.join(",") : "-";
|
||||
valueType: "ellipsis",
|
||||
render: ({ rowData, rowIndex }) => {
|
||||
return h("span", rowData.positionIds ? rowData.positionIds.join(",") : "-");
|
||||
},
|
||||
},
|
||||
{
|
||||
prop: "updateTime",
|
||||
label: "更新时间",
|
||||
align: "center",
|
||||
showOverflowTooltip: true,
|
||||
formatter: (val) => {
|
||||
return val.updateTime
|
||||
? dayjs(val.updateTime).format("YYYY-MM-DD HH:mm")
|
||||
: "-";
|
||||
},
|
||||
valueType: "date",
|
||||
format: "YYYY-MM-DD HH:mm",
|
||||
},
|
||||
{
|
||||
prop: "updater",
|
||||
@@ -199,8 +189,7 @@ const roleColumns = [
|
||||
prop: "actions",
|
||||
label: "操作",
|
||||
align: "right",
|
||||
fixed: "right",
|
||||
width: "200",
|
||||
width: 200,
|
||||
actions: [
|
||||
{
|
||||
label: "权限",
|
||||
@@ -209,7 +198,7 @@ const roleColumns = [
|
||||
permission: ["*"],
|
||||
onClick: (row) => {
|
||||
showPermission.value = true;
|
||||
selectMember.value = {...row};
|
||||
selectMember.value = { ...row };
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -234,7 +223,7 @@ const roleColumns = [
|
||||
permission: ["*"],
|
||||
onClick: (row) => {
|
||||
showRoles.value = true;
|
||||
selectMember.value = {...row};
|
||||
selectMember.value = { ...row };
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -242,7 +231,7 @@ const roleColumns = [
|
||||
type: "danger",
|
||||
link: true,
|
||||
permission: ["*"],
|
||||
disabledFn: (row) => {
|
||||
disabled: (row) => {
|
||||
return row.type !== "SYSTEM";
|
||||
},
|
||||
onClick: async (row) => {
|
||||
@@ -251,12 +240,9 @@ const roleColumns = [
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
})
|
||||
.then(() => {
|
||||
.then(async () => {
|
||||
try {
|
||||
deleteRole(row.id).then(() => {
|
||||
ElMessage.success("删除成功");
|
||||
tableRef.value.refresh();
|
||||
});
|
||||
await handleDelete(deleteRole,row.id)
|
||||
} catch (error) {
|
||||
console.log("error", error);
|
||||
}
|
||||
@@ -280,7 +266,7 @@ const tableColumns = computed(() => {
|
||||
// 当前成员数量
|
||||
const addMember = (row) => {
|
||||
showMember.value = true;
|
||||
selectMember.value = {...row};
|
||||
selectMember.value = { ...row };
|
||||
getMemberList({ id: row.id });
|
||||
};
|
||||
|
||||
@@ -288,35 +274,30 @@ const addMember = (row) => {
|
||||
const handleSaveMember = async (row) => {
|
||||
const { id } = selectMember.value;
|
||||
const userIds = memberList.value.map((item) => item.id);
|
||||
try {
|
||||
await batchSaveRole(id,{userIds})
|
||||
ElMessage.success("保存成功");
|
||||
const currentRow = dataList.value.find(item => item.id === id);
|
||||
if (currentRow) {
|
||||
currentRow.memberCount = memberList.value.length;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('batch save error',error);
|
||||
}
|
||||
try {
|
||||
await handleAction(()=>batchSaveRole(id,{userIds}),id, getTableData);
|
||||
} catch (error) {
|
||||
console.log("batch save error", error);
|
||||
}
|
||||
};
|
||||
|
||||
// 刷新列表
|
||||
const onRefresh = () => {
|
||||
tableRef.value && tableRef.value.reset();
|
||||
tableRef.value && tableRef.value.refresh();
|
||||
};
|
||||
|
||||
// 监听Tabs变化
|
||||
watch(activeTab, () => {
|
||||
dataList.value = [];
|
||||
tableRef.value && tableRef.value.reset();
|
||||
});
|
||||
// 监听Tabs变化(目前暂时先用不到)
|
||||
// watch(activeTab, () => {
|
||||
// dataList.value = [];
|
||||
// tableRef.value && tableRef.value.refresh();
|
||||
// });
|
||||
|
||||
// 启用-停用
|
||||
const handleDictStatus = async (row) => {
|
||||
try {
|
||||
row.status === 1 ? await disableRole(row.id) : await enableRole(row.id);
|
||||
ElMessage.success("操作成功");
|
||||
onRefresh();
|
||||
// row.status === 1 ? await disableRole(row.id) : await enableRole(row.id);
|
||||
const apiCall = row.status === 1 ? disableRole(row.id) : enableRole(row.id);
|
||||
await handleAction(apiCall, row.id, getTableData);
|
||||
} catch (error) {
|
||||
console.log("error", error);
|
||||
}
|
||||
@@ -327,8 +308,7 @@ const getTableData = async (params) => {
|
||||
// 正式请求
|
||||
const queryParams = {
|
||||
...(searchVal.value && { name: searchVal.value }),
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
...params
|
||||
};
|
||||
try {
|
||||
const res = await getRoleList(queryParams);
|
||||
@@ -340,7 +320,6 @@ const getTableData = async (params) => {
|
||||
|
||||
// 后面会修改成全部获取获取成员角色列表
|
||||
const getMemberList = async (params) => {
|
||||
|
||||
try {
|
||||
const response = await getRoleMemberList(params.id);
|
||||
memberList.value = response;
|
||||
@@ -351,7 +330,7 @@ const getMemberList = async (params) => {
|
||||
|
||||
// 获取
|
||||
const checkRolesText = computed(() => {
|
||||
const btnText = {
|
||||
const btnText = {
|
||||
1: "新增角色",
|
||||
}[activeTab.value];
|
||||
|
||||
@@ -369,14 +348,9 @@ const checkRolesText = computed(() => {
|
||||
const addBtnClick = () => {
|
||||
if (activeTab.value === 1) {
|
||||
showRoles.value = true;
|
||||
selectMember.value = {id:''}
|
||||
selectMember.value = { id: "" };
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
refresh();
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.mj-permission-management {
|
||||
|
||||
@@ -103,9 +103,11 @@ const roleDetail = async (roleId) =>{
|
||||
const response = await getRolePermission(roleId);
|
||||
menuList.value = response.map(group => {
|
||||
const checkedGroup = (group.children || []).filter(c => c.selected);
|
||||
const allCheckedGroup = (group.children || []).every(c => c.selected); //兼容后端全选后返回为false的问题
|
||||
const checkedCount = checkedGroup.length;
|
||||
return {
|
||||
...group,
|
||||
selected:allCheckedGroup,
|
||||
isIndeterminate: checkedCount > 0 && checkedCount < group.children.length,
|
||||
children:(group.children||[]).map(child=>{
|
||||
const actions = child.selected ? child.operations.filter(action => action.selected).map(action => action.id) : [];
|
||||
|
||||
@@ -1,27 +1,12 @@
|
||||
<template>
|
||||
<div class="personnel">
|
||||
<el-drawer
|
||||
v-model="drawer"
|
||||
size="80%"
|
||||
title="测试评论"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
destroy-on-close
|
||||
class="standard-ui-back-drawer"
|
||||
>
|
||||
<div style="padding: 20px;height:100%">
|
||||
<Comment />
|
||||
</div>
|
||||
</el-drawer>
|
||||
人员管理
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import Comment from "@/modules/Comment/index.vue";
|
||||
defineOptions({ name: "Personnel" });
|
||||
const drawer = ref(true);
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.personnel {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -164,12 +164,14 @@ const addDynamicRoutes = async () => {
|
||||
try {
|
||||
const { modules:backendRawData } = await getUserInfo();
|
||||
const allRoutes = transformBackendTree(backendRawData);
|
||||
// console.log('获取最终渲染的菜单数据格式:',allRoutes);
|
||||
console.log('获取最终渲染的菜单数据格式:',allRoutes);
|
||||
if (allRoutes) {
|
||||
// 转换路由数据
|
||||
const dynamicRoutes = transformRoutes(
|
||||
Array.isArray(allRoutes) ? allRoutes : [allRoutes]
|
||||
);
|
||||
|
||||
console.log('最终渲染的菜单数据:',dynamicRoutes)
|
||||
// 将动态路由添加到 Layout 的 children 中
|
||||
const layoutRoute = router
|
||||
.getRoutes()
|
||||
|
||||
@@ -15,8 +15,27 @@ const businessRoute = {
|
||||
"business.opportunity":"businessOpport",
|
||||
}
|
||||
|
||||
const contractRoute = {
|
||||
contract: "contractManage",
|
||||
}
|
||||
|
||||
const teamRoute = {
|
||||
team: "teamManage",
|
||||
}
|
||||
|
||||
const financeRoute = {
|
||||
finance: "financeManage",
|
||||
}
|
||||
|
||||
const recruitmentRoute = {
|
||||
recruitment: "recruitmentManage",
|
||||
}
|
||||
|
||||
export const componentMap: Record<string, string> = {
|
||||
...stageRoute,
|
||||
...businessRoute
|
||||
...businessRoute,
|
||||
...contractRoute,
|
||||
...teamRoute,
|
||||
...financeRoute,
|
||||
...recruitmentRoute
|
||||
};
|
||||
|
||||
@@ -203,5 +203,6 @@ body {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
word-break: break-all;
|
||||
display: block;
|
||||
cursor: default;
|
||||
}
|
||||
@@ -15,13 +15,16 @@ export const usePermission = () => {
|
||||
const userPermissions = appStore.role;
|
||||
const permissionArray = Array.isArray(requiredPermissions) ? requiredPermissions : [requiredPermissions];
|
||||
|
||||
// 为*跳过权限校验
|
||||
const isSkipCheck = requiredPermissions === "*" || (permissionArray && permissionArray.includes("*"));
|
||||
if (isSkipCheck) {return true;}
|
||||
|
||||
const hasPermission = permissionArray.some((permission) =>
|
||||
userPermissions.includes(permission)
|
||||
);
|
||||
|
||||
return hasPermission;
|
||||
};
|
||||
|
||||
return { checkPermission };
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user