feat: 添加角色和权限页面

master
绝弹 2023-08-01 21:07:31 +08:00
parent 6ce573fda7
commit 9096d4d71d
12 changed files with 913 additions and 444 deletions

2
.env
View File

@ -24,7 +24,7 @@ VITE_API_BASE_URL = http://127.0.0.1:3030
VITE_API_PROXY_URL = /api
# API文档地址(开发环境) 备注需为openapi规范的json文件
VITE_API_DOCS_URL = https://petstore.swagger.io/v2/swagger.json
VITE_API_DOCS_URL = http://127.0.0.1:3030/openapi.json
# 端口号(开发环境)
VITE_DEV_PORT = 3020

File diff suppressed because it is too large Load Diff

View File

@ -90,7 +90,7 @@ export const FormModal = defineComponent({
try {
const model = formRef.value?.getModel() || {};
const res = await props.submit?.({ items: props.items, model });
res?.message && Message.success(`提示: ${res.message}`);
res?.data?.message && Message.success(`提示: ${res.data.message}`);
emit("submited", res);
} catch (error: any) {
error.message && Message.error(`提示: ${error.message}`);

View File

@ -23,8 +23,8 @@ const initOptions = ({ item, model }: any) => {
const fetchData = item.options;
item._updateOptions = async () => {
let data = await fetchData({ item, model });
if (Array.isArray(data?.data)) {
data = data.data.map((i: any) => ({ label: i.name, value: i.id }));
if (Array.isArray(data?.data?.data)) {
data = data.data.data.map((i: any) => ({ label: i.name, value: i.id }));
}
if (Array.isArray(data)) {
item.nodeProps.options.splice(0);

View File

@ -47,7 +47,7 @@ export const Form = defineComponent({
props.items.forEach((item: any) => {
const node = nodeMap[item.type as NodeType];
defaultsDeep(item, { nodeProps: node?.nodeProps ?? {} });
(node as any).init?.({ item, model: props.model });
(node as any)?.init?.({ item, model: props.model });
});
const getItem = (field: string) => {

View File

@ -97,11 +97,10 @@ export const Table = defineComponent({
try {
loading.value = true;
const resData = await props.data(model, paging);
const { data = [], meta = {} } = resData || {};
const { page: pageNum, total } = meta;
const { data = [], total = 0 } = resData?.data || {};
renderData.value = data;
props.pagination.total = total;
props.pagination.current = pageNum;
props.pagination.current = paging.page;
} catch (error) {
console.log("table error", error);
} finally {
@ -160,9 +159,9 @@ export const Table = defineComponent({
<div class={`mb-2 flex justify-between ${!this.inlined && "mt-2"}`}>
<div class="flex-1 flex gap-2">
{this.create && <FormModal ref="createRef" onOk={this.reloadData} {...(this.create as any)}></FormModal>}
{this.create && <FormModal ref="createRef" onSubmited={this.reloadData} {...(this.create as any)}></FormModal>}
{this.modify && (
<FormModal ref="modifyRef" onOk={this.reloadData} trigger={false} {...(this.modify as any)}></FormModal>
<FormModal ref="modifyRef" onSubmited={this.reloadData} trigger={false} {...(this.modify as any)}></FormModal>
)}
{this.$slots.action?.()}
</div>

View File

@ -68,7 +68,7 @@ type ExtendableFormItem = (
/**
* common.itemsfield
*/
extend: string;
extend?: string;
} & Partial<IFormItem>)
| IFormItem
)[];

View File

@ -1,6 +1,7 @@
import { Link, Message, Modal, TableColumnData } from "@arco-design/web-vue";
import { defaultsDeep, isArray, isFunction, mergeWith } from "lodash-es";
import { reactive } from "vue";
import { useFormModal } from "../form";
import { TableInstance } from "./table";
import { config } from "./table.config";
import { UseTableOptions } from "./use-interface";
@ -125,14 +126,14 @@ export const useTable = (optionsOrFn: UseTableOptions | (() => UseTableOptions))
*
*/
if (options.create && propTruly(options.create, "extend")) {
options.create = merge(options.common, options.create);
options.create = useFormModal(merge(options.common, options.create)) as any;
}
/**
*
*/
if (options.modify && propTruly(options.modify, "extend")) {
options.modify = merge(options.common, options.modify);
options.modify = useFormModal(merge(options.common, options.modify)) as any;
}
return reactive({ ...options, columns });

View File

@ -0,0 +1,133 @@
<template>
<BreadPage>
<Table v-bind="table"></Table>
</BreadPage>
</template>
<script setup lang="tsx">
import { api } from "@/api";
import { Table, useTable } from "@/components";
import { dayjs } from "@/plugins";
const table = useTable({
data: async (model, paging) => {
return api.permission.getPermissions();
},
columns: [
{
type: "index",
},
{
title: "权限名称",
dataIndex: "username",
width: 200,
render({ record }) {
return (
<div class="flex flex-col overflow-hidden">
<span>{record.name}</span>
<span class="text-gray-400 text-xs truncate">标识{record.slug}</span>
</div>
);
},
},
{
title: "权限描述",
dataIndex: "description",
},
{
title: "创建时间",
dataIndex: "createdAt",
width: 200,
render: ({ record }) => dayjs(record.createdAt).format(),
},
{
title: "操作",
type: "button",
width: 70,
buttons: [
{
type: "modify",
text: "修改",
},
],
},
],
common: {
items: [
{
field: "name",
label: "角色名称",
type: "input",
required: true,
},
{
field: "slug",
label: "角色标识",
type: "input",
},
{
field: "description",
label: "个人描述",
type: "input",
},
{
field: "permissions",
label: "关联角色",
type: "select",
options: () => api.permission.getPermissions(),
component: ({ item }) => {
return (
<div class="flex flex-col">
<span>关联角色</span>
<div>
<span></span>
</div>
</div>
);
}
},
],
modalProps: {
width: 580,
maskClosable: false,
},
formProps: {
layout: "vertical",
},
},
search: {
items: [
{
field: "name",
label: "权限名称",
type: "input",
required: false,
},
],
},
create: {
title: "添加权限",
submit: ({ model }) => {
return api.permission.addPermission(model as any);
},
},
modify: {
title: "修改权限",
submit: ({ model }) => {
return api.permission.updatePermission(model.id, model);
},
},
});
</script>
<style scoped></style>
<route lang="json">
{
"meta": {
"sort": 10303,
"title": "权限管理",
"icon": "icon-park-outline-permissions"
}
}
</route>

View File

@ -13,7 +13,7 @@
<route lang="json">
{
"meta": {
"sort": 10401,
"sort": 10300,
"title": "文章管理",
"icon": "icon-park-outline-document-folder"
}

123
src/pages/role/index.vue Normal file
View File

@ -0,0 +1,123 @@
<template>
<BreadPage>
<Table v-bind="table"></Table>
</BreadPage>
</template>
<script setup lang="tsx">
import { api } from "@/api";
import { Table, useTable } from "@/components";
import { dayjs } from "@/plugins";
const table = useTable({
data: async (model, paging) => {
return api.role.getRoles();
},
columns: [
{
type: "index",
},
{
title: "角色名称",
dataIndex: "username",
width: 200,
render({ record }) {
return (
<div class="flex flex-col overflow-hidden">
<span>{record.name}</span>
<span class="text-gray-400 text-xs truncate">标识{record.slug}</span>
</div>
);
},
},
{
title: "角色描述",
dataIndex: "description",
},
{
title: "创建时间",
dataIndex: "createdAt",
width: 200,
render: ({ record }) => dayjs(record.createdAt).format(),
},
{
title: "操作",
type: "button",
width: 70,
buttons: [
{
type: "modify",
text: "修改",
},
],
},
],
common: {
items: [
{
field: "name",
label: "角色名称",
type: "input",
required: true,
},
{
field: "slug",
label: "角色标识",
type: "input",
},
{
field: "description",
label: "个人描述",
type: "textarea",
},
{
field: "permissions",
label: "关联角色",
type: "select",
options: () => api.permission.getPermissions(),
},
],
modalProps: {
width: 580,
maskClosable: false,
},
formProps: {
layout: "vertical",
},
},
search: {
items: [
{
field: "name",
label: "角色名称",
type: "input",
required: false,
},
],
},
create: {
title: "新建角色",
submit: ({ model }) => {
return api.role.addRole(model as any);
},
},
modify: {
title: "修改角色",
submit: ({ model }) => {
return api.role.updateRole(model.id, model);
},
},
});
</script>
<style scoped></style>
<route lang="json">
{
"meta": {
"sort": 10302,
"title": "角色管理",
"icon": "icon-park-outline-key"
}
}
</route>

View File

@ -5,27 +5,50 @@
</template>
<script setup lang="tsx">
import { api } from "@/api";
import { Table, useTable } from "@/components";
import { dayjs } from "@/plugins";
import { Avatar } from "@arco-design/web-vue";
const table = useTable({
data: async (model, paging) => ({ data: [{}], meta: { total: 0 } }),
data: async (model, paging) => {
return api.user.getUsers({ ...model, ...paging });
},
columns: [
{
type: "index",
},
{
title: "姓名",
title: "用户昵称",
dataIndex: "username",
width: 200,
render({ record }) {
return (
<div class="flex items-center">
<Avatar size={32}>
<img src={record.avatar} alt="" />
</Avatar>
<span class="ml-2 flex-1 flex flex-col overflow-hidden">
<span>{record.nickname}</span>
<span class="text-gray-400 text-xs truncate">账号{record.username}</span>
</span>
</div>
);
},
},
{
title: "昵称",
dataIndex: "name",
title: "用户描述",
dataIndex: "description",
},
{
title: "用户邮箱",
dataIndex: "email",
},
{
title: "创建时间",
dataIndex: "createdAt",
width: 200,
render: ({ record }) => dayjs(record.createdAt).format(),
},
{
title: "操作",
@ -46,13 +69,13 @@ const table = useTable({
items: [
{
field: "username",
label: "姓名1",
label: "登录账号",
type: "input",
required: true,
},
{
field: "nickname",
label: "昵称",
label: "用户昵称",
type: "input",
},
{