feat: 添加菜单管理页面

master
绝弹 2023-09-27 22:20:31 +08:00
parent 2f49f3814c
commit 5f7e71ae90
7 changed files with 248 additions and 20 deletions

View File

@ -1,7 +1,7 @@
<template> <template>
<a-dropdown position="br"> <a-dropdown position="br">
<span class="inline-flex items-center cursor-pointer rounded hover:bg-gray-100 px-2 py-1.5"> <span class="inline-flex items-center cursor-pointer rounded hover:bg-gray-100 px-2 py-1.5">
<a-avatar :size="20"> <a-avatar :size="24">
<img :src="userStore.avatar || 'https://github.com/juetan.png'" :alt="userStore.nickname" /> <img :src="userStore.avatar || 'https://github.com/juetan.png'" :alt="userStore.nickname" />
</a-avatar> </a-avatar>
<span class="mx-2"> <span class="mx-2">
@ -11,9 +11,14 @@
</span> </span>
<template #content> <template #content>
<a-doption> <a-doption>
<div class="w-[160px] leading-4 my-1"> <div class="w-[200px] flex items-center gap-2">
{{ userStore.nickname }} <a-avatar :size="32">
<div class="text-xs text-gray-500">@{{ userStore.username }}</div> <img :src="userStore.avatar || 'https://github.com/juetan.png'" :alt="userStore.nickname" />
</a-avatar>
<div class="leading-4 my-2">
{{ userStore.nickname }}
<div class="text-xs text-gray-500">@{{ userStore.username }}</div>
</div>
</div> </div>
</a-doption> </a-doption>
<a-divider :margin="4"></a-divider> <a-divider :margin="4"></a-divider>

View File

@ -25,9 +25,6 @@
<a-form-item label="个人描述"> <a-form-item label="个人描述">
<a-textarea v-model="user.description" placeholder="请输入" class="!w-[432px] h-24"></a-textarea> <a-textarea v-model="user.description" placeholder="请输入" class="!w-[432px] h-24"></a-textarea>
</a-form-item> </a-form-item>
<a-form-item label="密码">
<a-button>修改密码</a-button>
</a-form-item>
<a-form-item label="性别"> <a-form-item label="性别">
<a-radio-group v-model="user.gender" type="button"> <a-radio-group v-model="user.gender" type="button">
<a-radio :value="1"></a-radio> <a-radio :value="1"></a-radio>
@ -38,6 +35,7 @@
<a-form-item label="出生日期"> <a-form-item label="出生日期">
<a-date-picker v-model="user.birth"></a-date-picker> <a-date-picker v-model="user.birth"></a-date-picker>
</a-form-item> </a-form-item>
<a-button type="primary">保存修改</a-button>
</a-form> </a-form>
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="5" title="消息推送"> <a-tab-pane key="5" title="消息推送">
@ -63,6 +61,24 @@
</a-form-item> </a-form-item>
</a-form> </a-form>
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="8" title="账号密码">
<template #title>
<i class="icon-park-outline-lock"></i>
账号密码
</template>
<a-form :model="user" layout="vertical">
<a-form-item label="原密码">
<a-input placeholder="请输入原密码"></a-input>
</a-form-item>
<a-form-item label="新密码">
<a-input placeholder="请输入新密码"></a-input>
</a-form-item>
<a-form-item label="确认新密码">
<a-input placeholder="请再次输入新密码"></a-input>
</a-form-item>
<a-button type="primary">修改密码</a-button>
</a-form>
</a-tab-pane>
<a-tab-pane key="2" title="主题偏好"> <a-tab-pane key="2" title="主题偏好">
<template #title> <template #title>
<i class="icon-park-outline-theme"></i> <i class="icon-park-outline-theme"></i>

View File

@ -2,9 +2,7 @@
<BreadPage> <BreadPage>
<template #content> <template #content>
<div class="p-4"> <div class="p-4">
<a-alert :closable="true" class="mb-2"> <a-alert :closable="true" class="mb-2"> 仅展示近 90 天内的数据如需查看更多数据请联系管理员 </a-alert>
仅展示近 90 天内的数据如需查看更多数据请联系管理员
</a-alert>
<div class="bg-white p-4"> <div class="bg-white p-4">
<Table v-bind="table"></Table> <Table v-bind="table"></Table>
</div> </div>
@ -17,7 +15,6 @@
import { api } from "@/api"; import { api } from "@/api";
import { Table, useTable } from "@/components"; import { Table, useTable } from "@/components";
import { dayjs } from "@/libs/dayjs"; import { dayjs } from "@/libs/dayjs";
import { Tag } from "@arco-design/web-vue";
const table = useTable({ const table = useTable({
data: async (model, paging) => { data: async (model, paging) => {
@ -35,9 +32,13 @@ const table = useTable({
render: ({ record: { status, description } }) => { render: ({ record: { status, description } }) => {
return ( return (
<span> <span>
<Tag color={status === null || status ? "green" : "red"} class="mr-2"> <span
{status === null || status ? "成功" : "失败"} class={
</Tag> status === null || status
? "text-base text-green-500 icon-park-outline-check-one mr-2"
: "text-base text-red-500 icon-park-outline-close-one mr-2"
}
></span>
{description} {description}
</span> </span>
); );
@ -62,7 +63,7 @@ const table = useTable({
{ {
title: "登陆时间", title: "登陆时间",
dataIndex: "createdAt", dataIndex: "createdAt",
width: 120, width: 160,
render: ({ record }) => dayjs(record.createdAt).fromNow(), render: ({ record }) => dayjs(record.createdAt).fromNow(),
}, },
], ],
@ -71,10 +72,10 @@ const table = useTable({
{ {
field: "nickname", field: "nickname",
label: "登陆账号", label: "登陆账号",
type: "input", type: "search",
required: false, required: false,
nodeProps: { nodeProps: {
placeholder: "请输入登陆账号", // placeholder: "",
}, },
itemProps: { itemProps: {
hideLabel: true, hideLabel: true,

View File

@ -0,0 +1,197 @@
<template>
<bread-page class="">
<Table v-bind="table"></Table>
</bread-page>
</template>
<script setup lang="tsx">
import { api } from "@/api";
import { Table, useTable } from "@/components";
import { dayjs } from "@/libs/dayjs";
import { menus } from "@/router";
import { cloneDeep } from "lodash-es";
const items = cloneDeep(menus) as any;
for (const item of items) {
item.checked = false;
// if (item.icon) {
// const icon = item.icon;
// item.icon = () => <i class={icon}></i>;
// }
item.switcherIcon = () => null;
if (item.children) {
for (const child of item.children) {
// if (child.icon) {
// const icon = child.icon;
// child.icon = () => <i class={icon}></i>;
// }
child.checked = false;
}
}
}
const state = reactive({
menus: items,
visible: true,
});
const indeter = (items: any[]) => {
if (!items) {
return false;
}
const checked = items.filter((item) => item.checked);
return checked.length > 0 && checked.length < items.length;
};
const onItemChange = (item: any, menu: any) => {
const checked = menu.children.filter((item: any) => item.checked);
if (checked === 0) {
menu.checked = false;
} else if (checked === menu.children.length) {
menu.checked = true;
}
};
const table = useTable({
data: items,
columns: [
{
title: "角色名称",
dataIndex: "title",
width: 180,
render: ({ record }) => {
return (
<span>
<i class={`${record.icon} mr-1 ml-1 vertical-[-2px]`}></i>
{ record.title }
</span>
)
}
},
{
title: "类型",
dataIndex: "description",
align: 'center',
width: 80,
render: () => <a-tag color="blue">菜单</a-tag>,
},
{
title: "访问路径",
dataIndex: "path",
},
{
title: "启用",
dataIndex: "createdAt",
width: 80,
align: 'center',
render: ({ record }) => <a-switch size="small" checked-color="#3c9"></a-switch>,
},
{
title: "创建时间",
dataIndex: "createdAt",
width: 200,
render: ({ record }) => dayjs(record.createdAt).format(),
},
{
title: "操作",
type: "button",
width: 184,
buttons: [
{
type: "modify",
text: "修改",
},
{
text: "分配权限",
onClick: ({ record }) => {
console.log(record);
},
},
{
text: "删除",
type: "delete",
onClick: ({ record }) => {
return api.role.delRole(record.id);
},
},
],
},
],
pagination: {
visible: false
},
search: {
items: [
{
extend: "name",
required: false,
nodeProps: {
placeholder: "请输入角色名称",
},
itemProps: {
hideLabel: true,
},
},
],
},
create: {
title: "新建角色",
modalProps: {
width: 580,
maskClosable: false,
},
formProps: {
layout: "vertical",
},
items: [
{
field: "name",
label: "角色名称",
type: "input",
required: true,
},
{
field: "slug",
label: "角色标识",
type: "input",
},
{
field: "description",
label: "个人描述",
type: "textarea",
},
{
field: "permissionIds",
label: "关联权限",
type: "select",
options: () => api.permission.getPermissions(),
nodeProps: {
multiple: true,
},
},
],
submit: ({ model }) => {
return api.role.addRole(model);
},
},
modify: {
extend: true,
title: "修改角色",
submit: ({ model }) => {
return api.role.updateRole(model.id, model);
},
},
});
</script>
<style lang="less"></style>
<route lang="json">
{
"meta": {
"sort": 10201,
"title": "菜单管理",
"icon": "icon-park-outline-add-subtract"
}
}
</route>

View File

@ -22,7 +22,7 @@ const table = useTable({
return ( return (
<div class="flex flex-col overflow-hidden"> <div class="flex flex-col overflow-hidden">
<span>{record.name}</span> <span>{record.name}</span>
<span class="text-gray-400 text-xs truncate">{record.slug}</span> <span class="text-gray-400 text-xs truncate">@{record.slug}</span>
</div> </div>
); );
}, },
@ -112,7 +112,7 @@ const table = useTable({
extend: true, extend: true,
title: "修改权限", title: "修改权限",
submit: ({ model }) => { submit: ({ model }) => {
return api.permission.updatePermission(model.id, model); return api.permission.setPermission(model.id, model);
}, },
}, },
}); });

View File

@ -2,7 +2,7 @@
<BreadPage> <BreadPage>
<Table v-bind="table"> <Table v-bind="table">
<template #action> <template #action>
<a-button status="danger" type="outline" :disabled="true"> <a-button status="danger" :disabled="true">
<template #icon> <template #icon>
<i class="icon-park-outline-delete"></i> <i class="icon-park-outline-delete"></i>
</template> </template>
@ -24,6 +24,11 @@ const table = useTable({
data: async (model, paging) => { data: async (model, paging) => {
return api.user.getUsers({ ...model, ...paging }); return api.user.getUsers({ ...model, ...paging });
}, },
tableProps: {
rowSelection: {
showCheckedAll: true
}
},
columns: [ columns: [
{ {
title: "用户昵称", title: "用户昵称",

View File

@ -9,6 +9,9 @@ body {
margin-left: 4px; margin-left: 4px;
margin-right: 4px; margin-right: 4px;
border-radius: 4px; border-radius: 4px;
&:not(:first-child) {
margin-top: 4px;
}
} }
.arco-layout-sider { .arco-layout-sider {
box-shadow: none; box-shadow: none;
@ -39,6 +42,7 @@ body {
margin-top: 8px; margin-top: 8px;
} }
[class^="icon-"] { [class^="icon-"] {
font-size: 16px;
vertical-align: -2px; vertical-align: -2px;
} }
.arco-menu-item { .arco-menu-item {