feat: add role management page ui

Signed-off-by: Ryan Wang <i@ryanc.cc>
pull/581/head
Ryan Wang 2022-05-30 22:38:57 +08:00
parent 3c19615656
commit 5c9fd988f5
9 changed files with 556 additions and 19 deletions

View File

@ -73,6 +73,10 @@ import IconExchange from "~icons/ri/exchange-line";
import IconGitHub from "~icons/ri/github-fill";
// @ts-ignore
import IconUpload from "~icons/ri/upload-cloud-2-line";
// @ts-ignore
import IconShieldUser from "~icons/ri/shield-user-line";
// @ts-ignore
import IconGitBranch from "~icons/ri/git-branch-line";
export {
IconDashboard,
@ -112,4 +116,6 @@ export {
IconExchange,
IconGitHub,
IconUpload,
IconShieldUser,
IconGitBranch,
};

View File

@ -24,6 +24,7 @@ import PluginList from "../views/system/plugins/PluginList.vue";
import PluginDetail from "../views/system/plugins/PluginDetail.vue";
import UserList from "../views/system/users/UserList.vue";
import RoleList from "../views/system/roles/RoleList.vue";
import RoleDetail from "../views/system/roles/RoleDetail.vue";
import UserDetail from "../views/system/users/UserDetail.vue";
import ProfileModification from "../views/system/users/ProfileModification.vue";
import PasswordChange from "../views/system/users/PasswordChange.vue";
@ -209,6 +210,11 @@ export const routes: Array<RouteRecordRaw> = [
name: "Roles",
component: RoleList,
},
{
path: "roles/:id",
name: "RoleDetail",
component: RoleDetail,
},
],
},
],

View File

@ -14,6 +14,7 @@ import {
IconArrowLeft,
IconArrowRight,
IconBookRead,
IconDeleteBin,
IconSettings,
} from "@/core/icons";
import { posts } from "./posts-mock";
@ -308,12 +309,20 @@ const handleRouteToEditor = (post: any) => {
<IconBookRead class="mr-2 self-center" />
</template>
<template #actions>
<VButton :route="{ name: 'PostEditor' }" type="secondary">
<template #icon>
<IconAddCircle class="h-full w-full" />
</template>
新建
</VButton>
<VSpace>
<VButton size="sm">
<template #icon>
<IconDeleteBin class="h-full w-full" />
</template>
回收站
</VButton>
<VButton :route="{ name: 'PostEditor' }" type="secondary">
<template #icon>
<IconAddCircle class="h-full w-full" />
</template>
新建
</VButton>
</VSpace>
</template>
</VPageHeader>
@ -345,6 +354,42 @@ const handleRouteToEditor = (post: any) => {
</div>
<div class="mt-4 flex sm:mt-0">
<VSpace spacing="lg">
<FloatingDropdown>
<div
class="flex cursor-pointer select-none items-center text-sm text-gray-700 hover:text-black"
>
<span class="mr-0.5">状态</span>
<span>
<IconArrowDown />
</span>
</div>
<template #popper>
<div class="w-72 p-4">
<ul class="space-y-1">
<li
class="flex cursor-pointer items-center rounded px-3 py-2 text-sm text-gray-600 hover:bg-gray-100 hover:text-gray-900"
>
<span class="truncate">全部</span>
</li>
<li
class="flex cursor-pointer items-center rounded px-3 py-2 text-sm text-gray-600 hover:bg-gray-100 hover:text-gray-900"
>
<span class="truncate">已发布</span>
</li>
<li
class="flex cursor-pointer items-center rounded px-3 py-2 text-sm text-gray-600 hover:bg-gray-100 hover:text-gray-900"
>
<span class="truncate">草稿</span>
</li>
<li
class="flex cursor-pointer items-center rounded px-3 py-2 text-sm text-gray-600 hover:bg-gray-100 hover:text-gray-900"
>
<span class="truncate">未审核</span>
</li>
</ul>
</div>
</template>
</FloatingDropdown>
<FloatingDropdown>
<div
class="flex cursor-pointer select-none items-center text-sm text-gray-700 hover:text-black"

View File

@ -6,7 +6,12 @@ import { VTag } from "@/components/base/tag";
import { VTabItem, VTabs } from "@/components/base/tabs";
import { VInput } from "@/components/base/input";
import { VPageHeader } from "@/components/base/header";
import { IconArrowDown, IconPages, IconSettings } from "@/core/icons";
import {
IconAddCircle,
IconArrowDown,
IconPages,
IconSettings,
} from "@/core/icons";
import { ref } from "vue";
import { users } from "@/views/system/users/users-mock";
import halo from "@/assets/logo-mock/halo.png";
@ -62,7 +67,12 @@ const activeId = ref("advanced");
<IconPages class="mr-2 self-center" />
</template>
<template #actions>
<VButton type="secondary">发布</VButton>
<VButton type="secondary">
<template #icon>
<IconAddCircle class="h-full w-full" />
</template>
新建
</VButton>
</template>
</VPageHeader>

View File

@ -0,0 +1,159 @@
<script lang="ts" setup>
import { VPageHeader } from "@/components/base/header";
import { VButton } from "@/components/base/button";
import { VTabbar } from "@/components/base/tabs";
import { VTag } from "@/components/base/tag";
import { VCard } from "@/components/base/card";
import { IconArrowRight, IconGitBranch, IconShieldUser } from "@/core/icons";
import { useRoute, useRouter } from "vue-router";
import { roles } from "@/views/system/roles/roles-mock";
import { ref } from "vue";
import { users } from "@/views/system/users/users-mock";
const route = useRoute();
const role = ref();
const roleActiveId = ref("detail");
if (route.params.id) {
role.value = roles.find((r) => r.id === Number(route.params.id));
} else {
role.value = roles[0];
}
const router = useRouter();
const handleRouteToUser = (username: string) => {
router.push({ name: "UserDetail", params: { username } });
};
</script>
<template>
<VPageHeader :title="`角色:${role.name}`">
<template #icon>
<IconShieldUser class="mr-2 self-center" />
</template>
<template #actions>
<VButton type="secondary">
<template #icon>
<IconGitBranch class="h-full w-full" />
</template>
Fork
</VButton>
</template>
</VPageHeader>
<div class="m-0 md:m-4">
<VCard :body-class="['!p-0']">
<template #header>
<VTabbar
v-model:active-id="roleActiveId"
:items="[
{ id: 'detail', label: '详情' },
{ id: 'permissions', label: '权限设置' },
]"
class="w-full !rounded-none"
type="outline"
></VTabbar>
</template>
<div v-if="roleActiveId === 'detail'">
<div class="px-4 py-4 sm:px-6">
<h3 class="text-lg font-medium leading-6 text-gray-900">权限信息</h3>
<p
class="mt-1 flex max-w-2xl items-center gap-2 text-sm text-gray-500"
>
<span>包含 {{ role.permissions }} 个权限</span>
</p>
</div>
<div class="border-t border-gray-200">
<dl class="divide-y divide-gray-100">
<div
class="bg-white px-4 py-5 hover:bg-gray-50 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6"
>
<dt class="text-sm font-medium text-gray-900">名称</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
{{ role.name }}
</dd>
</div>
<div
class="bg-white px-4 py-5 hover:bg-gray-50 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6"
>
<dt class="text-sm font-medium text-gray-900">类型</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
<VTag>系统保留</VTag>
</dd>
</div>
<div
class="bg-white px-4 py-5 hover:bg-gray-50 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6"
>
<dt class="text-sm font-medium text-gray-900">描述</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
超级管理员
</dd>
</div>
<div
class="bg-white px-4 py-5 hover:bg-gray-50 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6"
>
<dt class="text-sm font-medium text-gray-900">创建时间</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
2020-01-01
</dd>
</div>
<div
class="bg-gray-50 px-4 py-5 hover:bg-gray-50 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6"
>
<dt class="text-sm font-medium text-gray-900">用户</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
<div
class="h-96 overflow-y-auto overflow-x-hidden rounded-sm bg-white shadow-sm transition-all hover:shadow"
>
<ul class="divide-y divide-gray-100" role="list">
<li
v-for="(user, index) in users"
:key="index"
class="block cursor-pointer hover:bg-gray-50"
@click="handleRouteToUser(user.username)"
>
<div class="flex items-center px-4 py-4">
<div class="flex min-w-0 flex-1 items-center">
<div class="flex-shrink-0">
<div
class="h-12 w-12 overflow-hidden rounded border bg-white hover:shadow-sm"
>
<img
:alt="user.name"
:src="user.avatar"
class="h-full w-full"
/>
</div>
</div>
<div
class="min-w-0 flex-1 px-4 md:grid md:grid-cols-2 md:gap-4"
>
<div>
<p
class="truncate text-sm font-medium text-gray-900"
>
{{ user.name }}
</p>
<p class="mt-2 flex items-center">
<span class="text-xs text-gray-500">
{{ user.username }}
</span>
</p>
</div>
</div>
</div>
<div>
<IconArrowRight />
</div>
</div>
</li>
</ul>
</div>
</dd>
</div>
</dl>
</div>
</div>
</VCard>
</div>
</template>

View File

@ -1,2 +1,256 @@
<script lang="ts" setup></script>
<template>Role</template>
<script lang="ts" setup>
import { VPageHeader } from "@/components/base/header";
import { VButton } from "@/components/base/button";
import { VCard } from "@/components/base/card";
import { VInput } from "@/components/base/input";
import { VTag } from "@/components/base/tag";
import { VSpace } from "@/components/base/space";
import {
IconAddCircle,
IconArrowDown,
IconSettings,
IconShieldUser,
} from "@/core/icons";
import { roles } from "./roles-mock";
import { useRouter } from "vue-router";
const router = useRouter();
const handleRouteToDetail = (id: number) => {
router.push({ name: "RoleDetail", params: { id } });
};
</script>
<template>
<VPageHeader title="角色">
<template #icon>
<IconShieldUser class="mr-2 self-center" />
</template>
<template #actions>
<VButton type="secondary">
<template #icon>
<IconAddCircle class="h-full w-full" />
</template>
新建角色
</VButton>
</template>
</VPageHeader>
<div class="m-0 md:m-4">
<VCard :body-class="['!p-0']">
<template #header>
<div class="block w-full bg-gray-50 px-4 py-3">
<div
class="relative flex flex-col items-start sm:flex-row sm:items-center"
>
<div class="flex w-full flex-1 sm:w-auto">
<VInput class="w-full sm:w-72" placeholder="输入关键词搜索" />
</div>
<div class="mt-4 flex sm:mt-0">
<VSpace spacing="lg">
<FloatingDropdown>
<div
class="flex cursor-pointer select-none items-center text-sm text-gray-700 hover:text-black"
>
<span class="mr-0.5">状态</span>
<span>
<IconArrowDown />
</span>
</div>
<template #popper>
<div class="w-52 p-4">
<ul class="space-y-1">
<li
class="flex cursor-pointer items-center rounded px-3 py-2 text-sm text-gray-600 hover:bg-gray-100 hover:text-gray-900"
>
<span class="truncate">正常</span>
</li>
<li
class="flex cursor-pointer items-center rounded px-3 py-2 text-sm text-gray-600 hover:bg-gray-100 hover:text-gray-900"
>
<span class="truncate">已禁用</span>
</li>
</ul>
</div>
</template>
</FloatingDropdown>
<FloatingDropdown>
<div
class="flex cursor-pointer select-none items-center text-sm text-gray-700 hover:text-black"
>
<span class="mr-0.5">类型</span>
<span>
<IconArrowDown />
</span>
</div>
<template #popper>
<div class="w-52 p-4">
<ul class="space-y-1">
<li
class="flex cursor-pointer items-center rounded px-3 py-2 text-sm text-gray-600 hover:bg-gray-100 hover:text-gray-900"
>
<span class="truncate">系统保留</span>
</li>
<li
class="flex cursor-pointer items-center rounded px-3 py-2 text-sm text-gray-600 hover:bg-gray-100 hover:text-gray-900"
>
<span class="truncate">自定义</span>
</li>
</ul>
</div>
</template>
</FloatingDropdown>
<FloatingDropdown>
<div
class="flex cursor-pointer select-none items-center text-sm text-gray-700 hover:text-black"
>
<span class="mr-0.5">排序</span>
<span>
<IconArrowDown />
</span>
</div>
<template #popper>
<div class="w-72 p-4">
<ul class="space-y-1">
<li
class="flex cursor-pointer items-center rounded px-3 py-2 text-sm text-gray-600 hover:bg-gray-100 hover:text-gray-900"
>
<span class="truncate">更高权限</span>
</li>
<li
class="flex cursor-pointer items-center rounded px-3 py-2 text-sm text-gray-600 hover:bg-gray-100 hover:text-gray-900"
>
<span class="truncate">更低权限</span>
</li>
</ul>
</div>
</template>
</FloatingDropdown>
</VSpace>
</div>
</div>
</div>
</template>
<ul class="box-border h-full w-full divide-y divide-gray-100" role="list">
<li
v-for="(role, index) in roles"
:key="index"
@click="handleRouteToDetail(role.id)"
>
<div
class="relative block cursor-pointer px-4 py-3 transition-all hover:bg-gray-50"
>
<div class="relative flex flex-row items-center">
<div class="flex-1">
<div class="flex flex-row items-center">
<span class="mr-2 truncate text-sm font-medium text-gray-900">
{{ role.name }}
</span>
</div>
<div class="mt-2 flex">
<span class="text-xs text-gray-500">
包含 {{ role.permissions }} 个权限
</span>
</div>
</div>
<div class="flex">
<div
class="inline-flex flex-col flex-col-reverse items-end gap-4 sm:flex-row sm:items-center sm:gap-6"
>
<a
class="hidden text-sm text-gray-500 hover:text-gray-900 sm:block"
target="_blank"
>
{{ role.users }} 个用户
</a>
<VTag> 系统保留</VTag>
<time class="text-sm text-gray-500" datetime="2020-01-07">
2020-01-07
</time>
<span class="cursor-pointer">
<IconSettings />
</span>
</div>
</div>
</div>
</div>
</li>
</ul>
<template #footer>
<div class="flex items-center justify-end bg-white">
<div class="flex flex-1 items-center justify-end">
<div>
<nav
aria-label="Pagination"
class="relative z-0 inline-flex -space-x-px rounded-md shadow-sm"
>
<a
class="relative inline-flex items-center rounded-l-md border border-gray-300 bg-white px-2 py-2 text-sm font-medium text-gray-500 hover:bg-gray-50"
href="#"
>
<span class="sr-only">Previous</span>
<svg
aria-hidden="true"
class="h-5 w-5"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
clip-rule="evenodd"
d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z"
fill-rule="evenodd"
/>
</svg>
</a>
<a
aria-current="page"
class="relative z-10 inline-flex items-center border border-indigo-500 bg-indigo-50 px-4 py-2 text-sm font-medium text-indigo-600"
href="#"
>
1
</a>
<a
class="relative inline-flex items-center border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-500 hover:bg-gray-50"
href="#"
>
2
</a>
<span
class="relative inline-flex items-center border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700"
>
...
</span>
<a
class="relative hidden items-center border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-500 hover:bg-gray-50 md:inline-flex"
href="#"
>
4
</a>
<a
class="relative inline-flex items-center rounded-r-md border border-gray-300 bg-white px-2 py-2 text-sm font-medium text-gray-500 hover:bg-gray-50"
href="#"
>
<span class="sr-only">Next</span>
<svg
aria-hidden="true"
class="h-5 w-5"
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
clip-rule="evenodd"
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
fill-rule="evenodd"
/>
</svg>
</a>
</nav>
</div>
</div>
</div>
</template>
</VCard>
</div>
</template>

View File

@ -0,0 +1,26 @@
export const roles = [
{
id: 1,
name: "Super Administrator",
permissions: 100,
users: 10,
},
{
id: 2,
name: "Administrator",
permissions: 32,
users: 5,
},
{
id: 3,
name: "Editor",
permissions: 10,
users: 3,
},
{
id: 4,
name: "Guest",
permissions: 5,
users: 10232,
},
];

View File

@ -2,8 +2,11 @@
import { VTag } from "@/components/base/tag";
import { IconUserSettings } from "@/core/icons";
import { inject } from "vue";
import { useRouter } from "vue-router";
const user = inject("user");
const router = useRouter();
</script>
<template>
<div class="border-t border-gray-200">
@ -37,7 +40,7 @@ const user = inject("user");
>
<dt class="text-sm font-medium text-gray-900">角色</dt>
<dd class="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
<VTag>
<VTag @click="router.push({ name: 'RoleDetail', params: { id: 1 } })">
<template #leftIcon>
<IconUserSettings class="h-full w-full" />
</template>

View File

@ -100,14 +100,42 @@ const handleRouteToDetail = (username: string) => {
</div>
</template>
</FloatingDropdown>
<div
class="flex cursor-pointer items-center text-sm text-gray-700 hover:text-black"
>
<span class="mr-0.5">角色</span>
<span>
<IconArrowDown />
</span>
</div>
<FloatingDropdown>
<div
class="flex cursor-pointer select-none items-center text-sm text-gray-700 hover:text-black"
>
<span class="mr-0.5">角色</span>
<span>
<IconArrowDown />
</span>
</div>
<template #popper>
<div class="w-52 p-4">
<ul class="space-y-1">
<li
class="flex cursor-pointer items-center rounded px-3 py-2 text-sm text-gray-600 hover:bg-gray-100 hover:text-gray-900"
>
<span class="truncate">Super Administrator</span>
</li>
<li
class="flex cursor-pointer items-center rounded px-3 py-2 text-sm text-gray-600 hover:bg-gray-100 hover:text-gray-900"
>
<span class="truncate">Administrator</span>
</li>
<li
class="flex cursor-pointer items-center rounded px-3 py-2 text-sm text-gray-600 hover:bg-gray-100 hover:text-gray-900"
>
<span class="truncate">Editor</span>
</li>
<li
class="flex cursor-pointer items-center rounded px-3 py-2 text-sm text-gray-600 hover:bg-gray-100 hover:text-gray-900"
>
<span class="truncate">Guest</span>
</li>
</ul>
</div>
</template>
</FloatingDropdown>
<FloatingDropdown>
<div
class="flex cursor-pointer select-none items-center text-sm text-gray-700 hover:text-black"