refactor(auth): 调整飞书OAuth回调及删除系统菜单与部门相关代码
Some checks failed
Lint Code / Lint Code (push) Failing after 1m36s
Some checks failed
Lint Code / Lint Code (push) Failing after 1m36s
- 从系统路由配置中删除了菜单和部门权限管理路由配置 - 飞书OAuth使用hash路由模式,调整redirect_uri以包含“#” - 优化飞书回调code处理,支持code在hash之前和hash内部两种情况 - 清理飞书登录页面中URL,避免重复处理code参数 - 删除系统部门管理视图及相关逻辑代码 - 删除系统菜单管理视图及相关逻辑代码 - 移除系统菜单和部门相关的表单、hooks、工具类型与验证规则等代码
This commit is contained in:
@@ -39,26 +39,6 @@ export default {
|
|||||||
title: "权限管理",
|
title: "权限管理",
|
||||||
roles: ["admin"]
|
roles: ["admin"]
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/system/menu",
|
|
||||||
name: "SystemMenu",
|
|
||||||
component: () => import("@/views/system/menu/index.vue"),
|
|
||||||
meta: {
|
|
||||||
icon: "ep:menu",
|
|
||||||
title: $t("menus.pureSystemMenu"),
|
|
||||||
roles: ["admin"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/system/dept",
|
|
||||||
name: "SystemDept",
|
|
||||||
component: () => import("@/views/system/dept/index.vue"),
|
|
||||||
meta: {
|
|
||||||
icon: "ri:git-branch-line",
|
|
||||||
title: $t("menus.pureDept"),
|
|
||||||
roles: ["admin"]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
} satisfies RouteConfigsTable;
|
} satisfies RouteConfigsTable;
|
||||||
|
|||||||
@@ -43,8 +43,9 @@ defineOptions({
|
|||||||
|
|
||||||
// 飞书OAuth配置
|
// 飞书OAuth配置
|
||||||
const FEISHU_APP_ID = "cli_a94c8a7930badcd5";
|
const FEISHU_APP_ID = "cli_a94c8a7930badcd5";
|
||||||
|
// 使用hash路由模式,redirect_uri需要包含#
|
||||||
const FEISHU_REDIRECT_URI = encodeURIComponent(
|
const FEISHU_REDIRECT_URI = encodeURIComponent(
|
||||||
window.location.origin + "/login"
|
window.location.origin + "/#/login"
|
||||||
);
|
);
|
||||||
|
|
||||||
const imgCode = ref("");
|
const imgCode = ref("");
|
||||||
@@ -207,14 +208,27 @@ const switchToFeishuLogin = () => {
|
|||||||
|
|
||||||
// 页面加载时检查是否有飞书回调code
|
// 页面加载时检查是否有飞书回调code
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 从 URL 中解析 code 参数(处理 hash 路由模式下 query 参数在 hash 之前的情况)
|
// hash路由模式下,飞书回调URL格式为: http://localhost:8848/?code=xxx#/login
|
||||||
|
// 或者在hash内部: http://localhost:8848/#/login?code=xxx
|
||||||
|
// 需要同时检查两种情况
|
||||||
|
let code: string | null = null;
|
||||||
|
|
||||||
|
// 方式1: code在hash之前 (飞书回调的标准方式)
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
const code = urlParams.get("code");
|
code = urlParams.get("code");
|
||||||
|
|
||||||
|
// 方式2: 如果没有找到,尝试从hash内部获取
|
||||||
|
if (!code) {
|
||||||
|
const hash = window.location.hash;
|
||||||
|
const hashParams = new URLSearchParams(hash.split("?")[1] || "");
|
||||||
|
code = hashParams.get("code");
|
||||||
|
}
|
||||||
|
|
||||||
if (code) {
|
if (code) {
|
||||||
handleFeishuCallback(code);
|
handleFeishuCallback(code);
|
||||||
// 清除 URL 中的 code 参数,避免重复处理
|
// 清除 URL 中的 code 参数,避免重复处理
|
||||||
const newUrl = window.location.pathname + window.location.hash;
|
const cleanUrl = window.location.origin + "/#/login";
|
||||||
window.history.replaceState({}, document.title, newUrl);
|
window.history.replaceState({}, document.title, cleanUrl);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,139 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { ref } from "vue";
|
|
||||||
import ReCol from "@/components/ReCol";
|
|
||||||
import { formRules } from "./utils/rule";
|
|
||||||
import { FormProps } from "./utils/types";
|
|
||||||
import { usePublicHooks } from "../hooks";
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<FormProps>(), {
|
|
||||||
formInline: () => ({
|
|
||||||
higherDeptOptions: [],
|
|
||||||
parentId: 0,
|
|
||||||
name: "",
|
|
||||||
principal: "",
|
|
||||||
phone: "",
|
|
||||||
email: "",
|
|
||||||
sort: 0,
|
|
||||||
status: 1,
|
|
||||||
remark: ""
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
const ruleFormRef = ref();
|
|
||||||
const { switchStyle } = usePublicHooks();
|
|
||||||
const newFormInline = ref(props.formInline);
|
|
||||||
|
|
||||||
function getRef() {
|
|
||||||
return ruleFormRef.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
defineExpose({ getRef });
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<el-form
|
|
||||||
ref="ruleFormRef"
|
|
||||||
:model="newFormInline"
|
|
||||||
:rules="formRules"
|
|
||||||
label-width="82px"
|
|
||||||
>
|
|
||||||
<el-row :gutter="30">
|
|
||||||
<re-col>
|
|
||||||
<el-form-item label="上级部门">
|
|
||||||
<el-cascader
|
|
||||||
v-model="newFormInline.parentId"
|
|
||||||
class="w-full"
|
|
||||||
:options="newFormInline.higherDeptOptions"
|
|
||||||
:props="{
|
|
||||||
value: 'id',
|
|
||||||
label: 'name',
|
|
||||||
emitPath: false,
|
|
||||||
checkStrictly: true
|
|
||||||
}"
|
|
||||||
clearable
|
|
||||||
filterable
|
|
||||||
placeholder="请选择上级部门"
|
|
||||||
>
|
|
||||||
<template #default="{ node, data }">
|
|
||||||
<span>{{ data.name }}</span>
|
|
||||||
<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
|
|
||||||
</template>
|
|
||||||
</el-cascader>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
|
|
||||||
<re-col :value="12" :xs="24" :sm="24">
|
|
||||||
<el-form-item label="部门名称" prop="name">
|
|
||||||
<el-input
|
|
||||||
v-model="newFormInline.name"
|
|
||||||
clearable
|
|
||||||
placeholder="请输入部门名称"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
<re-col :value="12" :xs="24" :sm="24">
|
|
||||||
<el-form-item label="部门负责人">
|
|
||||||
<el-input
|
|
||||||
v-model="newFormInline.principal"
|
|
||||||
clearable
|
|
||||||
placeholder="请输入部门负责人"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
|
|
||||||
<re-col :value="12" :xs="24" :sm="24">
|
|
||||||
<el-form-item label="手机号" prop="phone">
|
|
||||||
<el-input
|
|
||||||
v-model="newFormInline.phone"
|
|
||||||
clearable
|
|
||||||
placeholder="请输入手机号"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
<re-col :value="12" :xs="24" :sm="24">
|
|
||||||
<el-form-item label="邮箱" prop="email">
|
|
||||||
<el-input
|
|
||||||
v-model="newFormInline.email"
|
|
||||||
clearable
|
|
||||||
placeholder="请输入邮箱"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
|
|
||||||
<re-col :value="12" :xs="24" :sm="24">
|
|
||||||
<el-form-item label="排序">
|
|
||||||
<el-input-number
|
|
||||||
v-model="newFormInline.sort"
|
|
||||||
class="w-full!"
|
|
||||||
:min="0"
|
|
||||||
:max="9999"
|
|
||||||
controls-position="right"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
<re-col :value="12" :xs="24" :sm="24">
|
|
||||||
<el-form-item label="部门状态">
|
|
||||||
<el-switch
|
|
||||||
v-model="newFormInline.status"
|
|
||||||
inline-prompt
|
|
||||||
:active-value="1"
|
|
||||||
:inactive-value="0"
|
|
||||||
active-text="启用"
|
|
||||||
inactive-text="停用"
|
|
||||||
:style="switchStyle"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
|
|
||||||
<re-col>
|
|
||||||
<el-form-item label="备注">
|
|
||||||
<el-input
|
|
||||||
v-model="newFormInline.remark"
|
|
||||||
placeholder="请输入备注信息"
|
|
||||||
type="textarea"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
</el-row>
|
|
||||||
</el-form>
|
|
||||||
</template>
|
|
||||||
@@ -1,172 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { ref } from "vue";
|
|
||||||
import { useDept } from "./utils/hook";
|
|
||||||
import { PureTableBar } from "@/components/RePureTableBar";
|
|
||||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
|
||||||
|
|
||||||
import Delete from "~icons/ep/delete";
|
|
||||||
import EditPen from "~icons/ep/edit-pen";
|
|
||||||
import Refresh from "~icons/ep/refresh";
|
|
||||||
import AddFill from "~icons/ri/add-circle-line";
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: "SystemDept"
|
|
||||||
});
|
|
||||||
|
|
||||||
const formRef = ref();
|
|
||||||
const tableRef = ref();
|
|
||||||
const {
|
|
||||||
form,
|
|
||||||
loading,
|
|
||||||
columns,
|
|
||||||
dataList,
|
|
||||||
onSearch,
|
|
||||||
resetForm,
|
|
||||||
openDialog,
|
|
||||||
handleDelete,
|
|
||||||
handleSelectionChange
|
|
||||||
} = useDept();
|
|
||||||
|
|
||||||
function onFullscreen() {
|
|
||||||
// 重置表格高度
|
|
||||||
tableRef.value.setAdaptive();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="main">
|
|
||||||
<el-form
|
|
||||||
ref="formRef"
|
|
||||||
:inline="true"
|
|
||||||
:model="form"
|
|
||||||
class="search-form bg-bg_color w-full pl-8 pt-3 overflow-auto"
|
|
||||||
>
|
|
||||||
<el-form-item label="部门名称:" prop="name">
|
|
||||||
<el-input
|
|
||||||
v-model="form.name"
|
|
||||||
placeholder="请输入部门名称"
|
|
||||||
clearable
|
|
||||||
class="w-45!"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="状态:" prop="status">
|
|
||||||
<el-select
|
|
||||||
v-model="form.status"
|
|
||||||
placeholder="请选择状态"
|
|
||||||
clearable
|
|
||||||
class="w-45!"
|
|
||||||
>
|
|
||||||
<el-option label="启用" :value="1" />
|
|
||||||
<el-option label="停用" :value="0" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item>
|
|
||||||
<el-button
|
|
||||||
type="primary"
|
|
||||||
:icon="useRenderIcon('ri/search-line')"
|
|
||||||
:loading="loading"
|
|
||||||
@click="onSearch"
|
|
||||||
>
|
|
||||||
搜索
|
|
||||||
</el-button>
|
|
||||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)">
|
|
||||||
重置
|
|
||||||
</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
|
|
||||||
<PureTableBar
|
|
||||||
title="部门管理(仅演示,操作后不生效)"
|
|
||||||
:columns="columns"
|
|
||||||
:tableRef="tableRef?.getTableRef()"
|
|
||||||
@refresh="onSearch"
|
|
||||||
@fullscreen="onFullscreen"
|
|
||||||
>
|
|
||||||
<template #buttons>
|
|
||||||
<el-button
|
|
||||||
type="primary"
|
|
||||||
:icon="useRenderIcon(AddFill)"
|
|
||||||
@click="openDialog()"
|
|
||||||
>
|
|
||||||
新增部门
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
<template v-slot="{ size, dynamicColumns }">
|
|
||||||
<pure-table
|
|
||||||
ref="tableRef"
|
|
||||||
adaptive
|
|
||||||
:adaptiveConfig="{ offsetBottom: 45 }"
|
|
||||||
align-whole="center"
|
|
||||||
row-key="id"
|
|
||||||
showOverflowTooltip
|
|
||||||
table-layout="auto"
|
|
||||||
default-expand-all
|
|
||||||
:loading="loading"
|
|
||||||
:size="size"
|
|
||||||
:data="dataList"
|
|
||||||
:columns="dynamicColumns"
|
|
||||||
:header-cell-style="{
|
|
||||||
background: 'var(--el-fill-color-light)',
|
|
||||||
color: 'var(--el-text-color-primary)'
|
|
||||||
}"
|
|
||||||
@selection-change="handleSelectionChange"
|
|
||||||
>
|
|
||||||
<template #operation="{ row }">
|
|
||||||
<el-button
|
|
||||||
class="reset-margin"
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
:size="size"
|
|
||||||
:icon="useRenderIcon(EditPen)"
|
|
||||||
@click="openDialog('修改', row)"
|
|
||||||
>
|
|
||||||
修改
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
class="reset-margin"
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
:size="size"
|
|
||||||
:icon="useRenderIcon(AddFill)"
|
|
||||||
@click="openDialog('新增', { parentId: row.id } as any)"
|
|
||||||
>
|
|
||||||
新增
|
|
||||||
</el-button>
|
|
||||||
<el-popconfirm
|
|
||||||
:title="`是否确认删除部门名称为${row.name}的这条数据`"
|
|
||||||
@confirm="handleDelete(row)"
|
|
||||||
>
|
|
||||||
<template #reference>
|
|
||||||
<el-button
|
|
||||||
class="reset-margin"
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
:size="size"
|
|
||||||
:icon="useRenderIcon(Delete)"
|
|
||||||
>
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-popconfirm>
|
|
||||||
</template>
|
|
||||||
</pure-table>
|
|
||||||
</template>
|
|
||||||
</PureTableBar>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
:deep(.el-table__inner-wrapper::before) {
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-content {
|
|
||||||
margin: 24px 24px 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-form {
|
|
||||||
:deep(.el-form-item) {
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,181 +0,0 @@
|
|||||||
import dayjs from "dayjs";
|
|
||||||
import editForm from "../form.vue";
|
|
||||||
import { handleTree } from "@/utils/tree";
|
|
||||||
import { message } from "@/utils/message";
|
|
||||||
import { getDeptList } from "@/api/system";
|
|
||||||
import { usePublicHooks } from "../../hooks";
|
|
||||||
import { addDialog } from "@/components/ReDialog";
|
|
||||||
import { reactive, ref, onMounted, h } from "vue";
|
|
||||||
import type { FormItemProps } from "../utils/types";
|
|
||||||
import { cloneDeep, isAllEmpty, deviceDetection } from "@pureadmin/utils";
|
|
||||||
|
|
||||||
export function useDept() {
|
|
||||||
const form = reactive({
|
|
||||||
name: "",
|
|
||||||
status: null
|
|
||||||
});
|
|
||||||
|
|
||||||
const formRef = ref();
|
|
||||||
const dataList = ref([]);
|
|
||||||
const loading = ref(true);
|
|
||||||
const { tagStyle } = usePublicHooks();
|
|
||||||
|
|
||||||
const columns: TableColumnList = [
|
|
||||||
{
|
|
||||||
label: "部门名称",
|
|
||||||
prop: "name",
|
|
||||||
width: 180,
|
|
||||||
align: "left"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "排序",
|
|
||||||
prop: "sort",
|
|
||||||
minWidth: 70
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "状态",
|
|
||||||
prop: "status",
|
|
||||||
minWidth: 100,
|
|
||||||
cellRenderer: ({ row, props }) => (
|
|
||||||
<el-tag size={props.size} style={tagStyle.value(row.status)}>
|
|
||||||
{row.status === 1 ? "启用" : "停用"}
|
|
||||||
</el-tag>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "创建时间",
|
|
||||||
minWidth: 200,
|
|
||||||
prop: "createTime",
|
|
||||||
formatter: ({ createTime }) =>
|
|
||||||
dayjs(createTime).format("YYYY-MM-DD HH:mm:ss")
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "备注",
|
|
||||||
prop: "remark",
|
|
||||||
minWidth: 320
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "操作",
|
|
||||||
fixed: "right",
|
|
||||||
width: 210,
|
|
||||||
slot: "operation"
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
function handleSelectionChange(val) {
|
|
||||||
console.log("handleSelectionChange", val);
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetForm(formEl) {
|
|
||||||
if (!formEl) return;
|
|
||||||
formEl.resetFields();
|
|
||||||
onSearch();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function onSearch() {
|
|
||||||
loading.value = true;
|
|
||||||
const { code, data } = await getDeptList(); // 这里是返回一维数组结构,前端自行处理成树结构,返回格式要求:唯一id加父节点parentId,parentId取父节点id
|
|
||||||
if (code === 200) {
|
|
||||||
let newData = data;
|
|
||||||
if (!isAllEmpty(form.name)) {
|
|
||||||
// 前端搜索部门名称
|
|
||||||
newData = newData.filter(item => item.name.includes(form.name));
|
|
||||||
}
|
|
||||||
if (!isAllEmpty(form.status)) {
|
|
||||||
// 前端搜索状态
|
|
||||||
newData = newData.filter(item => item.status === form.status);
|
|
||||||
}
|
|
||||||
dataList.value = handleTree(newData); // 处理成树结构
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
loading.value = false;
|
|
||||||
}, 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatHigherDeptOptions(treeList) {
|
|
||||||
// 根据返回数据的status字段值判断追加是否禁用disabled字段,返回处理后的树结构,用于上级部门级联选择器的展示(实际开发中也是如此,不可能前端需要的每个字段后端都会返回,这时需要前端自行根据后端返回的某些字段做逻辑处理)
|
|
||||||
if (!treeList || !treeList.length) return;
|
|
||||||
const newTreeList = [];
|
|
||||||
for (let i = 0; i < treeList.length; i++) {
|
|
||||||
treeList[i].disabled = treeList[i].status === 0 ? true : false;
|
|
||||||
formatHigherDeptOptions(treeList[i].children);
|
|
||||||
newTreeList.push(treeList[i]);
|
|
||||||
}
|
|
||||||
return newTreeList;
|
|
||||||
}
|
|
||||||
|
|
||||||
function openDialog(title = "新增", row?: FormItemProps) {
|
|
||||||
addDialog({
|
|
||||||
title: `${title}部门`,
|
|
||||||
props: {
|
|
||||||
formInline: {
|
|
||||||
higherDeptOptions: formatHigherDeptOptions(cloneDeep(dataList.value)),
|
|
||||||
parentId: row?.parentId ?? 0,
|
|
||||||
name: row?.name ?? "",
|
|
||||||
principal: row?.principal ?? "",
|
|
||||||
phone: row?.phone ?? "",
|
|
||||||
email: row?.email ?? "",
|
|
||||||
sort: row?.sort ?? 0,
|
|
||||||
status: row?.status ?? 1,
|
|
||||||
remark: row?.remark ?? ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
width: "40%",
|
|
||||||
draggable: true,
|
|
||||||
fullscreen: deviceDetection(),
|
|
||||||
fullscreenIcon: true,
|
|
||||||
closeOnClickModal: false,
|
|
||||||
contentRenderer: () => h(editForm, { ref: formRef, formInline: null }),
|
|
||||||
beforeSure: (done, { options }) => {
|
|
||||||
const FormRef = formRef.value.getRef();
|
|
||||||
const curData = options.props.formInline as FormItemProps;
|
|
||||||
function chores() {
|
|
||||||
message(`您${title}了部门名称为${curData.name}的这条数据`, {
|
|
||||||
type: "success"
|
|
||||||
});
|
|
||||||
done(); // 关闭弹框
|
|
||||||
onSearch(); // 刷新表格数据
|
|
||||||
}
|
|
||||||
FormRef.validate(valid => {
|
|
||||||
if (valid) {
|
|
||||||
console.log("curData", curData);
|
|
||||||
// 表单规则校验通过
|
|
||||||
if (title === "新增") {
|
|
||||||
// 实际开发先调用新增接口,再进行下面操作
|
|
||||||
chores();
|
|
||||||
} else {
|
|
||||||
// 实际开发先调用修改接口,再进行下面操作
|
|
||||||
chores();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDelete(row) {
|
|
||||||
message(`您删除了部门名称为${row.name}的这条数据`, { type: "success" });
|
|
||||||
onSearch();
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
onSearch();
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
form,
|
|
||||||
loading,
|
|
||||||
columns,
|
|
||||||
dataList,
|
|
||||||
/** 搜索 */
|
|
||||||
onSearch,
|
|
||||||
/** 重置 */
|
|
||||||
resetForm,
|
|
||||||
/** 新增、修改部门 */
|
|
||||||
openDialog,
|
|
||||||
/** 删除部门 */
|
|
||||||
handleDelete,
|
|
||||||
handleSelectionChange
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import { reactive } from "vue";
|
|
||||||
import type { FormRules } from "element-plus";
|
|
||||||
import { isPhone, isEmail } from "@pureadmin/utils";
|
|
||||||
|
|
||||||
/** 自定义表单规则校验 */
|
|
||||||
export const formRules = reactive(<FormRules>{
|
|
||||||
name: [{ required: true, message: "部门名称为必填项", trigger: "blur" }],
|
|
||||||
phone: [
|
|
||||||
{
|
|
||||||
validator: (rule, value, callback) => {
|
|
||||||
if (value === "") {
|
|
||||||
callback();
|
|
||||||
} else if (!isPhone(value)) {
|
|
||||||
callback(new Error("请输入正确的手机号码格式"));
|
|
||||||
} else {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
trigger: "blur"
|
|
||||||
// trigger: "click" // 如果想在点击确定按钮时触发这个校验,trigger 设置成 click 即可
|
|
||||||
}
|
|
||||||
],
|
|
||||||
email: [
|
|
||||||
{
|
|
||||||
validator: (rule, value, callback) => {
|
|
||||||
if (value === "") {
|
|
||||||
callback();
|
|
||||||
} else if (!isEmail(value)) {
|
|
||||||
callback(new Error("请输入正确的邮箱格式"));
|
|
||||||
} else {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
trigger: "blur"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
interface FormItemProps {
|
|
||||||
higherDeptOptions: Record<string, unknown>[];
|
|
||||||
parentId: number;
|
|
||||||
name: string;
|
|
||||||
principal: string;
|
|
||||||
phone: string | number;
|
|
||||||
email: string;
|
|
||||||
sort: number;
|
|
||||||
status: number;
|
|
||||||
remark: string;
|
|
||||||
}
|
|
||||||
interface FormProps {
|
|
||||||
formInline: FormItemProps;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type { FormItemProps, FormProps };
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
## 字段含义
|
|
||||||
|
|
||||||
| 字段 | 说明 |
|
|
||||||
| :---------------- | :----------------------------------------------------------- |
|
|
||||||
| `menuType` | 菜单类型(`0`代表菜单、`1`代表`iframe`、`2`代表外链、`3`代表按钮) |
|
|
||||||
| `parentId` | |
|
|
||||||
| `title` | 菜单名称(兼容国际化、非国际化,如果用国际化的写法就必须在根目录的`locales`文件夹下对应添加) |
|
|
||||||
| `name` | 路由名称(必须唯一并且和当前路由`component`字段对应的页面里用`defineOptions`包起来的`name`保持一致) |
|
|
||||||
| `path` | 路由路径 |
|
|
||||||
| `component` | 组件路径(传`component`组件路径,那么`path`可以随便写,如果不传,`component`组件路径会跟`path`保持一致) |
|
|
||||||
| `rank` | 菜单排序(平台规定只有`home`路由的`rank`才能为`0`,所以后端在返回`rank`的时候需要从非`0`开始 [点击查看更多](https://pure-admin.cn/pages/routerMenu/#%E8%8F%9C%E5%8D%95%E6%8E%92%E5%BA%8F-rank)) |
|
|
||||||
| `redirect` | 路由重定向 |
|
|
||||||
| `icon` | 菜单图标 |
|
|
||||||
| `extraIcon` | 右侧图标 |
|
|
||||||
| `enterTransition` | 进场动画(页面加载动画) |
|
|
||||||
| `leaveTransition` | 离场动画(页面加载动画) |
|
|
||||||
| `activePath` | 菜单激活(将某个菜单激活,主要用于通过`query`或`params`传参的路由,当它们通过配置`showLink: false`后不在菜单中显示,就不会有任何菜单高亮,而通过设置`activePath`指定激活菜单即可获得高亮,`activePath`为指定激活菜单的`path`) |
|
|
||||||
| `auths` | 权限标识(按钮级别权限设置) |
|
|
||||||
| `frameSrc` | 链接地址(需要内嵌的`iframe`链接地址) |
|
|
||||||
| `frameLoading` | 加载动画(内嵌的`iframe`页面是否开启首次加载动画) |
|
|
||||||
| `keepAlive` | 缓存页面(是否缓存该路由页面,开启后会保存该页面的整体状态,刷新后会清空状态) |
|
|
||||||
| `hiddenTag` | 标签页(当前菜单名称或自定义信息禁止添加到标签页) |
|
|
||||||
| `fixedTag` | 固定标签页(当前菜单名称是否固定显示在标签页且不可关闭) |
|
|
||||||
| `showLink` | 菜单(是否显示该菜单) |
|
|
||||||
| `showParent` | 父级菜单(是否显示父级菜单 [点击查看更多](https://pure-admin.cn/pages/routerMenu/#%E7%AC%AC%E4%B8%80%E7%A7%8D-%E8%AF%A5%E6%A8%A1%E5%BC%8F%E9%92%88%E5%AF%B9%E7%88%B6%E7%BA%A7%E8%8F%9C%E5%8D%95%E4%B8%8B%E5%8F%AA%E6%9C%89%E4%B8%80%E4%B8%AA%E5%AD%90%E8%8F%9C%E5%8D%95%E7%9A%84%E6%83%85%E5%86%B5-%E5%9C%A8%E5%AD%90%E8%8F%9C%E5%8D%95%E7%9A%84-meta-%E5%B1%9E%E6%80%A7%E4%B8%AD%E5%8A%A0%E4%B8%8A-showparent-true-%E5%8D%B3%E5%8F%AF)) |
|
|
||||||
|
|
||||||
@@ -1,342 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { ref } from "vue";
|
|
||||||
import ReCol from "@/components/ReCol";
|
|
||||||
import { formRules } from "./utils/rule";
|
|
||||||
import { FormProps } from "./utils/types";
|
|
||||||
import { transformI18n } from "@/plugins/i18n";
|
|
||||||
import { IconSelect } from "@/components/ReIcon";
|
|
||||||
import Segmented from "@/components/ReSegmented";
|
|
||||||
import ReAnimateSelector from "@/components/ReAnimateSelector";
|
|
||||||
import {
|
|
||||||
menuTypeOptions,
|
|
||||||
showLinkOptions,
|
|
||||||
fixedTagOptions,
|
|
||||||
keepAliveOptions,
|
|
||||||
hiddenTagOptions,
|
|
||||||
showParentOptions,
|
|
||||||
frameLoadingOptions
|
|
||||||
} from "./utils/enums";
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<FormProps>(), {
|
|
||||||
formInline: () => ({
|
|
||||||
menuType: 0,
|
|
||||||
higherMenuOptions: [],
|
|
||||||
parentId: 0,
|
|
||||||
title: "",
|
|
||||||
name: "",
|
|
||||||
path: "",
|
|
||||||
component: "",
|
|
||||||
rank: 99,
|
|
||||||
redirect: "",
|
|
||||||
icon: "",
|
|
||||||
extraIcon: "",
|
|
||||||
enterTransition: "",
|
|
||||||
leaveTransition: "",
|
|
||||||
activePath: "",
|
|
||||||
auths: "",
|
|
||||||
frameSrc: "",
|
|
||||||
frameLoading: true,
|
|
||||||
keepAlive: false,
|
|
||||||
hiddenTag: false,
|
|
||||||
fixedTag: false,
|
|
||||||
showLink: true,
|
|
||||||
showParent: false
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
const ruleFormRef = ref();
|
|
||||||
const newFormInline = ref(props.formInline);
|
|
||||||
|
|
||||||
function getRef() {
|
|
||||||
return ruleFormRef.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
defineExpose({ getRef });
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<el-form
|
|
||||||
ref="ruleFormRef"
|
|
||||||
:model="newFormInline"
|
|
||||||
:rules="formRules"
|
|
||||||
label-width="82px"
|
|
||||||
>
|
|
||||||
<el-row :gutter="30">
|
|
||||||
<re-col>
|
|
||||||
<el-form-item label="菜单类型">
|
|
||||||
<Segmented
|
|
||||||
v-model="newFormInline.menuType"
|
|
||||||
:options="menuTypeOptions"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
|
|
||||||
<re-col>
|
|
||||||
<el-form-item label="上级菜单">
|
|
||||||
<el-cascader
|
|
||||||
v-model="newFormInline.parentId"
|
|
||||||
class="w-full"
|
|
||||||
:options="newFormInline.higherMenuOptions"
|
|
||||||
:props="{
|
|
||||||
value: 'id',
|
|
||||||
label: 'title',
|
|
||||||
emitPath: false,
|
|
||||||
checkStrictly: true
|
|
||||||
}"
|
|
||||||
clearable
|
|
||||||
filterable
|
|
||||||
placeholder="请选择上级菜单"
|
|
||||||
>
|
|
||||||
<template #default="{ node, data }">
|
|
||||||
<span>{{ transformI18n(data.title) }}</span>
|
|
||||||
<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
|
|
||||||
</template>
|
|
||||||
</el-cascader>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
|
|
||||||
<re-col :value="12" :xs="24" :sm="24">
|
|
||||||
<el-form-item label="菜单名称" prop="title">
|
|
||||||
<el-input
|
|
||||||
v-model="newFormInline.title"
|
|
||||||
clearable
|
|
||||||
placeholder="请输入菜单名称"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
<re-col v-if="newFormInline.menuType !== 3" :value="12" :xs="24" :sm="24">
|
|
||||||
<el-form-item label="路由名称" prop="name">
|
|
||||||
<el-input
|
|
||||||
v-model="newFormInline.name"
|
|
||||||
clearable
|
|
||||||
placeholder="请输入路由名称"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
|
|
||||||
<re-col v-if="newFormInline.menuType !== 3" :value="12" :xs="24" :sm="24">
|
|
||||||
<el-form-item label="路由路径" prop="path">
|
|
||||||
<el-input
|
|
||||||
v-model="newFormInline.path"
|
|
||||||
clearable
|
|
||||||
placeholder="请输入路由路径"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
<re-col
|
|
||||||
v-show="newFormInline.menuType === 0"
|
|
||||||
:value="12"
|
|
||||||
:xs="24"
|
|
||||||
:sm="24"
|
|
||||||
>
|
|
||||||
<el-form-item label="组件路径">
|
|
||||||
<el-input
|
|
||||||
v-model="newFormInline.component"
|
|
||||||
clearable
|
|
||||||
placeholder="请输入组件路径"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
|
|
||||||
<re-col :value="12" :xs="24" :sm="24">
|
|
||||||
<el-form-item label="菜单排序">
|
|
||||||
<el-input-number
|
|
||||||
v-model="newFormInline.rank"
|
|
||||||
class="w-full!"
|
|
||||||
:min="1"
|
|
||||||
:max="9999"
|
|
||||||
controls-position="right"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
<re-col
|
|
||||||
v-show="newFormInline.menuType === 0"
|
|
||||||
:value="12"
|
|
||||||
:xs="24"
|
|
||||||
:sm="24"
|
|
||||||
>
|
|
||||||
<el-form-item label="路由重定向">
|
|
||||||
<el-input
|
|
||||||
v-model="newFormInline.redirect"
|
|
||||||
clearable
|
|
||||||
placeholder="请输入默认跳转地址"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
|
|
||||||
<re-col
|
|
||||||
v-show="newFormInline.menuType !== 3"
|
|
||||||
:value="12"
|
|
||||||
:xs="24"
|
|
||||||
:sm="24"
|
|
||||||
>
|
|
||||||
<el-form-item label="菜单图标">
|
|
||||||
<IconSelect v-model="newFormInline.icon" class="w-full" />
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
<re-col
|
|
||||||
v-show="newFormInline.menuType !== 3"
|
|
||||||
:value="12"
|
|
||||||
:xs="24"
|
|
||||||
:sm="24"
|
|
||||||
>
|
|
||||||
<el-form-item label="右侧图标">
|
|
||||||
<el-input
|
|
||||||
v-model="newFormInline.extraIcon"
|
|
||||||
clearable
|
|
||||||
placeholder="菜单名称右侧的额外图标"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
|
|
||||||
<re-col v-show="newFormInline.menuType < 2" :value="12" :xs="24" :sm="24">
|
|
||||||
<el-form-item label="进场动画">
|
|
||||||
<ReAnimateSelector
|
|
||||||
v-model="newFormInline.enterTransition"
|
|
||||||
placeholder="请选择页面进场加载动画"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
<re-col v-show="newFormInline.menuType < 2" :value="12" :xs="24" :sm="24">
|
|
||||||
<el-form-item label="离场动画">
|
|
||||||
<ReAnimateSelector
|
|
||||||
v-model="newFormInline.leaveTransition"
|
|
||||||
placeholder="请选择页面离场加载动画"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
|
|
||||||
<re-col
|
|
||||||
v-show="newFormInline.menuType === 0"
|
|
||||||
:value="12"
|
|
||||||
:xs="24"
|
|
||||||
:sm="24"
|
|
||||||
>
|
|
||||||
<el-form-item label="菜单激活">
|
|
||||||
<el-input
|
|
||||||
v-model="newFormInline.activePath"
|
|
||||||
clearable
|
|
||||||
placeholder="请输入需要激活的菜单"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
<re-col v-if="newFormInline.menuType === 3" :value="12" :xs="24" :sm="24">
|
|
||||||
<!-- 按钮级别权限设置 -->
|
|
||||||
<el-form-item label="权限标识" prop="auths">
|
|
||||||
<el-input
|
|
||||||
v-model="newFormInline.auths"
|
|
||||||
clearable
|
|
||||||
placeholder="请输入权限标识"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
|
|
||||||
<re-col
|
|
||||||
v-show="newFormInline.menuType === 1"
|
|
||||||
:value="12"
|
|
||||||
:xs="24"
|
|
||||||
:sm="24"
|
|
||||||
>
|
|
||||||
<!-- iframe -->
|
|
||||||
<el-form-item label="链接地址">
|
|
||||||
<el-input
|
|
||||||
v-model="newFormInline.frameSrc"
|
|
||||||
clearable
|
|
||||||
placeholder="请输入 iframe 链接地址"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
<re-col v-if="newFormInline.menuType === 1" :value="12" :xs="24" :sm="24">
|
|
||||||
<el-form-item label="加载动画">
|
|
||||||
<Segmented
|
|
||||||
:modelValue="newFormInline.frameLoading ? 0 : 1"
|
|
||||||
:options="frameLoadingOptions"
|
|
||||||
@change="
|
|
||||||
({ option: { value } }) => {
|
|
||||||
newFormInline.frameLoading = value;
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
|
|
||||||
<re-col
|
|
||||||
v-show="newFormInline.menuType !== 3"
|
|
||||||
:value="12"
|
|
||||||
:xs="24"
|
|
||||||
:sm="24"
|
|
||||||
>
|
|
||||||
<el-form-item label="菜单">
|
|
||||||
<Segmented
|
|
||||||
:modelValue="newFormInline.showLink ? 0 : 1"
|
|
||||||
:options="showLinkOptions"
|
|
||||||
@change="
|
|
||||||
({ option: { value } }) => {
|
|
||||||
newFormInline.showLink = value;
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
<re-col
|
|
||||||
v-show="newFormInline.menuType !== 3"
|
|
||||||
:value="12"
|
|
||||||
:xs="24"
|
|
||||||
:sm="24"
|
|
||||||
>
|
|
||||||
<el-form-item label="父级菜单">
|
|
||||||
<Segmented
|
|
||||||
:modelValue="newFormInline.showParent ? 0 : 1"
|
|
||||||
:options="showParentOptions"
|
|
||||||
@change="
|
|
||||||
({ option: { value } }) => {
|
|
||||||
newFormInline.showParent = value;
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
|
|
||||||
<re-col v-show="newFormInline.menuType < 2" :value="12" :xs="24" :sm="24">
|
|
||||||
<el-form-item label="缓存页面">
|
|
||||||
<Segmented
|
|
||||||
:modelValue="newFormInline.keepAlive ? 0 : 1"
|
|
||||||
:options="keepAliveOptions"
|
|
||||||
@change="
|
|
||||||
({ option: { value } }) => {
|
|
||||||
newFormInline.keepAlive = value;
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
|
|
||||||
<re-col v-show="newFormInline.menuType < 2" :value="12" :xs="24" :sm="24">
|
|
||||||
<el-form-item label="标签页">
|
|
||||||
<Segmented
|
|
||||||
:modelValue="newFormInline.hiddenTag ? 1 : 0"
|
|
||||||
:options="hiddenTagOptions"
|
|
||||||
@change="
|
|
||||||
({ option: { value } }) => {
|
|
||||||
newFormInline.hiddenTag = value;
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
<re-col v-show="newFormInline.menuType < 2" :value="12" :xs="24" :sm="24">
|
|
||||||
<el-form-item label="固定标签页">
|
|
||||||
<Segmented
|
|
||||||
:modelValue="newFormInline.fixedTag ? 0 : 1"
|
|
||||||
:options="fixedTagOptions"
|
|
||||||
@change="
|
|
||||||
({ option: { value } }) => {
|
|
||||||
newFormInline.fixedTag = value;
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
</el-row>
|
|
||||||
</el-form>
|
|
||||||
</template>
|
|
||||||
@@ -1,163 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { ref } from "vue";
|
|
||||||
import { useMenu } from "./utils/hook";
|
|
||||||
import { transformI18n } from "@/plugins/i18n";
|
|
||||||
import { PureTableBar } from "@/components/RePureTableBar";
|
|
||||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
|
||||||
|
|
||||||
import Delete from "~icons/ep/delete";
|
|
||||||
import EditPen from "~icons/ep/edit-pen";
|
|
||||||
import Refresh from "~icons/ep/refresh";
|
|
||||||
import AddFill from "~icons/ri/add-circle-line";
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: "SystemMenu"
|
|
||||||
});
|
|
||||||
|
|
||||||
const formRef = ref();
|
|
||||||
const tableRef = ref();
|
|
||||||
const {
|
|
||||||
form,
|
|
||||||
loading,
|
|
||||||
columns,
|
|
||||||
dataList,
|
|
||||||
onSearch,
|
|
||||||
resetForm,
|
|
||||||
openDialog,
|
|
||||||
handleDelete,
|
|
||||||
handleSelectionChange
|
|
||||||
} = useMenu();
|
|
||||||
|
|
||||||
function onFullscreen() {
|
|
||||||
// 重置表格高度
|
|
||||||
tableRef.value.setAdaptive();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="main">
|
|
||||||
<el-form
|
|
||||||
ref="formRef"
|
|
||||||
:inline="true"
|
|
||||||
:model="form"
|
|
||||||
class="search-form bg-bg_color w-full pl-8 pt-3 overflow-auto"
|
|
||||||
>
|
|
||||||
<el-form-item label="菜单名称:" prop="title">
|
|
||||||
<el-input
|
|
||||||
v-model="form.title"
|
|
||||||
placeholder="请输入菜单名称"
|
|
||||||
clearable
|
|
||||||
class="w-45!"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item>
|
|
||||||
<el-button
|
|
||||||
type="primary"
|
|
||||||
:icon="useRenderIcon('ri/search-line')"
|
|
||||||
:loading="loading"
|
|
||||||
@click="onSearch"
|
|
||||||
>
|
|
||||||
搜索
|
|
||||||
</el-button>
|
|
||||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)">
|
|
||||||
重置
|
|
||||||
</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
|
|
||||||
<PureTableBar
|
|
||||||
title="菜单管理(仅演示,操作后不生效)"
|
|
||||||
:columns="columns"
|
|
||||||
:isExpandAll="false"
|
|
||||||
:tableRef="tableRef?.getTableRef()"
|
|
||||||
@refresh="onSearch"
|
|
||||||
@fullscreen="onFullscreen"
|
|
||||||
>
|
|
||||||
<template #buttons>
|
|
||||||
<el-button
|
|
||||||
type="primary"
|
|
||||||
:icon="useRenderIcon(AddFill)"
|
|
||||||
@click="openDialog()"
|
|
||||||
>
|
|
||||||
新增菜单
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
<template v-slot="{ size, dynamicColumns }">
|
|
||||||
<pure-table
|
|
||||||
ref="tableRef"
|
|
||||||
adaptive
|
|
||||||
:adaptiveConfig="{ offsetBottom: 45 }"
|
|
||||||
align-whole="center"
|
|
||||||
row-key="id"
|
|
||||||
showOverflowTooltip
|
|
||||||
table-layout="auto"
|
|
||||||
:loading="loading"
|
|
||||||
:size="size"
|
|
||||||
:data="dataList"
|
|
||||||
:columns="dynamicColumns"
|
|
||||||
:header-cell-style="{
|
|
||||||
background: 'var(--el-fill-color-light)',
|
|
||||||
color: 'var(--el-text-color-primary)'
|
|
||||||
}"
|
|
||||||
@selection-change="handleSelectionChange"
|
|
||||||
>
|
|
||||||
<template #operation="{ row }">
|
|
||||||
<el-button
|
|
||||||
class="reset-margin"
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
:size="size"
|
|
||||||
:icon="useRenderIcon(EditPen)"
|
|
||||||
@click="openDialog('修改', row)"
|
|
||||||
>
|
|
||||||
修改
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-show="row.menuType !== 3"
|
|
||||||
class="reset-margin"
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
:size="size"
|
|
||||||
:icon="useRenderIcon(AddFill)"
|
|
||||||
@click="openDialog('新增', { parentId: row.id } as any)"
|
|
||||||
>
|
|
||||||
新增
|
|
||||||
</el-button>
|
|
||||||
<el-popconfirm
|
|
||||||
:title="`是否确认删除菜单名称为${transformI18n(row.title)}的这条数据${row?.children?.length > 0 ? '。注意下级菜单也会一并删除,请谨慎操作' : ''}`"
|
|
||||||
@confirm="handleDelete(row)"
|
|
||||||
>
|
|
||||||
<template #reference>
|
|
||||||
<el-button
|
|
||||||
class="reset-margin"
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
:size="size"
|
|
||||||
:icon="useRenderIcon(Delete)"
|
|
||||||
>
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-popconfirm>
|
|
||||||
</template>
|
|
||||||
</pure-table>
|
|
||||||
</template>
|
|
||||||
</PureTableBar>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
:deep(.el-table__inner-wrapper::before) {
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-content {
|
|
||||||
margin: 24px 24px 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-form {
|
|
||||||
:deep(.el-form-item) {
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
import type { OptionsType } from "@/components/ReSegmented";
|
|
||||||
|
|
||||||
const menuTypeOptions: Array<OptionsType> = [
|
|
||||||
{
|
|
||||||
label: "菜单",
|
|
||||||
value: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "iframe",
|
|
||||||
value: 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "外链",
|
|
||||||
value: 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "按钮",
|
|
||||||
value: 3
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const showLinkOptions: Array<OptionsType> = [
|
|
||||||
{
|
|
||||||
label: "显示",
|
|
||||||
tip: "会在菜单中显示",
|
|
||||||
value: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "隐藏",
|
|
||||||
tip: "不会在菜单中显示",
|
|
||||||
value: false
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const fixedTagOptions: Array<OptionsType> = [
|
|
||||||
{
|
|
||||||
label: "固定",
|
|
||||||
tip: "当前菜单名称固定显示在标签页且不可关闭",
|
|
||||||
value: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "不固定",
|
|
||||||
tip: "当前菜单名称不固定显示在标签页且可关闭",
|
|
||||||
value: false
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const keepAliveOptions: Array<OptionsType> = [
|
|
||||||
{
|
|
||||||
label: "缓存",
|
|
||||||
tip: "会保存该页面的整体状态,刷新后会清空状态",
|
|
||||||
value: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "不缓存",
|
|
||||||
tip: "不会保存该页面的整体状态",
|
|
||||||
value: false
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const hiddenTagOptions: Array<OptionsType> = [
|
|
||||||
{
|
|
||||||
label: "允许",
|
|
||||||
tip: "当前菜单名称或自定义信息允许添加到标签页",
|
|
||||||
value: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "禁止",
|
|
||||||
tip: "当前菜单名称或自定义信息禁止添加到标签页",
|
|
||||||
value: true
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const showParentOptions: Array<OptionsType> = [
|
|
||||||
{
|
|
||||||
label: "显示",
|
|
||||||
tip: "会显示父级菜单",
|
|
||||||
value: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "隐藏",
|
|
||||||
tip: "不会显示父级菜单",
|
|
||||||
value: false
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const frameLoadingOptions: Array<OptionsType> = [
|
|
||||||
{
|
|
||||||
label: "开启",
|
|
||||||
tip: "有首次加载动画",
|
|
||||||
value: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "关闭",
|
|
||||||
tip: "无首次加载动画",
|
|
||||||
value: false
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
export {
|
|
||||||
menuTypeOptions,
|
|
||||||
showLinkOptions,
|
|
||||||
fixedTagOptions,
|
|
||||||
keepAliveOptions,
|
|
||||||
hiddenTagOptions,
|
|
||||||
showParentOptions,
|
|
||||||
frameLoadingOptions
|
|
||||||
};
|
|
||||||
@@ -1,228 +0,0 @@
|
|||||||
import editForm from "../form.vue";
|
|
||||||
import { handleTree } from "@/utils/tree";
|
|
||||||
import { message } from "@/utils/message";
|
|
||||||
import { getMenuList } from "@/api/system";
|
|
||||||
import { transformI18n } from "@/plugins/i18n";
|
|
||||||
import { addDialog } from "@/components/ReDialog";
|
|
||||||
import { reactive, ref, onMounted, h } from "vue";
|
|
||||||
import type { FormItemProps } from "../utils/types";
|
|
||||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
|
||||||
import { cloneDeep, isAllEmpty, deviceDetection } from "@pureadmin/utils";
|
|
||||||
|
|
||||||
export function useMenu() {
|
|
||||||
const form = reactive({
|
|
||||||
title: ""
|
|
||||||
});
|
|
||||||
|
|
||||||
const formRef = ref();
|
|
||||||
const dataList = ref([]);
|
|
||||||
const loading = ref(true);
|
|
||||||
|
|
||||||
const getMenuType = (type, text = false) => {
|
|
||||||
switch (type) {
|
|
||||||
case 0:
|
|
||||||
return text ? "菜单" : "primary";
|
|
||||||
case 1:
|
|
||||||
return text ? "iframe" : "warning";
|
|
||||||
case 2:
|
|
||||||
return text ? "外链" : "danger";
|
|
||||||
case 3:
|
|
||||||
return text ? "按钮" : "info";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const columns: TableColumnList = [
|
|
||||||
{
|
|
||||||
label: "菜单名称",
|
|
||||||
prop: "title",
|
|
||||||
align: "left",
|
|
||||||
cellRenderer: ({ row }) => (
|
|
||||||
<>
|
|
||||||
<span class="inline-block mr-1">
|
|
||||||
{h(useRenderIcon(row.icon), {
|
|
||||||
style: { paddingTop: "1px" }
|
|
||||||
})}
|
|
||||||
</span>
|
|
||||||
<span>{transformI18n(row.title)}</span>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "菜单类型",
|
|
||||||
prop: "menuType",
|
|
||||||
width: 100,
|
|
||||||
cellRenderer: ({ row, props }) => (
|
|
||||||
<el-tag
|
|
||||||
size={props.size}
|
|
||||||
type={getMenuType(row.menuType)}
|
|
||||||
effect="plain"
|
|
||||||
>
|
|
||||||
{getMenuType(row.menuType, true)}
|
|
||||||
</el-tag>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "路由路径",
|
|
||||||
prop: "path"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "组件路径",
|
|
||||||
prop: "component",
|
|
||||||
formatter: ({ path, component }) =>
|
|
||||||
isAllEmpty(component) ? path : component
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "权限标识",
|
|
||||||
prop: "auths"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "排序",
|
|
||||||
prop: "rank",
|
|
||||||
width: 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "隐藏",
|
|
||||||
prop: "showLink",
|
|
||||||
formatter: ({ showLink }) => (showLink ? "否" : "是"),
|
|
||||||
width: 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "操作",
|
|
||||||
fixed: "right",
|
|
||||||
width: 210,
|
|
||||||
slot: "operation"
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
function handleSelectionChange(val) {
|
|
||||||
console.log("handleSelectionChange", val);
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetForm(formEl) {
|
|
||||||
if (!formEl) return;
|
|
||||||
formEl.resetFields();
|
|
||||||
onSearch();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function onSearch() {
|
|
||||||
loading.value = true;
|
|
||||||
const { code, data } = await getMenuList(); // 这里是返回一维数组结构,前端自行处理成树结构,返回格式要求:唯一id加父节点parentId,parentId取父节点id
|
|
||||||
if (code === 200) {
|
|
||||||
let newData = data;
|
|
||||||
if (!isAllEmpty(form.title)) {
|
|
||||||
// 前端搜索菜单名称
|
|
||||||
newData = newData.filter(item =>
|
|
||||||
transformI18n(item.title).includes(form.title)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
dataList.value = handleTree(newData); // 处理成树结构
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
loading.value = false;
|
|
||||||
}, 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatHigherMenuOptions(treeList) {
|
|
||||||
if (!treeList || !treeList.length) return;
|
|
||||||
const newTreeList = [];
|
|
||||||
for (let i = 0; i < treeList.length; i++) {
|
|
||||||
treeList[i].title = transformI18n(treeList[i].title);
|
|
||||||
formatHigherMenuOptions(treeList[i].children);
|
|
||||||
newTreeList.push(treeList[i]);
|
|
||||||
}
|
|
||||||
return newTreeList;
|
|
||||||
}
|
|
||||||
|
|
||||||
function openDialog(title = "新增", row?: FormItemProps) {
|
|
||||||
addDialog({
|
|
||||||
title: `${title}菜单`,
|
|
||||||
props: {
|
|
||||||
formInline: {
|
|
||||||
menuType: row?.menuType ?? 0,
|
|
||||||
higherMenuOptions: formatHigherMenuOptions(cloneDeep(dataList.value)),
|
|
||||||
parentId: row?.parentId ?? 0,
|
|
||||||
title: row?.title ?? "",
|
|
||||||
name: row?.name ?? "",
|
|
||||||
path: row?.path ?? "",
|
|
||||||
component: row?.component ?? "",
|
|
||||||
rank: row?.rank ?? 99,
|
|
||||||
redirect: row?.redirect ?? "",
|
|
||||||
icon: row?.icon ?? "",
|
|
||||||
extraIcon: row?.extraIcon ?? "",
|
|
||||||
enterTransition: row?.enterTransition ?? "",
|
|
||||||
leaveTransition: row?.leaveTransition ?? "",
|
|
||||||
activePath: row?.activePath ?? "",
|
|
||||||
auths: row?.auths ?? "",
|
|
||||||
frameSrc: row?.frameSrc ?? "",
|
|
||||||
frameLoading: row?.frameLoading ?? true,
|
|
||||||
keepAlive: row?.keepAlive ?? false,
|
|
||||||
hiddenTag: row?.hiddenTag ?? false,
|
|
||||||
fixedTag: row?.fixedTag ?? false,
|
|
||||||
showLink: row?.showLink ?? true,
|
|
||||||
showParent: row?.showParent ?? false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
width: "45%",
|
|
||||||
draggable: true,
|
|
||||||
fullscreen: deviceDetection(),
|
|
||||||
fullscreenIcon: true,
|
|
||||||
closeOnClickModal: false,
|
|
||||||
contentRenderer: () => h(editForm, { ref: formRef, formInline: null }),
|
|
||||||
beforeSure: (done, { options }) => {
|
|
||||||
const FormRef = formRef.value.getRef();
|
|
||||||
const curData = options.props.formInline as FormItemProps;
|
|
||||||
function chores() {
|
|
||||||
message(
|
|
||||||
`您${title}了菜单名称为${transformI18n(curData.title)}的这条数据`,
|
|
||||||
{
|
|
||||||
type: "success"
|
|
||||||
}
|
|
||||||
);
|
|
||||||
done(); // 关闭弹框
|
|
||||||
onSearch(); // 刷新表格数据
|
|
||||||
}
|
|
||||||
FormRef.validate(valid => {
|
|
||||||
if (valid) {
|
|
||||||
console.log("curData", curData);
|
|
||||||
// 表单规则校验通过
|
|
||||||
if (title === "新增") {
|
|
||||||
// 实际开发先调用新增接口,再进行下面操作
|
|
||||||
chores();
|
|
||||||
} else {
|
|
||||||
// 实际开发先调用修改接口,再进行下面操作
|
|
||||||
chores();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDelete(row) {
|
|
||||||
message(`您删除了菜单名称为${transformI18n(row.title)}的这条数据`, {
|
|
||||||
type: "success"
|
|
||||||
});
|
|
||||||
onSearch();
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
onSearch();
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
form,
|
|
||||||
loading,
|
|
||||||
columns,
|
|
||||||
dataList,
|
|
||||||
/** 搜索 */
|
|
||||||
onSearch,
|
|
||||||
/** 重置 */
|
|
||||||
resetForm,
|
|
||||||
/** 新增、修改菜单 */
|
|
||||||
openDialog,
|
|
||||||
/** 删除菜单 */
|
|
||||||
handleDelete,
|
|
||||||
handleSelectionChange
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import { reactive } from "vue";
|
|
||||||
import type { FormRules } from "element-plus";
|
|
||||||
|
|
||||||
/** 自定义表单规则校验 */
|
|
||||||
export const formRules = reactive(<FormRules>{
|
|
||||||
title: [{ required: true, message: "菜单名称为必填项", trigger: "blur" }],
|
|
||||||
name: [{ required: true, message: "路由名称为必填项", trigger: "blur" }],
|
|
||||||
path: [{ required: true, message: "路由路径为必填项", trigger: "blur" }],
|
|
||||||
auths: [{ required: true, message: "权限标识为必填项", trigger: "blur" }]
|
|
||||||
});
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
interface FormItemProps {
|
|
||||||
/** 菜单类型(0代表菜单、1代表iframe、2代表外链、3代表按钮)*/
|
|
||||||
menuType: number;
|
|
||||||
higherMenuOptions: Record<string, unknown>[];
|
|
||||||
parentId: number;
|
|
||||||
title: string;
|
|
||||||
name: string;
|
|
||||||
path: string;
|
|
||||||
component: string;
|
|
||||||
rank: number;
|
|
||||||
redirect: string;
|
|
||||||
icon: string;
|
|
||||||
extraIcon: string;
|
|
||||||
enterTransition: string;
|
|
||||||
leaveTransition: string;
|
|
||||||
activePath: string;
|
|
||||||
auths: string;
|
|
||||||
frameSrc: string;
|
|
||||||
frameLoading: boolean;
|
|
||||||
keepAlive: boolean;
|
|
||||||
hiddenTag: boolean;
|
|
||||||
fixedTag: boolean;
|
|
||||||
showLink: boolean;
|
|
||||||
showParent: boolean;
|
|
||||||
}
|
|
||||||
interface FormProps {
|
|
||||||
formInline: FormItemProps;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type { FormItemProps, FormProps };
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import tree from "./tree.vue";
|
|
||||||
import { useUser } from "./utils/hook";
|
import { useUser } from "./utils/hook";
|
||||||
import { PureTableBar } from "@/components/RePureTableBar";
|
import { PureTableBar } from "@/components/RePureTableBar";
|
||||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||||
@@ -18,7 +17,6 @@ defineOptions({
|
|||||||
name: "SystemUser"
|
name: "SystemUser"
|
||||||
});
|
});
|
||||||
|
|
||||||
const treeRef = ref();
|
|
||||||
const formRef = ref();
|
const formRef = ref();
|
||||||
const tableRef = ref();
|
const tableRef = ref();
|
||||||
|
|
||||||
@@ -27,8 +25,6 @@ const {
|
|||||||
loading,
|
loading,
|
||||||
columns,
|
columns,
|
||||||
dataList,
|
dataList,
|
||||||
treeData,
|
|
||||||
treeLoading,
|
|
||||||
selectedNum,
|
selectedNum,
|
||||||
pagination,
|
pagination,
|
||||||
buttonClass,
|
buttonClass,
|
||||||
@@ -37,7 +33,6 @@ const {
|
|||||||
resetForm,
|
resetForm,
|
||||||
onbatchDel,
|
onbatchDel,
|
||||||
openDialog,
|
openDialog,
|
||||||
onTreeSelect,
|
|
||||||
handleUpdate,
|
handleUpdate,
|
||||||
handleDelete,
|
handleDelete,
|
||||||
handleUpload,
|
handleUpload,
|
||||||
@@ -47,210 +42,197 @@ const {
|
|||||||
onSelectionCancel,
|
onSelectionCancel,
|
||||||
handleCurrentChange,
|
handleCurrentChange,
|
||||||
handleSelectionChange
|
handleSelectionChange
|
||||||
} = useUser(tableRef, treeRef);
|
} = useUser(tableRef);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="['flex', 'justify-between', deviceDetection() && 'flex-wrap']">
|
<div class="w-full">
|
||||||
<tree
|
<el-form
|
||||||
ref="treeRef"
|
ref="formRef"
|
||||||
:class="['mr-2', deviceDetection() ? 'w-full' : 'min-w-50']"
|
:inline="true"
|
||||||
:treeData="treeData"
|
:model="form"
|
||||||
:treeLoading="treeLoading"
|
class="search-form bg-bg_color w-full pl-8 pt-3 overflow-auto"
|
||||||
@tree-select="onTreeSelect"
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
:class="[deviceDetection() ? ['w-full', 'mt-2'] : 'w-[calc(100%-200px)]']"
|
|
||||||
>
|
>
|
||||||
<el-form
|
<el-form-item label="用户名称:" prop="username">
|
||||||
ref="formRef"
|
<el-input
|
||||||
:inline="true"
|
v-model="form.username"
|
||||||
:model="form"
|
placeholder="请输入用户名称"
|
||||||
class="search-form bg-bg_color w-full pl-8 pt-3 overflow-auto"
|
clearable
|
||||||
>
|
class="w-45!"
|
||||||
<el-form-item label="用户名称:" prop="username">
|
/>
|
||||||
<el-input
|
</el-form-item>
|
||||||
v-model="form.username"
|
<el-form-item label="手机号码:" prop="phone">
|
||||||
placeholder="请输入用户名称"
|
<el-input
|
||||||
clearable
|
v-model="form.phone"
|
||||||
class="w-45!"
|
placeholder="请输入手机号码"
|
||||||
/>
|
clearable
|
||||||
</el-form-item>
|
class="w-45!"
|
||||||
<el-form-item label="手机号码:" prop="phone">
|
/>
|
||||||
<el-input
|
</el-form-item>
|
||||||
v-model="form.phone"
|
<el-form-item label="状态:" prop="status">
|
||||||
placeholder="请输入手机号码"
|
<el-select
|
||||||
clearable
|
v-model="form.status"
|
||||||
class="w-45!"
|
placeholder="请选择"
|
||||||
/>
|
clearable
|
||||||
</el-form-item>
|
class="w-45!"
|
||||||
<el-form-item label="状态:" prop="status">
|
>
|
||||||
<el-select
|
<el-option label="已开启" value="1" />
|
||||||
v-model="form.status"
|
<el-option label="已关闭" value="0" />
|
||||||
placeholder="请选择"
|
</el-select>
|
||||||
clearable
|
</el-form-item>
|
||||||
class="w-45!"
|
<el-form-item>
|
||||||
>
|
<el-button
|
||||||
<el-option label="已开启" value="1" />
|
type="primary"
|
||||||
<el-option label="已关闭" value="0" />
|
:icon="useRenderIcon('ri/search-line')"
|
||||||
</el-select>
|
:loading="loading"
|
||||||
</el-form-item>
|
@click="onSearch"
|
||||||
<el-form-item>
|
>
|
||||||
<el-button
|
搜索
|
||||||
type="primary"
|
</el-button>
|
||||||
:icon="useRenderIcon('ri/search-line')"
|
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)">
|
||||||
:loading="loading"
|
重置
|
||||||
@click="onSearch"
|
</el-button>
|
||||||
>
|
</el-form-item>
|
||||||
搜索
|
</el-form>
|
||||||
</el-button>
|
|
||||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)">
|
|
||||||
重置
|
|
||||||
</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
|
|
||||||
<PureTableBar
|
<PureTableBar
|
||||||
title="用户管理(仅演示,操作后不生效)"
|
title="用户管理(仅演示,操作后不生效)"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
@refresh="onSearch"
|
@refresh="onSearch"
|
||||||
>
|
>
|
||||||
<template #buttons>
|
<template #buttons>
|
||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
:icon="useRenderIcon(AddFill)"
|
:icon="useRenderIcon(AddFill)"
|
||||||
@click="openDialog()"
|
@click="openDialog()"
|
||||||
>
|
>
|
||||||
新增用户
|
新增用户
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot="{ size, dynamicColumns }">
|
<template v-slot="{ size, dynamicColumns }">
|
||||||
<div
|
<div
|
||||||
v-if="selectedNum > 0"
|
v-if="selectedNum > 0"
|
||||||
v-motion-fade
|
v-motion-fade
|
||||||
class="bg-(--el-fill-color-light) w-full h-11.5 mb-2 pl-4 flex items-center"
|
class="bg-(--el-fill-color-light) w-full h-11.5 mb-2 pl-4 flex items-center"
|
||||||
>
|
>
|
||||||
<div class="flex-auto">
|
<div class="flex-auto">
|
||||||
<span
|
<span
|
||||||
style="font-size: var(--el-font-size-base)"
|
style="font-size: var(--el-font-size-base)"
|
||||||
class="text-[rgba(42,46,54,0.5)] dark:text-[rgba(220,220,242,0.5)]"
|
class="text-[rgba(42,46,54,0.5)] dark:text-[rgba(220,220,242,0.5)]"
|
||||||
>
|
>
|
||||||
已选 {{ selectedNum }} 项
|
已选 {{ selectedNum }} 项
|
||||||
</span>
|
</span>
|
||||||
<el-button type="primary" text @click="onSelectionCancel">
|
<el-button type="primary" text @click="onSelectionCancel">
|
||||||
取消选择
|
取消选择
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
|
||||||
<el-popconfirm title="是否确认删除?" @confirm="onbatchDel">
|
|
||||||
<template #reference>
|
|
||||||
<el-button type="danger" text class="mr-1!">
|
|
||||||
批量删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-popconfirm>
|
|
||||||
</div>
|
</div>
|
||||||
<pure-table
|
<el-popconfirm title="是否确认删除?" @confirm="onbatchDel">
|
||||||
ref="tableRef"
|
<template #reference>
|
||||||
row-key="id"
|
<el-button type="danger" text class="mr-1!"> 批量删除 </el-button>
|
||||||
adaptive
|
</template>
|
||||||
:adaptiveConfig="{ offsetBottom: 108 }"
|
</el-popconfirm>
|
||||||
align-whole="center"
|
</div>
|
||||||
table-layout="auto"
|
<pure-table
|
||||||
:loading="loading"
|
ref="tableRef"
|
||||||
:size="size"
|
row-key="id"
|
||||||
:data="dataList"
|
adaptive
|
||||||
:columns="dynamicColumns"
|
:adaptiveConfig="{ offsetBottom: 108 }"
|
||||||
:pagination="{ ...pagination, size }"
|
align-whole="center"
|
||||||
:header-cell-style="{
|
table-layout="auto"
|
||||||
background: 'var(--el-fill-color-light)',
|
:loading="loading"
|
||||||
color: 'var(--el-text-color-primary)'
|
:size="size"
|
||||||
}"
|
:data="dataList"
|
||||||
@selection-change="handleSelectionChange"
|
:columns="dynamicColumns"
|
||||||
@page-size-change="handleSizeChange"
|
:pagination="{ ...pagination, size }"
|
||||||
@page-current-change="handleCurrentChange"
|
:header-cell-style="{
|
||||||
>
|
background: 'var(--el-fill-color-light)',
|
||||||
<template #operation="{ row }">
|
color: 'var(--el-text-color-primary)'
|
||||||
<el-button
|
}"
|
||||||
class="reset-margin"
|
@selection-change="handleSelectionChange"
|
||||||
link
|
@page-size-change="handleSizeChange"
|
||||||
type="primary"
|
@page-current-change="handleCurrentChange"
|
||||||
:size="size"
|
>
|
||||||
:icon="useRenderIcon(EditPen)"
|
<template #operation="{ row }">
|
||||||
@click="openDialog('修改', row)"
|
<el-button
|
||||||
>
|
class="reset-margin"
|
||||||
修改
|
link
|
||||||
</el-button>
|
type="primary"
|
||||||
<el-popconfirm
|
:size="size"
|
||||||
:title="`是否确认删除用户编号为${row.id}的这条数据`"
|
:icon="useRenderIcon(EditPen)"
|
||||||
@confirm="handleDelete(row)"
|
@click="openDialog('修改', row)"
|
||||||
>
|
>
|
||||||
<template #reference>
|
修改
|
||||||
<el-button
|
</el-button>
|
||||||
class="reset-margin"
|
<el-popconfirm
|
||||||
link
|
:title="`是否确认删除用户编号为${row.id}的这条数据`"
|
||||||
type="primary"
|
@confirm="handleDelete(row)"
|
||||||
:size="size"
|
>
|
||||||
:icon="useRenderIcon(Delete)"
|
<template #reference>
|
||||||
>
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-popconfirm>
|
|
||||||
<el-dropdown>
|
|
||||||
<el-button
|
<el-button
|
||||||
class="ml-3! mt-0.5!"
|
class="reset-margin"
|
||||||
link
|
link
|
||||||
type="primary"
|
type="primary"
|
||||||
:size="size"
|
:size="size"
|
||||||
:icon="useRenderIcon(More)"
|
:icon="useRenderIcon(Delete)"
|
||||||
@click="handleUpdate(row)"
|
>
|
||||||
/>
|
删除
|
||||||
<template #dropdown>
|
</el-button>
|
||||||
<el-dropdown-menu>
|
</template>
|
||||||
<el-dropdown-item>
|
</el-popconfirm>
|
||||||
<el-button
|
<el-dropdown>
|
||||||
:class="buttonClass"
|
<el-button
|
||||||
link
|
class="ml-3! mt-0.5!"
|
||||||
type="primary"
|
link
|
||||||
:size="size"
|
type="primary"
|
||||||
:icon="useRenderIcon(Upload)"
|
:size="size"
|
||||||
@click="handleUpload(row)"
|
:icon="useRenderIcon(More)"
|
||||||
>
|
@click="handleUpdate(row)"
|
||||||
上传头像
|
/>
|
||||||
</el-button>
|
<template #dropdown>
|
||||||
</el-dropdown-item>
|
<el-dropdown-menu>
|
||||||
<el-dropdown-item>
|
<el-dropdown-item>
|
||||||
<el-button
|
<el-button
|
||||||
:class="buttonClass"
|
:class="buttonClass"
|
||||||
link
|
link
|
||||||
type="primary"
|
type="primary"
|
||||||
:size="size"
|
:size="size"
|
||||||
:icon="useRenderIcon(Password)"
|
:icon="useRenderIcon(Upload)"
|
||||||
@click="handleReset(row)"
|
@click="handleUpload(row)"
|
||||||
>
|
>
|
||||||
重置密码
|
上传头像
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
<el-dropdown-item>
|
<el-dropdown-item>
|
||||||
<el-button
|
<el-button
|
||||||
:class="buttonClass"
|
:class="buttonClass"
|
||||||
link
|
link
|
||||||
type="primary"
|
type="primary"
|
||||||
:size="size"
|
:size="size"
|
||||||
:icon="useRenderIcon(Role)"
|
:icon="useRenderIcon(Password)"
|
||||||
@click="handleRole(row)"
|
@click="handleReset(row)"
|
||||||
>
|
>
|
||||||
分配角色
|
重置密码
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
<el-dropdown-item>
|
||||||
</template>
|
<el-button
|
||||||
</el-dropdown>
|
:class="buttonClass"
|
||||||
</template>
|
link
|
||||||
</pure-table>
|
type="primary"
|
||||||
</template>
|
:size="size"
|
||||||
</PureTableBar>
|
:icon="useRenderIcon(Role)"
|
||||||
</div>
|
@click="handleRole(row)"
|
||||||
|
>
|
||||||
|
分配角色
|
||||||
|
</el-button>
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
</template>
|
||||||
|
</pure-table>
|
||||||
|
</template>
|
||||||
|
</PureTableBar>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
<svg width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M22 4V2H2v2h9v14.17l-5.5-5.5-1.42 1.41L12 22l7.92-7.92-1.42-1.41-5.5 5.5V4z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 161 B |
@@ -1 +0,0 @@
|
|||||||
<svg width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M4 2H2v20h2v-9h14.17l-5.5 5.5 1.41 1.42L22 12l-7.92-7.92-1.41 1.42 5.5 5.5H4z"/></svg>
|
|
||||||
|
Before Width: | Height: | Size: 163 B |
@@ -1,211 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
|
||||||
import { ref, computed, watch, getCurrentInstance } from "vue";
|
|
||||||
|
|
||||||
import Dept from "~icons/ri/git-branch-line";
|
|
||||||
// import Reset from "~icons/ri/restart-line";
|
|
||||||
import More2Fill from "~icons/ri/more-2-fill?width=18&height=18";
|
|
||||||
import OfficeBuilding from "~icons/ep/office-building";
|
|
||||||
import LocationCompany from "~icons/ep/add-location";
|
|
||||||
import ExpandIcon from "./svg/expand.svg?component";
|
|
||||||
import UnExpandIcon from "./svg/unexpand.svg?component";
|
|
||||||
|
|
||||||
interface Tree {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
highlight?: boolean;
|
|
||||||
children?: Tree[];
|
|
||||||
}
|
|
||||||
|
|
||||||
defineProps({
|
|
||||||
treeLoading: Boolean,
|
|
||||||
treeData: Array
|
|
||||||
});
|
|
||||||
|
|
||||||
const emit = defineEmits(["tree-select"]);
|
|
||||||
|
|
||||||
const treeRef = ref();
|
|
||||||
const isExpand = ref(true);
|
|
||||||
const searchValue = ref("");
|
|
||||||
const highlightMap = ref({});
|
|
||||||
const { proxy } = getCurrentInstance();
|
|
||||||
const defaultProps = {
|
|
||||||
children: "children",
|
|
||||||
label: "name"
|
|
||||||
};
|
|
||||||
const buttonClass = computed(() => {
|
|
||||||
return [
|
|
||||||
"h-5!",
|
|
||||||
"text-sm!",
|
|
||||||
"reset-margin",
|
|
||||||
"text-(--el-text-color-regular)!",
|
|
||||||
"dark:text-white!",
|
|
||||||
"dark:hover:text-primary!"
|
|
||||||
];
|
|
||||||
});
|
|
||||||
|
|
||||||
const filterNode = (value: string, data: Tree) => {
|
|
||||||
if (!value) return true;
|
|
||||||
return data.name.includes(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
function nodeClick(value) {
|
|
||||||
const nodeId = value.$treeNodeId;
|
|
||||||
highlightMap.value[nodeId] = highlightMap.value[nodeId]?.highlight
|
|
||||||
? Object.assign({ id: nodeId }, highlightMap.value[nodeId], {
|
|
||||||
highlight: false
|
|
||||||
})
|
|
||||||
: Object.assign({ id: nodeId }, highlightMap.value[nodeId], {
|
|
||||||
highlight: true
|
|
||||||
});
|
|
||||||
Object.values(highlightMap.value).forEach((v: Tree) => {
|
|
||||||
if (v.id !== nodeId) {
|
|
||||||
v.highlight = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
emit(
|
|
||||||
"tree-select",
|
|
||||||
highlightMap.value[nodeId]?.highlight
|
|
||||||
? Object.assign({ ...value, selected: true })
|
|
||||||
: Object.assign({ ...value, selected: false })
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleRowExpansionAll(status) {
|
|
||||||
isExpand.value = status;
|
|
||||||
const nodes = (proxy.$refs["treeRef"] as any).store._getAllNodes();
|
|
||||||
for (let i = 0; i < nodes.length; i++) {
|
|
||||||
nodes[i].expanded = status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 重置部门树状态(选中状态、搜索框值、树初始化) */
|
|
||||||
function onTreeReset() {
|
|
||||||
highlightMap.value = {};
|
|
||||||
searchValue.value = "";
|
|
||||||
toggleRowExpansionAll(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
watch(searchValue, val => {
|
|
||||||
treeRef.value!.filter(val);
|
|
||||||
});
|
|
||||||
|
|
||||||
defineExpose({ onTreeReset });
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div
|
|
||||||
v-loading="treeLoading"
|
|
||||||
class="h-full bg-bg_color overflow-hidden relative"
|
|
||||||
:style="{ minHeight: `calc(100vh - 141px)` }"
|
|
||||||
>
|
|
||||||
<div class="flex items-center h-8.5">
|
|
||||||
<el-input
|
|
||||||
v-model="searchValue"
|
|
||||||
class="ml-2"
|
|
||||||
size="small"
|
|
||||||
placeholder="请输入部门名称"
|
|
||||||
clearable
|
|
||||||
>
|
|
||||||
<template #suffix>
|
|
||||||
<el-icon class="el-input__icon">
|
|
||||||
<IconifyIconOffline
|
|
||||||
v-show="searchValue.length === 0"
|
|
||||||
icon="ri/search-line"
|
|
||||||
/>
|
|
||||||
</el-icon>
|
|
||||||
</template>
|
|
||||||
</el-input>
|
|
||||||
<el-dropdown :hide-on-click="false">
|
|
||||||
<More2Fill class="w-7 cursor-pointer outline-hidden" />
|
|
||||||
<template #dropdown>
|
|
||||||
<el-dropdown-menu>
|
|
||||||
<el-dropdown-item>
|
|
||||||
<el-button
|
|
||||||
:class="buttonClass"
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
:icon="useRenderIcon(isExpand ? ExpandIcon : UnExpandIcon)"
|
|
||||||
@click="toggleRowExpansionAll(isExpand ? false : true)"
|
|
||||||
>
|
|
||||||
{{ isExpand ? "折叠全部" : "展开全部" }}
|
|
||||||
</el-button>
|
|
||||||
</el-dropdown-item>
|
|
||||||
<!-- <el-dropdown-item>
|
|
||||||
<el-button
|
|
||||||
:class="buttonClass"
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
:icon="useRenderIcon(Reset)"
|
|
||||||
@click="onTreeReset"
|
|
||||||
>
|
|
||||||
重置状态
|
|
||||||
</el-button>
|
|
||||||
</el-dropdown-item> -->
|
|
||||||
</el-dropdown-menu>
|
|
||||||
</template>
|
|
||||||
</el-dropdown>
|
|
||||||
</div>
|
|
||||||
<el-divider />
|
|
||||||
<el-scrollbar height="calc(90vh - 88px)">
|
|
||||||
<el-tree
|
|
||||||
ref="treeRef"
|
|
||||||
:data="treeData"
|
|
||||||
node-key="id"
|
|
||||||
size="small"
|
|
||||||
:props="defaultProps"
|
|
||||||
default-expand-all
|
|
||||||
:expand-on-click-node="false"
|
|
||||||
:filter-node-method="filterNode"
|
|
||||||
@node-click="nodeClick"
|
|
||||||
>
|
|
||||||
<template #default="{ node, data }">
|
|
||||||
<div
|
|
||||||
:class="[
|
|
||||||
'rounded-sm',
|
|
||||||
'flex',
|
|
||||||
'items-center',
|
|
||||||
'select-none',
|
|
||||||
'hover:text-primary',
|
|
||||||
searchValue.trim().length > 0 &&
|
|
||||||
node.label.includes(searchValue) &&
|
|
||||||
'text-red-500',
|
|
||||||
highlightMap[node.id]?.highlight ? 'dark:text-primary' : ''
|
|
||||||
]"
|
|
||||||
:style="{
|
|
||||||
color: highlightMap[node.id]?.highlight
|
|
||||||
? 'var(--el-color-primary)'
|
|
||||||
: '',
|
|
||||||
background: highlightMap[node.id]?.highlight
|
|
||||||
? 'var(--el-color-primary-light-7)'
|
|
||||||
: 'transparent'
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<IconifyIconOffline
|
|
||||||
:icon="
|
|
||||||
data.type === 1
|
|
||||||
? OfficeBuilding
|
|
||||||
: data.type === 2
|
|
||||||
? LocationCompany
|
|
||||||
: Dept
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<span class="w-30! truncate!" :title="node.label">
|
|
||||||
{{ node.label }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-tree>
|
|
||||||
</el-scrollbar>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
:deep(.el-divider) {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.el-tree) {
|
|
||||||
--el-tree-node-hover-bg-color: transparent;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -3,7 +3,6 @@ import dayjs from "dayjs";
|
|||||||
import roleForm from "../form/role.vue";
|
import roleForm from "../form/role.vue";
|
||||||
import editForm from "../form/index.vue";
|
import editForm from "../form/index.vue";
|
||||||
import { zxcvbn } from "@zxcvbn-ts/core";
|
import { zxcvbn } from "@zxcvbn-ts/core";
|
||||||
import { handleTree } from "@/utils/tree";
|
|
||||||
import { message } from "@/utils/message";
|
import { message } from "@/utils/message";
|
||||||
import userAvatar from "@/assets/user.jpg";
|
import userAvatar from "@/assets/user.jpg";
|
||||||
import { usePublicHooks } from "../../hooks";
|
import { usePublicHooks } from "../../hooks";
|
||||||
@@ -17,12 +16,7 @@ import {
|
|||||||
hideTextAtIndex,
|
hideTextAtIndex,
|
||||||
deviceDetection
|
deviceDetection
|
||||||
} from "@pureadmin/utils";
|
} from "@pureadmin/utils";
|
||||||
import {
|
import { getUserRoleIds, getUserList, getAllRoleList } from "@/api/system";
|
||||||
getRoleIds,
|
|
||||||
getDeptList,
|
|
||||||
getUserList,
|
|
||||||
getAllRoleList
|
|
||||||
} from "@/api/system";
|
|
||||||
import {
|
import {
|
||||||
ElForm,
|
ElForm,
|
||||||
ElInput,
|
ElInput,
|
||||||
@@ -32,10 +26,8 @@ import {
|
|||||||
} from "element-plus";
|
} from "element-plus";
|
||||||
import { type Ref, h, ref, watch, computed, reactive, onMounted } from "vue";
|
import { type Ref, h, ref, watch, computed, reactive, onMounted } from "vue";
|
||||||
|
|
||||||
export function useUser(tableRef: Ref, treeRef: Ref) {
|
export function useUser(tableRef: Ref) {
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
// 左侧部门树的id
|
|
||||||
deptId: "",
|
|
||||||
username: "",
|
username: "",
|
||||||
phone: "",
|
phone: "",
|
||||||
status: ""
|
status: ""
|
||||||
@@ -48,9 +40,6 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
|||||||
const avatarInfo = ref();
|
const avatarInfo = ref();
|
||||||
const switchLoadMap = ref({});
|
const switchLoadMap = ref({});
|
||||||
const { switchStyle } = usePublicHooks();
|
const { switchStyle } = usePublicHooks();
|
||||||
const higherDeptOptions = ref();
|
|
||||||
const treeData = ref([]);
|
|
||||||
const treeLoading = ref(true);
|
|
||||||
const selectedNum = ref(0);
|
const selectedNum = ref(0);
|
||||||
const pagination = reactive<PaginationProps>({
|
const pagination = reactive<PaginationProps>({
|
||||||
total: 0,
|
total: 0,
|
||||||
@@ -279,36 +268,15 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
|||||||
const resetForm = formEl => {
|
const resetForm = formEl => {
|
||||||
if (!formEl) return;
|
if (!formEl) return;
|
||||||
formEl.resetFields();
|
formEl.resetFields();
|
||||||
form.deptId = "";
|
|
||||||
treeRef.value.onTreeReset();
|
|
||||||
onSearch();
|
onSearch();
|
||||||
};
|
};
|
||||||
|
|
||||||
function onTreeSelect({ id, selected }) {
|
|
||||||
form.deptId = selected ? id : "";
|
|
||||||
onSearch();
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatHigherDeptOptions(treeList) {
|
|
||||||
// 根据返回数据的status字段值判断追加是否禁用disabled字段,返回处理后的树结构,用于上级部门级联选择器的展示(实际开发中也是如此,不可能前端需要的每个字段后端都会返回,这时需要前端自行根据后端返回的某些字段做逻辑处理)
|
|
||||||
if (!treeList || !treeList.length) return;
|
|
||||||
const newTreeList = [];
|
|
||||||
for (let i = 0; i < treeList.length; i++) {
|
|
||||||
treeList[i].disabled = treeList[i].status === 0 ? true : false;
|
|
||||||
formatHigherDeptOptions(treeList[i].children);
|
|
||||||
newTreeList.push(treeList[i]);
|
|
||||||
}
|
|
||||||
return newTreeList;
|
|
||||||
}
|
|
||||||
|
|
||||||
function openDialog(title = "新增", row?: FormItemProps) {
|
function openDialog(title = "新增", row?: FormItemProps) {
|
||||||
addDialog({
|
addDialog({
|
||||||
title: `${title}用户`,
|
title: `${title}用户`,
|
||||||
props: {
|
props: {
|
||||||
formInline: {
|
formInline: {
|
||||||
title,
|
title,
|
||||||
higherDeptOptions: formatHigherDeptOptions(higherDeptOptions.value),
|
|
||||||
parentId: row?.dept.id ?? 0,
|
|
||||||
nickname: row?.nickname ?? "",
|
nickname: row?.nickname ?? "",
|
||||||
username: row?.username ?? "",
|
username: row?.username ?? "",
|
||||||
password: row?.password ?? "",
|
password: row?.password ?? "",
|
||||||
@@ -459,7 +427,7 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
|||||||
/** 分配角色 */
|
/** 分配角色 */
|
||||||
async function handleRole(row) {
|
async function handleRole(row) {
|
||||||
// 选中的角色列表
|
// 选中的角色列表
|
||||||
const ids = (await getRoleIds({ userId: row.id })).data ?? [];
|
const ids = (await getUserRoleIds(row.id)).data ?? [];
|
||||||
addDialog({
|
addDialog({
|
||||||
title: `分配 ${row.username} 用户的角色`,
|
title: `分配 ${row.username} 用户的角色`,
|
||||||
props: {
|
props: {
|
||||||
@@ -486,18 +454,7 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
treeLoading.value = true;
|
|
||||||
onSearch();
|
onSearch();
|
||||||
|
|
||||||
// 归属部门
|
|
||||||
const { code, data } = await getDeptList();
|
|
||||||
if (code === 200) {
|
|
||||||
higherDeptOptions.value = handleTree(data);
|
|
||||||
treeData.value = handleTree(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
treeLoading.value = false;
|
|
||||||
|
|
||||||
// 角色列表
|
// 角色列表
|
||||||
roleOptions.value = (await getAllRoleList()).data ?? [];
|
roleOptions.value = (await getAllRoleList()).data ?? [];
|
||||||
});
|
});
|
||||||
@@ -507,8 +464,6 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
|||||||
loading,
|
loading,
|
||||||
columns,
|
columns,
|
||||||
dataList,
|
dataList,
|
||||||
treeData,
|
|
||||||
treeLoading,
|
|
||||||
selectedNum,
|
selectedNum,
|
||||||
pagination,
|
pagination,
|
||||||
buttonClass,
|
buttonClass,
|
||||||
@@ -517,7 +472,6 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
|||||||
resetForm,
|
resetForm,
|
||||||
onbatchDel,
|
onbatchDel,
|
||||||
openDialog,
|
openDialog,
|
||||||
onTreeSelect,
|
|
||||||
handleUpdate,
|
handleUpdate,
|
||||||
handleDelete,
|
handleDelete,
|
||||||
handleUpload,
|
handleUpload,
|
||||||
|
|||||||
Reference in New Issue
Block a user