feat: ui permission binding

see https://github.com/halo-dev/halo/pull/2260
pull/3445/head
Ryan Wang 2022-07-19 14:07:28 +08:00
parent 1cded7a581
commit ca4e4bbeae
12 changed files with 61 additions and 21 deletions

View File

@ -144,9 +144,7 @@ async function loadCurrentUser() {
const { data: user } = await apiClient.user.getCurrentUserDetail(); const { data: user } = await apiClient.user.getCurrentUserDetail();
app.provide<User>("currentUser", user); app.provide<User>("currentUser", user);
const { data: currentPermissions } = await apiClient.user.getPermissions( const { data: currentPermissions } = await apiClient.user.getPermissions("-");
user.metadata.name
);
const roleStore = useRoleStore(); const roleStore = useRoleStore();
roleStore.$patch({ roleStore.$patch({
permissions: currentPermissions, permissions: currentPermissions,
@ -174,7 +172,7 @@ async function loadCurrentUser() {
})(); })();
async function initApp() { async function initApp() {
// TODO 实验性特性 // TODO 实验性
const theme = localStorage.getItem("theme"); const theme = localStorage.getItem("theme");
if (theme) { if (theme) {
document.body.classList.add(theme); document.body.classList.add(theme);

View File

@ -213,7 +213,7 @@ onMounted(handleFetchPlugin);
</VTag> </VTag>
</p> </p>
</div> </div>
<div> <div v-permission="['system:plugins:manage']">
<VSwitch <VSwitch
:model-value="isStarted" :model-value="isStarted"
@change="handleChangePluginStatus" @change="handleChangePluginStatus"

View File

@ -74,7 +74,7 @@ onMounted(handleFetchPlugins);
<IconPlug class="mr-2 self-center" /> <IconPlug class="mr-2 self-center" />
</template> </template>
<template #actions> <template #actions>
<VButton type="secondary"> <VButton v-permission="['system:plugins:manage']" type="secondary">
<template #icon> <template #icon>
<IconAddCircle class="h-full w-full" /> <IconAddCircle class="h-full w-full" />
</template> </template>
@ -274,13 +274,19 @@ onMounted(handleFetchPlugins);
<time class="text-sm text-gray-500" datetime="2020-01-07"> <time class="text-sm text-gray-500" datetime="2020-01-07">
{{ plugin.metadata.creationTimestamp }} {{ plugin.metadata.creationTimestamp }}
</time> </time>
<div class="flex items-center"> <div
v-permission="['system:plugins:manage']"
class="flex items-center"
>
<VSwitch <VSwitch
:model-value="isStarted(plugin)" :model-value="isStarted(plugin)"
@click="handleChangeStatus(plugin)" @click="handleChangeStatus(plugin)"
/> />
</div> </div>
<span class="cursor-pointer"> <span
v-permission="['system:plugins:manage']"
class="cursor-pointer"
>
<IconSettings @click.stop="handleRouteToDetail(plugin)" /> <IconSettings @click.stop="handleRouteToDetail(plugin)" />
</span> </span>
</div> </div>

View File

@ -15,11 +15,17 @@ export default definePlugin({
path: "", path: "",
name: "Plugins", name: "Plugins",
component: PluginList, component: PluginList,
meta: {
permissions: ["system:plugins:view"],
},
}, },
{ {
path: ":pluginName", path: ":pluginName",
name: "PluginDetail", name: "PluginDetail",
component: PluginDetail, component: PluginDetail,
meta: {
permissions: ["system:plugins:view"],
},
}, },
], ],
}, },

View File

@ -154,7 +154,7 @@ onMounted(() => {
<IconShieldUser class="mr-2 self-center" /> <IconShieldUser class="mr-2 self-center" />
</template> </template>
<template #actions> <template #actions>
<VButton type="secondary"> <VButton v-permission="['system:roles:manage']" type="secondary">
<template #icon> <template #icon>
<IconGitBranch class="h-full w-full" /> <IconGitBranch class="h-full w-full" />
</template> </template>
@ -345,7 +345,7 @@ onMounted(() => {
</dd> </dd>
</div> </div>
</dl> </dl>
<div class="p-4"> <div v-permission="['system:roles:manage']" class="p-4">
<VButton <VButton
:loading="formState.saving" :loading="formState.saving"
type="secondary" type="secondary"

View File

@ -56,7 +56,11 @@ onMounted(() => {
<IconShieldUser class="mr-2 self-center" /> <IconShieldUser class="mr-2 self-center" />
</template> </template>
<template #actions> <template #actions>
<VButton type="secondary" @click="createVisible = true"> <VButton
v-permission="['system:roles:manage']"
type="secondary"
@click="createVisible = true"
>
<template #icon> <template #icon>
<IconAddCircle class="h-full w-full" /> <IconAddCircle class="h-full w-full" />
</template> </template>
@ -198,7 +202,10 @@ onMounted(() => {
<time class="text-sm text-gray-500" datetime="2020-01-07"> <time class="text-sm text-gray-500" datetime="2020-01-07">
2020-01-07 2020-01-07
</time> </time>
<span class="cursor-pointer"> <span
v-permission="['system:roles:manage']"
class="cursor-pointer"
>
<IconSettings /> <IconSettings />
</span> </span>
</div> </div>

View File

@ -14,11 +14,17 @@ export default definePlugin({
path: "roles", path: "roles",
name: "Roles", name: "Roles",
component: RoleList, component: RoleList,
meta: {
permissions: ["system:roles:view"],
},
}, },
{ {
path: "roles/:name", path: "roles/:name",
name: "RoleDetail", name: "RoleDetail",
component: RoleDetail, component: RoleDetail,
meta: {
permissions: ["system:roles:view"],
},
}, },
], ],
}, },

View File

@ -84,12 +84,14 @@ onMounted(() => {
<template> <template>
<UserEditingModal <UserEditingModal
v-model:visible="editingModal" v-model:visible="editingModal"
v-permission="['system:users:manage']"
:user="selectedUser" :user="selectedUser"
@close="handleFetchUsers" @close="handleFetchUsers"
/> />
<UserPasswordChangeModal <UserPasswordChangeModal
v-model:visible="passwordChangeModal" v-model:visible="passwordChangeModal"
v-permission="['system:users:manage']"
:user="selectedUser" :user="selectedUser"
@close="handleFetchUsers" @close="handleFetchUsers"
/> />
@ -100,13 +102,22 @@ onMounted(() => {
</template> </template>
<template #actions> <template #actions>
<VSpace> <VSpace>
<VButton :route="{ name: 'Roles' }" size="sm" type="default"> <VButton
v-permission="['system:roles:view']"
:route="{ name: 'Roles' }"
size="sm"
type="default"
>
<template #icon> <template #icon>
<IconUserFollow class="h-full w-full" /> <IconUserFollow class="h-full w-full" />
</template> </template>
角色管理 角色管理
</VButton> </VButton>
<VButton type="secondary" @click="editingModal = true"> <VButton
v-permission="['system:users:manage']"
type="secondary"
@click="editingModal = true"
>
<template #icon> <template #icon>
<IconAddCircle class="h-full w-full" /> <IconAddCircle class="h-full w-full" />
</template> </template>
@ -126,6 +137,7 @@ onMounted(() => {
<div class="mr-4 hidden items-center sm:flex"> <div class="mr-4 hidden items-center sm:flex">
<input <input
v-model="checkAll" v-model="checkAll"
v-permission="['system:users:manage']"
class="h-4 w-4 rounded border-gray-300 text-indigo-600" class="h-4 w-4 rounded border-gray-300 text-indigo-600"
type="checkbox" type="checkbox"
/> />
@ -252,6 +264,7 @@ onMounted(() => {
<div class="mr-4 hidden items-center sm:flex"> <div class="mr-4 hidden items-center sm:flex">
<input <input
v-model="checkAll" v-model="checkAll"
v-permission="['system:users:manage']"
class="h-4 w-4 rounded border-gray-300 text-indigo-600" class="h-4 w-4 rounded border-gray-300 text-indigo-600"
type="checkbox" type="checkbox"
/> />
@ -304,10 +317,12 @@ onMounted(() => {
<time class="text-sm text-gray-500" datetime="2020-01-07"> <time class="text-sm text-gray-500" datetime="2020-01-07">
{{ user.metadata.creationTimestamp }} {{ user.metadata.creationTimestamp }}
</time> </time>
<span class="cursor-pointer"> <span
v-permission="['system:users:manage']"
class="cursor-pointer"
>
<FloatingDropdown> <FloatingDropdown>
<IconSettings /> <IconSettings />
<template #popper> <template #popper>
<div class="links-w-48 links-p-2"> <div class="links-w-48 links-p-2">
<VSpace class="links-w-full" direction="column"> <VSpace class="links-w-full" direction="column">
@ -319,7 +334,6 @@ onMounted(() => {
修改资料 修改资料
</VButton> </VButton>
<VButton <VButton
v-permission="['system:users:manage']"
block block
@click="handleOpenPasswordChangeModal(user)" @click="handleOpenPasswordChangeModal(user)"
> >

View File

@ -33,6 +33,9 @@ export default definePlugin({
path: "", path: "",
name: "Users", name: "Users",
component: UserList, component: UserList,
meta: {
permissions: ["system:users:view"],
},
}, },
], ],
}, },

View File

@ -11,7 +11,7 @@ export function setupPermissionGuard(router: Router) {
const flag = hasPermission( const flag = hasPermission(
Array.from(uiPermissions), Array.from(uiPermissions),
meta.permissions as string[], meta.permissions as string[],
false true
); );
if (!flag) { if (!flag) {
next({ name: "Forbidden" }); next({ name: "Forbidden" });

View File

@ -6,10 +6,10 @@ describe("hasPermission", () => {
const uiPermissions = ["system:post:manage", "system:post:view"]; const uiPermissions = ["system:post:manage", "system:post:view"];
expect(hasPermission(uiPermissions, ["system:post:manage"], false)).toBe( expect(hasPermission(uiPermissions, ["system:post:manage"], false)).toBe(
false true
); );
expect(hasPermission(uiPermissions, ["system:post:view"], false)).toBe( expect(hasPermission(uiPermissions, ["system:post:view"], false)).toBe(
false true
); );
expect(hasPermission(uiPermissions, ["system:post:view"], true)).toBe(true); expect(hasPermission(uiPermissions, ["system:post:view"], true)).toBe(true);
expect( expect(

View File

@ -22,5 +22,5 @@ export function hasPermission(
return true; return true;
} }
return !!(!any && isEqual(uiPermissions, targetPermissions)); return !!(!any && isEqual(intersection, targetPermissions));
} }