feat: add permission settings to dashboard widgets (#4112)

#### What type of PR is this?

/kind improvement
/area console
/milestone 2.7.x

#### What this PR does / why we need it:

为仪表板内置的小组件添加权限设置,解决部分没有权限的用户访问仪表盘提示异常的问题。

#### Which issue(s) this PR fixes:

Fixes #4111 

#### Special notes for your reviewer:

测试方式:

1. 创建一个访客角色的用户。
2. 访问仪表盘,观察是否有异常提示,以及是否出现没有权限的小组件。

#### Does this PR introduce a user-facing change?

```release-note
Console 端仪表盘的内置小组件添加权限设置。
```
pull/4114/head^2
Ryan Wang 2023-06-26 12:12:16 +08:00 committed by GitHub
parent 96225e4040
commit ac47942a04
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 108 additions and 48 deletions

View File

@ -38,23 +38,24 @@
:use-css-transforms="true" :use-css-transforms="true"
:vertical-compact="true" :vertical-compact="true"
> >
<grid-item <template v-for="(item, index) in layout" :key="index">
v-for="(item, index) in layout" <grid-item
:key="index" v-if="currentUserHasPermission(item.permissions)"
:h="item.h" :h="item.h"
:i="item.i" :i="item.i"
:w="item.w" :w="item.w"
:x="item.x" :x="item.x"
:y="item.y" :y="item.y"
> >
<component :is="item.widget" /> <component :is="item.widget" />
<div v-if="settings" class="absolute right-2 top-2"> <div v-if="settings" class="absolute right-2 top-2">
<IconCloseCircle <IconCloseCircle
class="cursor-pointer text-lg text-gray-500 hover:text-gray-900" class="cursor-pointer text-lg text-gray-500 hover:text-gray-900"
@click="handleRemove(item)" @click="handleRemove(item)"
/> />
</div> </div>
</grid-item> </grid-item>
</template>
</grid-layout> </grid-layout>
</div> </div>
@ -88,19 +89,20 @@
:use-css-transforms="true" :use-css-transforms="true"
:vertical-compact="true" :vertical-compact="true"
> >
<grid-item <template v-for="(item, index) in group.widgets" :key="index">
v-for="(item, index) in group.widgets" <grid-item
:key="index" v-if="currentUserHasPermission(item.permissions)"
:h="item.h" :h="item.h"
:i="item.i" :i="item.i"
:w="item.w" :w="item.w"
:x="item.x" :x="item.x"
:y="item.y" :y="item.y"
class="cursor-pointer" class="cursor-pointer"
@click="handleAddWidget(item)" @click="handleAddWidget(item)"
> >
<component :is="item.widget" /> <component :is="item.widget" />
</grid-item> </grid-item>
</template>
</grid-layout> </grid-layout>
</template> </template>
</div> </div>
@ -125,23 +127,48 @@ import cloneDeep from "lodash.clonedeep";
import { apiClient } from "@/utils/api-client"; import { apiClient } from "@/utils/api-client";
import type { DashboardStats } from "@halo-dev/api-client"; import type { DashboardStats } from "@halo-dev/api-client";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { usePermission } from "@/utils/permission";
const { t } = useI18n(); const { t } = useI18n();
const { currentUserHasPermission } = usePermission();
const widgetsGroup = [ const widgetsGroup = [
{ {
id: "post", id: "post",
label: t("core.dashboard.widgets.groups.post"), label: t("core.dashboard.widgets.groups.post"),
widgets: [ widgets: [
{ x: 0, y: 0, w: 3, h: 3, i: 0, widget: "PostStatsWidget" }, {
{ x: 0, y: 0, w: 6, h: 10, i: 1, widget: "RecentPublishedWidget" }, x: 0,
y: 0,
w: 3,
h: 3,
i: 0,
widget: "PostStatsWidget",
},
{
x: 0,
y: 0,
w: 6,
h: 10,
i: 1,
widget: "RecentPublishedWidget",
permissions: ["system:posts:view"],
},
], ],
}, },
{ {
id: "page", id: "page",
label: t("core.dashboard.widgets.groups.page"), label: t("core.dashboard.widgets.groups.page"),
widgets: [ widgets: [
{ x: 0, y: 0, w: 3, h: 3, i: 0, widget: "SinglePageStatsWidget" }, {
x: 0,
y: 0,
w: 3,
h: 3,
i: 0,
widget: "SinglePageStatsWidget",
permissions: ["system:singlepages:view"],
},
], ],
}, },
{ {
@ -152,10 +179,7 @@ const widgetsGroup = [
{ {
id: "user", id: "user",
label: t("core.dashboard.widgets.groups.user"), label: t("core.dashboard.widgets.groups.user"),
widgets: [ widgets: [{ x: 0, y: 0, w: 3, h: 3, i: 0, widget: "UserStatsWidget" }],
{ x: 0, y: 0, w: 3, h: 3, i: 0, widget: "UserStatsWidget" },
{ x: 0, y: 0, w: 3, h: 3, i: 1, widget: "UserProfileWidget" },
],
}, },
{ {
id: "other", id: "other",
@ -172,18 +196,54 @@ const widgetsModal = ref(false);
const activeId = ref(widgetsGroup[0].id); const activeId = ref(widgetsGroup[0].id);
const layout = useStorage("widgets", [ const layout = useStorage("widgets", [
{ x: 0, y: 0, w: 3, h: 3, i: 0, widget: "PostStatsWidget" },
{ x: 3, y: 0, w: 3, h: 3, i: 1, widget: "UserStatsWidget" },
{ x: 6, y: 0, w: 3, h: 3, i: 2, widget: "CommentStatsWidget" },
{ x: 9, y: 0, w: 3, h: 3, i: 3, widget: "ViewsStatsWidget" },
{ x: 0, y: 3, w: 4, h: 10, i: 4, widget: "QuickLinkWidget" },
{ {
x: 4, x: 0,
y: 0,
w: 3,
h: 3,
i: 0,
widget: "PostStatsWidget",
},
{
x: 3,
y: 0,
w: 3,
h: 3,
i: 1,
widget: "UserStatsWidget",
},
{
x: 6,
y: 0,
w: 3,
h: 3,
i: 2,
widget: "CommentStatsWidget",
},
{
x: 9,
y: 0,
w: 3,
h: 3,
i: 3,
widget: "ViewsStatsWidget",
},
{
x: 0,
y: 3, y: 3,
w: 4, w: 6,
h: 10, h: 12,
i: 4,
widget: "QuickLinkWidget",
},
{
x: 6,
y: 3,
w: 6,
h: 12,
i: 5, i: 5,
widget: "RecentPublishedWidget", widget: "RecentPublishedWidget",
permissions: ["system:posts:view"],
}, },
]); ]);

View File

@ -36,7 +36,7 @@ export function hasPermission(
} }
interface usePermissionReturn { interface usePermissionReturn {
currentUserHasPermission: (targetPermissions: Array<string>) => boolean; currentUserHasPermission: (targetPermissions?: Array<string>) => boolean;
} }
/** /**
@ -49,9 +49,9 @@ export function usePermission(): usePermissionReturn {
const { uiPermissions } = roleStore.permissions; const { uiPermissions } = roleStore.permissions;
const currentUserHasPermission = ( const currentUserHasPermission = (
targetPermissions: Array<string> targetPermissions?: Array<string>
): boolean => { ): boolean => {
return hasPermission(uiPermissions, targetPermissions, true); return hasPermission(uiPermissions, targetPermissions || [], true);
}; };
return { return {