Repository navigation
fix: home perm #7822
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: home perm #7822
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,21 +1,25 @@ | ||
| <script setup lang="ts"> | ||
| import { computed, onMounted, ref } from 'vue' | ||
| import { useRouter } from 'vue-router' | ||
| import { useRoute, useRouter } from 'vue-router' | ||
| import type HomepageApi from '@/api/admin/workspace/homepage' | ||
| import type { HomeApplicationAggregation, HomeKnowledgeAggregation, HomeToolAggregation, HomeModelAggregation } from '@/api/types' | ||
| import { toThousands } from '@/utils/number' | ||
| import perm from '@/permission' | ||
| interface HomeResourceCard { | ||
| label: string | ||
| route: string | ||
| icon: string | ||
| color: string | ||
| disabled?: boolean | ||
| total?: number | ||
| details: [string, number | undefined][] | ||
| } | ||
|
|
||
| /* 资源概况与列表导航 */ | ||
| const props = defineProps<{ workspaceId: string; api: typeof HomepageApi }>() | ||
| const router = useRouter() | ||
| const route = useRoute() | ||
| const isSystemScope = computed(() => route.meta.scope === 'system') | ||
| const loading = ref(false) | ||
| const application = ref<HomeApplicationAggregation>() | ||
| const knowledge = ref<HomeKnowledgeAggregation>() | ||
|
|
@@ -24,6 +28,7 @@ const model = ref<HomeModelAggregation>() | |
| const resourceGroups = computed<HomeResourceCard[]>(() => [ | ||
| { | ||
| label: '智能体', | ||
| disabled: isSystemScope.value || !perm.application.workspace.isShare(), | ||
| route: 'workspace-application-list', | ||
| icon: 'icon_robot_filled', | ||
| color: '#3370FF', | ||
|
|
@@ -35,6 +40,7 @@ const resourceGroups = computed<HomeResourceCard[]>(() => [ | |
| }, | ||
| { | ||
| label: '知识库', | ||
| disabled: isSystemScope.value || !perm.knowledge.workspace.isShare(), | ||
| route: 'workspace-knowledge-list', | ||
| icon: 'icon_book_filled', | ||
| color: '#7F3BF5', | ||
|
|
@@ -46,6 +52,7 @@ const resourceGroups = computed<HomeResourceCard[]>(() => [ | |
| }, | ||
| { | ||
| label: '工具', | ||
| disabled: isSystemScope.value || !perm.tool.workspace.isShare(), | ||
| route: 'workspace-tools', | ||
| icon: 'icon_busy_filled', | ||
| color: '#2CA91F', | ||
|
|
@@ -58,6 +65,7 @@ const resourceGroups = computed<HomeResourceCard[]>(() => [ | |
| }, | ||
| { | ||
| label: '模型', | ||
| disabled: isSystemScope.value || !perm.model.workspace.isShare(), | ||
| route: 'workspace-model', | ||
| icon: 'icon_dataset_filled', | ||
| color: '#FF8800', | ||
|
|
@@ -109,8 +117,9 @@ function loadResources() { | |
| loading.value = false | ||
| }) | ||
| } | ||
| function handleNavigate(name: string) { | ||
| router.push({ name, params: { workspaceId: props.workspaceId } }) | ||
| function handleNavigate(card: HomeResourceCard) { | ||
| if (card.disabled) return | ||
| router.push({ name: card.route, params: { workspaceId: props.workspaceId } }) | ||
| } | ||
| onMounted(loadResources) | ||
| defineExpose({ refresh: loadResources }) | ||
|
|
@@ -119,11 +128,12 @@ defineExpose({ refresh: loadResources }) | |
| <section> | ||
| <div v-loading="loading" class="grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4"> | ||
| <template v-for="card in resourceGroups" :key="card.route"> | ||
| <!-- 进入未禁用资源的列表 --> | ||
| <el-card | ||
| shadow="hover" | ||
| class="cursor-pointer hover:border-current!" | ||
| :shadow="card.disabled ? 'never' : 'hover'" | ||
| :class="{ 'cursor-pointer hover:border-current!': !card.disabled }" | ||
| :style="{ color: card.color, backgroundImage: `linear-gradient(to bottom, ${card.color}0A 0%, #FFFFFF 40%, #FFFFFF 100%)` }" | ||
| @click="handleNavigate(card.route)" | ||
| @click="handleNavigate(card)" | ||
| > | ||
| <div class="flex-between text-N900"> | ||
| <div> | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here's my review: Potential Issues
Minor / Optimization
SummaryThe diff is functionally sound. Main improvements: add visual/keyboard feedback for disabled cards, centralize the permission predicate, make |
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -36,7 +36,6 @@ onMounted(() => { | |
| <template> | ||
| <MkViewLayout title=""> | ||
| <!-- 快捷创建 --> | ||
| <h4 class="mb-4 mt-4">快捷创建</h4> | ||
| <HomeQuickCreate :workspace-id="workspaceId" /> | ||
| <!-- 资源 --> | ||
| <h4 class="mb-4 mt-4">资源</h4> | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here's my review of the diff: SummaryThe change simply removes a Potential Issues
Optimization Suggestions
VerdictThe diff is safe but should be verified against:
No functional bugs are introduced by the change itself. |
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here are the issues and suggestions for the diff:
1. Duplicated permission checks (maintenance risk)
The same permission expressions (
perm.application.workspace.create(), etc.) are repeated in bothshowQuickCreateand eachv-if. If you later change a permission, you must update it in two places. Consider deriving a small object or using the computed flags:Then use
v-if="can.application"etc.2. Potential reactivity issue
perm.application.workspace.create()looks like an imperative call, not necessarily reactive. Calling it insidecomputedonly re-evaluates when its reactive dependencies change. Verify thatperm.*.create()reads reactive state internally; otherwise the UI won't update when permissions change (e.g., after login or role switch).3. Possible early-render flicker
If permissions are loaded asynchronously (e.g., fetched after mount), the section will briefly render empty/hidden before permissions resolve. Ensure permission state is ready before rendering, or add a loading guard.
4. Inconsistent
fit-trigger-widthcasing (pre-existing, but worth noting)ButtonCreateApplicationusesfit-trigger-width(kebab-case attr), while the others usepopper-class/popperClassinconsistently across components. Not introduced by this diff, but the reformatting exposes it — verify each component's actual prop names to avoid silently ignored props.5. Import ordering / lint
import perm from '@/permission'is placed after other imports but grouped loosely. Depending on your ESLint config (import/order), you may want to keep third-party and@/imports ordered.6. Heading semantics
<h4 class="mb-4 mt-4">快捷创建</h4>— hard-coded Chinese inline, consistent with existing code but consider i18n if the project supports multiple languages (other strings appear to be through components). Minor.7. Skipping
onMountedfetch when no permissionsonMountedstill runs fetching providers/model data regardless ofshowQuickCreate. If the section is hidden entirely, you could skip those API calls whenperm.model.workspace.create()is false, to reduce unnecessary requests.8. Empty grid layout
When
showQuickCreateis true but only some cards show, thexl:grid-cols-4with 1–2 items is fine, butsm:grid-cols-2handles it. No action needed, just confirm spacing looks right with fewer items.9. Prop naming consistency
defaultProvider,providers,ModelApiusage unchanged — fine.Overall the logic is correct; the main improvements are DRY-ing the permission checks (#1) and verifying reactive permission state (#2).