fix: restore dashboard workspace widgets
This commit is contained in:
+76
-23
@@ -1,88 +1,136 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import type { Component } from 'vue';
|
||||||
import type { DashboardWidget } from '#/components/dashboard-design';
|
import type { DashboardWidget } from '#/components/dashboard-design';
|
||||||
|
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ChevronRight,
|
ChevronRight,
|
||||||
ClipboardCheck,
|
ClipboardCheck,
|
||||||
ClipboardList,
|
ClipboardList,
|
||||||
FilePen,
|
FilePen,
|
||||||
|
IconifyIcon,
|
||||||
Play,
|
Play,
|
||||||
Send,
|
Send,
|
||||||
UserCheck,
|
UserCheck,
|
||||||
} from '@vben/icons';
|
} from '@vben/icons';
|
||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
|
import { useAppContextStore } from '#/store/app-context';
|
||||||
|
|
||||||
|
interface ApprovalMenuItem {
|
||||||
|
color: string;
|
||||||
|
icon: Component | string;
|
||||||
|
key: number | string;
|
||||||
|
path: string;
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
widget: DashboardWidget;
|
widget: DashboardWidget;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
// 审批中心菜单项
|
const router = useRouter();
|
||||||
const menuItems = computed(() => [
|
const appContextStore = useAppContextStore();
|
||||||
|
|
||||||
|
const defaultColors = [
|
||||||
|
'rgba(59, 130, 246, 0.85)',
|
||||||
|
'rgba(245, 158, 11, 0.85)',
|
||||||
|
'rgba(139, 92, 246, 0.85)',
|
||||||
|
'rgba(14, 165, 233, 0.85)',
|
||||||
|
'rgba(249, 115, 22, 0.85)',
|
||||||
|
'rgba(20, 184, 166, 0.85)',
|
||||||
|
];
|
||||||
|
|
||||||
|
const defaultMenuItems = computed<ApprovalMenuItem[]>(() => [
|
||||||
{
|
{
|
||||||
key: 'initiated',
|
key: 'initiated',
|
||||||
title: $t('dashboard-design.widgets.approvalCenter.initiated'),
|
title: $t('dashboard-design.widgets.approvalCenter.initiated'),
|
||||||
icon: Send,
|
icon: Send,
|
||||||
color: 'rgba(59, 130, 246, 0.85)',
|
color: defaultColors[0]!,
|
||||||
path: '/workflow/initiated',
|
path: '/workflow/initiated',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'pending',
|
key: 'pending',
|
||||||
title: $t('dashboard-design.widgets.approvalCenter.pending'),
|
title: $t('dashboard-design.widgets.approvalCenter.pending'),
|
||||||
icon: ClipboardList,
|
icon: ClipboardList,
|
||||||
color: 'rgba(245, 158, 11, 0.85)',
|
color: defaultColors[1]!,
|
||||||
path: '/workflow/pending',
|
path: '/workflow/pending',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'handling',
|
key: 'handling',
|
||||||
title: $t('dashboard-design.widgets.approvalCenter.handling'),
|
title: $t('dashboard-design.widgets.approvalCenter.handling'),
|
||||||
icon: FilePen,
|
icon: FilePen,
|
||||||
color: 'rgba(139, 92, 246, 0.85)',
|
color: defaultColors[2]!,
|
||||||
path: '/workflow/pending',
|
path: '/workflow/pending',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'handled',
|
key: 'handled',
|
||||||
title: $t('dashboard-design.widgets.approvalCenter.handled'),
|
title: $t('dashboard-design.widgets.approvalCenter.handled'),
|
||||||
icon: ClipboardCheck,
|
icon: ClipboardCheck,
|
||||||
color: 'rgba(14, 165, 233, 0.85)',
|
color: defaultColors[3]!,
|
||||||
path: '/workflow/handled',
|
path: '/workflow/handled',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'copy',
|
key: 'copy',
|
||||||
title: $t('dashboard-design.widgets.approvalCenter.copy'),
|
title: $t('dashboard-design.widgets.approvalCenter.copy'),
|
||||||
icon: UserCheck,
|
icon: UserCheck,
|
||||||
color: 'rgba(249, 115, 22, 0.85)',
|
color: defaultColors[4]!,
|
||||||
path: '/workflow/copy',
|
path: '/workflow/copy',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'start',
|
key: 'start',
|
||||||
title: $t('dashboard-design.widgets.approvalCenter.start'),
|
title: $t('dashboard-design.widgets.approvalCenter.start'),
|
||||||
icon: Play,
|
icon: Play,
|
||||||
color: 'rgba(20, 184, 166, 0.85)',
|
color: defaultColors[5]!,
|
||||||
path: '/workflow/start',
|
path: '/workflow/start',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// 路由前缀
|
const displayMenuItems = computed<ApprovalMenuItem[]>(() => {
|
||||||
|
const customMenus = props.widget.props.menus || [];
|
||||||
|
if (!customMenus.length) return defaultMenuItems.value;
|
||||||
|
|
||||||
|
return customMenus.map((item: any, index: number) => ({
|
||||||
|
key: item.id || item.key || item.title || index,
|
||||||
|
title: item.title,
|
||||||
|
icon: item.icon || 'lucide:circle',
|
||||||
|
color:
|
||||||
|
item.color || item.bgColor || defaultColors[index % defaultColors.length],
|
||||||
|
path: item.path || '',
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
const routePrefix = computed(
|
const routePrefix = computed(
|
||||||
() => props.widget.props.routePrefix || '/app/workflow_center',
|
() => props.widget.props.routePrefix || '/app/workflow_center',
|
||||||
);
|
);
|
||||||
|
|
||||||
// 点击菜单项
|
function handleClick(item: ApprovalMenuItem) {
|
||||||
const handleClick = (item: { path: string }) => {
|
if (!item.path) return;
|
||||||
window.open(`${routePrefix.value}${item.path}`, '_blank');
|
|
||||||
};
|
|
||||||
|
|
||||||
// 点击更多
|
if (item.path.startsWith('http://') || item.path.startsWith('https://')) {
|
||||||
const handleMore = () => {
|
window.open(item.path, '_blank');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.widget.props.menus?.length) {
|
||||||
|
const code = appContextStore.appCode;
|
||||||
|
const targetPath = code ? `/app/${code}${item.path}` : item.path;
|
||||||
|
router.push(targetPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.open(`${routePrefix.value}${item.path}`, '_blank');
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleMore() {
|
||||||
window.open(`${routePrefix.value}/workflow/pending`, '_blank');
|
window.open(`${routePrefix.value}/workflow/pending`, '_blank');
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="approval-center flex h-full flex-col p-4">
|
<div class="approval-center flex h-full flex-col p-4">
|
||||||
<!-- 头部 -->
|
|
||||||
<div class="mb-4 flex items-center justify-between">
|
<div class="mb-4 flex items-center justify-between">
|
||||||
<span class="text-sm font-medium">{{ widget.props.title }}</span>
|
<span class="text-sm font-medium">{{ widget.props.title }}</span>
|
||||||
<button
|
<button
|
||||||
@@ -95,10 +143,10 @@ const handleMore = () => {
|
|||||||
<ChevronRight class="h-3.5 w-3.5" />
|
<ChevronRight class="h-3.5 w-3.5" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<!-- 菜单网格 -->
|
|
||||||
<div class="flex flex-1 items-center justify-around gap-2">
|
<div class="flex flex-1 items-center justify-around gap-2">
|
||||||
<div
|
<div
|
||||||
v-for="item in menuItems"
|
v-for="item in displayMenuItems"
|
||||||
:key="item.key"
|
:key="item.key"
|
||||||
class="flex cursor-pointer flex-col items-center gap-2 transition-transform hover:scale-105"
|
class="flex cursor-pointer flex-col items-center gap-2 transition-transform hover:scale-105"
|
||||||
@click="handleClick(item)"
|
@click="handleClick(item)"
|
||||||
@@ -107,11 +155,16 @@ const handleMore = () => {
|
|||||||
class="flex h-11 w-11 items-center justify-center rounded-full"
|
class="flex h-11 w-11 items-center justify-center rounded-full"
|
||||||
:style="{ backgroundColor: item.color }"
|
:style="{ backgroundColor: item.color }"
|
||||||
>
|
>
|
||||||
<component :is="item.icon" class="h-5 w-5 text-white" />
|
<IconifyIcon
|
||||||
|
v-if="typeof item.icon === 'string'"
|
||||||
|
:icon="item.icon"
|
||||||
|
class="h-5 w-5 text-white"
|
||||||
|
/>
|
||||||
|
<component :is="item.icon" v-else class="h-5 w-5 text-white" />
|
||||||
</div>
|
</div>
|
||||||
<span class="text-muted-foreground whitespace-nowrap text-xs">{{
|
<span class="text-muted-foreground whitespace-nowrap text-xs">
|
||||||
item.title
|
{{ item.title }}
|
||||||
}}</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { DashboardWidget } from '#/components/dashboard-design';
|
|
||||||
import type { ApplicationListItem } from '#/api/core/application';
|
import type { ApplicationListItem } from '#/api/core/application';
|
||||||
|
import type { DashboardWidget } from '#/components/dashboard-design';
|
||||||
|
|
||||||
import { computed, onMounted, ref } from 'vue';
|
import { computed, onMounted, ref } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
import { AppWindow, ChevronRight, IconifyIcon } from '@vben/icons';
|
import { AppWindow, ChevronRight, IconifyIcon } from '@vben/icons';
|
||||||
import { $t } from '@vben/locales';
|
import { $t } from '@vben/locales';
|
||||||
@@ -10,23 +11,42 @@ import { $t } from '@vben/locales';
|
|||||||
import { ElEmpty, ElScrollbar } from 'element-plus';
|
import { ElEmpty, ElScrollbar } from 'element-plus';
|
||||||
|
|
||||||
import { getApplicationListApi } from '#/api/core/application';
|
import { getApplicationListApi } from '#/api/core/application';
|
||||||
|
import { useAppContextStore } from '#/store/app-context';
|
||||||
|
|
||||||
|
interface StaticAppItem {
|
||||||
|
bgColor?: string;
|
||||||
|
color?: string;
|
||||||
|
icon?: string;
|
||||||
|
id?: string;
|
||||||
|
name?: string;
|
||||||
|
path?: string;
|
||||||
|
title?: string;
|
||||||
|
}
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
widget: DashboardWidget;
|
widget: DashboardWidget;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
// 应用列表
|
const router = useRouter();
|
||||||
|
const appContextStore = useAppContextStore();
|
||||||
const appList = ref<ApplicationListItem[]>([]);
|
const appList = ref<ApplicationListItem[]>([]);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
|
||||||
// 最大显示数量
|
const staticMenus = computed<StaticAppItem[]>(
|
||||||
|
() => props.widget.props.menus || [],
|
||||||
|
);
|
||||||
const maxCount = computed(() => props.widget.props.maxCount || 8);
|
const maxCount = computed(() => props.widget.props.maxCount || 8);
|
||||||
|
|
||||||
// 显示的应用列表
|
const displayApps = computed(() => {
|
||||||
const displayApps = computed(() => appList.value.slice(0, maxCount.value));
|
if (staticMenus.value.length) {
|
||||||
|
return staticMenus.value.slice(0, maxCount.value);
|
||||||
|
}
|
||||||
|
return appList.value.slice(0, maxCount.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function loadApps() {
|
||||||
|
if (staticMenus.value.length) return;
|
||||||
|
|
||||||
// 加载已发布的应用列表
|
|
||||||
const loadApps = async () => {
|
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
const res = await getApplicationListApi({
|
const res = await getApplicationListApi({
|
||||||
@@ -40,17 +60,50 @@ const loadApps = async () => {
|
|||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
// 点击应用
|
function handleClick(item: ApplicationListItem | StaticAppItem) {
|
||||||
const handleClick = (app: ApplicationListItem) => {
|
const staticPath = (item as StaticAppItem).path;
|
||||||
|
if (staticPath) {
|
||||||
|
if (staticPath.startsWith('http://') || staticPath.startsWith('https://')) {
|
||||||
|
window.open(staticPath, '_blank');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const code = appContextStore.appCode;
|
||||||
|
const targetPath = code ? `/app/${code}${staticPath}` : staticPath;
|
||||||
|
router.push(targetPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = item as ApplicationListItem;
|
||||||
window.open(`${window.location.origin}/app/${app.code}`, '_blank');
|
window.open(`${window.location.origin}/app/${app.code}`, '_blank');
|
||||||
};
|
}
|
||||||
|
|
||||||
// 点击更多
|
function handleMore() {
|
||||||
const handleMore = () => {
|
const morePath = props.widget.props.morePath;
|
||||||
|
if (morePath) {
|
||||||
|
router.push(morePath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
window.open(`${window.location.origin}/application`, '_blank');
|
window.open(`${window.location.origin}/application`, '_blank');
|
||||||
};
|
}
|
||||||
|
|
||||||
|
function getItemTitle(item: ApplicationListItem | StaticAppItem) {
|
||||||
|
return (item as StaticAppItem).title || (item as StaticAppItem).name || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getItemIcon(item: ApplicationListItem | StaticAppItem) {
|
||||||
|
return (item as StaticAppItem).icon || (item as ApplicationListItem).icon || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getItemBg(item: ApplicationListItem | StaticAppItem) {
|
||||||
|
return (
|
||||||
|
(item as StaticAppItem).bgColor ||
|
||||||
|
(item as StaticAppItem).color ||
|
||||||
|
'linear-gradient(135deg, var(--el-color-primary-light-3), var(--el-color-primary))'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadApps();
|
loadApps();
|
||||||
@@ -59,7 +112,6 @@ onMounted(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="my-apps flex h-full flex-col p-4" v-loading="loading">
|
<div class="my-apps flex h-full flex-col p-4" v-loading="loading">
|
||||||
<!-- 头部 -->
|
|
||||||
<div class="mb-4 flex items-center justify-between">
|
<div class="mb-4 flex items-center justify-between">
|
||||||
<span class="text-sm font-medium">{{ widget.props.title }}</span>
|
<span class="text-sm font-medium">{{ widget.props.title }}</span>
|
||||||
<button
|
<button
|
||||||
@@ -72,31 +124,30 @@ onMounted(() => {
|
|||||||
<ChevronRight class="h-3.5 w-3.5" />
|
<ChevronRight class="h-3.5 w-3.5" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<!-- 应用网格 -->
|
|
||||||
<ElScrollbar class="flex-1">
|
<ElScrollbar class="flex-1">
|
||||||
|
<div v-if="displayApps.length > 0" class="flex flex-wrap gap-4">
|
||||||
<div
|
<div
|
||||||
v-if="displayApps.length > 0"
|
v-for="item in displayApps"
|
||||||
class="flex flex-wrap gap-4"
|
:key="(item as any).id || getItemTitle(item)"
|
||||||
>
|
class="m-4 flex cursor-pointer flex-col items-center gap-2 transition-transform hover:scale-105"
|
||||||
<div
|
|
||||||
v-for="app in displayApps"
|
|
||||||
:key="app.id"
|
|
||||||
class="flex cursor-pointer flex-col items-center gap-2 transition-transform hover:scale-105 m-4"
|
|
||||||
style="width: 72px"
|
style="width: 72px"
|
||||||
@click="handleClick(app)"
|
@click="handleClick(item)"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="flex h-12 w-12 items-center justify-center rounded-xl"
|
class="flex h-12 w-12 items-center justify-center rounded-xl"
|
||||||
style="background: linear-gradient(135deg, var(--el-color-primary-light-3), var(--el-color-primary))"
|
:style="{ background: getItemBg(item) }"
|
||||||
>
|
>
|
||||||
<IconifyIcon
|
<IconifyIcon
|
||||||
v-if="app.icon"
|
v-if="getItemIcon(item)"
|
||||||
:icon="app.icon"
|
:icon="getItemIcon(item)"
|
||||||
class="h-6 w-6 text-white"
|
class="h-6 w-6 text-white"
|
||||||
/>
|
/>
|
||||||
<AppWindow v-else class="h-6 w-6 text-white" />
|
<AppWindow v-else class="h-6 w-6 text-white" />
|
||||||
</div>
|
</div>
|
||||||
<span class="text-muted-foreground w-full truncate text-center text-xs">{{ app.name }}</span>
|
<span class="text-muted-foreground w-full truncate text-center text-xs">
|
||||||
|
{{ getItemTitle(item) }}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ElEmpty
|
<ElEmpty
|
||||||
|
|||||||
+4
-1
@@ -131,7 +131,10 @@ onUnmounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="server-monitor flex h-full flex-col p-4" v-loading="loading">
|
<div
|
||||||
|
class="server-monitor flex h-full flex-col p-4"
|
||||||
|
v-loading="loading && widget.props.blockingLoading"
|
||||||
|
>
|
||||||
<!-- 头部 -->
|
<!-- 头部 -->
|
||||||
<div class="mb-6 flex items-center justify-between">
|
<div class="mb-6 flex items-center justify-between">
|
||||||
<span class="text-sm font-medium">{{ widget.props.title }}</span>
|
<span class="text-sm font-medium">{{ widget.props.title }}</span>
|
||||||
|
|||||||
@@ -12,43 +12,48 @@ const route = useRoute();
|
|||||||
const DashboardRenderer = defineAsyncComponent(
|
const DashboardRenderer = defineAsyncComponent(
|
||||||
() => import('#/components/dashboard-design/DashboardRenderer.vue'),
|
() => import('#/components/dashboard-design/DashboardRenderer.vue'),
|
||||||
);
|
);
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const pageConfig = ref<string>('');
|
const pageConfig = ref('');
|
||||||
const pageName = ref('');
|
|
||||||
const pageCode = ref('');
|
|
||||||
|
|
||||||
const aiHomeMenus = [
|
const aiHomeMenus = [
|
||||||
{
|
{
|
||||||
|
color: 'rgba(59, 130, 246, 0.85)',
|
||||||
icon: 'lucide:bot',
|
icon: 'lucide:bot',
|
||||||
id: 'ai-agent',
|
id: 'ai-agent',
|
||||||
path: '/ai-platform/agent',
|
path: '/ai-platform/agent',
|
||||||
title: '智能体',
|
title: '智能体',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
color: 'rgba(22, 163, 74, 0.85)',
|
||||||
icon: 'lucide:git-branch',
|
icon: 'lucide:git-branch',
|
||||||
id: 'ai-workflow',
|
id: 'ai-workflow',
|
||||||
path: '/ai-platform/workflow',
|
path: '/ai-platform/workflow',
|
||||||
title: '流程编排',
|
title: '流程编排',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
color: 'rgba(124, 58, 237, 0.85)',
|
||||||
icon: 'lucide:history',
|
icon: 'lucide:history',
|
||||||
id: 'ai-workflow-runs',
|
id: 'ai-workflow-runs',
|
||||||
path: '/ai-platform/workflow-runs',
|
path: '/ai-platform/workflow-runs',
|
||||||
title: '执行历史',
|
title: '执行历史',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
color: 'rgba(249, 115, 22, 0.85)',
|
||||||
icon: 'lucide:message-square-code',
|
icon: 'lucide:message-square-code',
|
||||||
id: 'codex-chat',
|
id: 'codex-chat',
|
||||||
path: '/agent-chat/codex',
|
path: '/agent-chat/codex',
|
||||||
title: 'Codex',
|
title: 'Codex',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
color: 'rgba(15, 118, 110, 0.85)',
|
||||||
icon: 'lucide:database',
|
icon: 'lucide:database',
|
||||||
id: 'knowledge-base',
|
id: 'knowledge-base',
|
||||||
path: '/ai-platform/knowledge-base',
|
path: '/ai-platform/knowledge-base',
|
||||||
title: '知识库',
|
title: '知识库',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
color: 'rgba(71, 85, 105, 0.85)',
|
||||||
icon: 'lucide:settings',
|
icon: 'lucide:settings',
|
||||||
id: 'model-config',
|
id: 'model-config',
|
||||||
path: '/ai-platform/model',
|
path: '/ai-platform/model',
|
||||||
@@ -58,36 +63,42 @@ const aiHomeMenus = [
|
|||||||
|
|
||||||
const adminHomeMenus = [
|
const adminHomeMenus = [
|
||||||
{
|
{
|
||||||
|
bgColor: 'linear-gradient(135deg, #60a5fa, #2563eb)',
|
||||||
icon: 'lucide:users',
|
icon: 'lucide:users',
|
||||||
id: 'system-user',
|
id: 'system-user',
|
||||||
path: '/system/user',
|
path: '/system/user',
|
||||||
title: '用户管理',
|
title: '用户管理',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
bgColor: 'linear-gradient(135deg, #a78bfa, #7c3aed)',
|
||||||
icon: 'lucide:shield-check',
|
icon: 'lucide:shield-check',
|
||||||
id: 'system-role',
|
id: 'system-role',
|
||||||
path: '/system/role',
|
path: '/system/role',
|
||||||
title: '角色权限',
|
title: '角色权限',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
bgColor: 'linear-gradient(135deg, #fb923c, #f97316)',
|
||||||
icon: 'lucide:menu',
|
icon: 'lucide:menu',
|
||||||
id: 'system-menu',
|
id: 'system-menu',
|
||||||
path: '/system/menu',
|
path: '/system/menu',
|
||||||
title: '菜单管理',
|
title: '菜单管理',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
bgColor: 'linear-gradient(135deg, #2dd4bf, #0f766e)',
|
||||||
icon: 'lucide:key-round',
|
icon: 'lucide:key-round',
|
||||||
id: 'system-permission',
|
id: 'system-permission',
|
||||||
path: '/system/new-permission',
|
path: '/system/new-permission',
|
||||||
title: 'API 权限',
|
title: 'API 权限',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
bgColor: 'linear-gradient(135deg, #4ade80, #16a34a)',
|
||||||
icon: 'lucide:megaphone',
|
icon: 'lucide:megaphone',
|
||||||
id: 'announcement',
|
id: 'announcement',
|
||||||
path: '/message/announcement',
|
path: '/message/announcement',
|
||||||
title: '公告管理',
|
title: '公告管理',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
bgColor: 'linear-gradient(135deg, #94a3b8, #475569)',
|
||||||
icon: 'lucide:inbox',
|
icon: 'lucide:inbox',
|
||||||
id: 'message-list',
|
id: 'message-list',
|
||||||
path: '/message/list',
|
path: '/message/list',
|
||||||
@@ -137,6 +148,35 @@ function toQuickLinksWidget(widget: any, title: string, menus: any[]) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toApprovalCenterWidget(widget: any, title: string, menus: any[]) {
|
||||||
|
return {
|
||||||
|
...widget,
|
||||||
|
props: {
|
||||||
|
...widget.props,
|
||||||
|
menus,
|
||||||
|
showMore: false,
|
||||||
|
title,
|
||||||
|
},
|
||||||
|
title,
|
||||||
|
type: 'approval-center',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function toStaticAppsWidget(widget: any, title: string, menus: any[]) {
|
||||||
|
return {
|
||||||
|
...widget,
|
||||||
|
props: {
|
||||||
|
...widget.props,
|
||||||
|
maxCount: menus.length,
|
||||||
|
menus,
|
||||||
|
showMore: false,
|
||||||
|
title,
|
||||||
|
},
|
||||||
|
title,
|
||||||
|
type: 'my-apps',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function getLightMainHomeConfig(config: Record<string, any>) {
|
function getLightMainHomeConfig(config: Record<string, any>) {
|
||||||
const next = JSON.parse(JSON.stringify(config));
|
const next = JSON.parse(JSON.stringify(config));
|
||||||
if (!Array.isArray(next.widgets)) return next;
|
if (!Array.isArray(next.widgets)) return next;
|
||||||
@@ -154,11 +194,11 @@ function getLightMainHomeConfig(config: Record<string, any>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (widget.type === 'approval-center') {
|
if (widget.type === 'approval-center') {
|
||||||
return toQuickLinksWidget(widget, 'AI 协作中心', aiHomeMenus);
|
return toApprovalCenterWidget(widget, 'AI 协作中心', aiHomeMenus);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (widget.type === 'my-apps') {
|
if (widget.type === 'my-apps') {
|
||||||
return toQuickLinksWidget(widget, '基础管理', adminHomeMenus);
|
return toStaticAppsWidget(widget, '基础管理', adminHomeMenus);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (widget.type === 'quick-links') {
|
if (widget.type === 'quick-links') {
|
||||||
@@ -171,19 +211,10 @@ function getLightMainHomeConfig(config: Record<string, any>) {
|
|||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取页面编码
|
|
||||||
function getPageCode(): string {
|
function getPageCode(): string {
|
||||||
// 优先从 query 获取
|
if (route.query.pageCode) return route.query.pageCode as string;
|
||||||
if (route.query.pageCode) {
|
if (route.params.code) return route.params.code as string;
|
||||||
return route.query.pageCode as string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 其次从 params 获取
|
|
||||||
if (route.params.code) {
|
|
||||||
return route.params.code as string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 最后从路径中提取(路径格式:/page-render/xxx)
|
|
||||||
const pathParts = route.path.split('/').filter(Boolean);
|
const pathParts = route.path.split('/').filter(Boolean);
|
||||||
const length = pathParts.length;
|
const length = pathParts.length;
|
||||||
if (length >= 2 && pathParts[length - 2] === 'page-render') {
|
if (length >= 2 && pathParts[length - 2] === 'page-render') {
|
||||||
@@ -193,7 +224,6 @@ function getPageCode(): string {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载页面数据
|
|
||||||
async function loadPageData() {
|
async function loadPageData() {
|
||||||
const code = getPageCode();
|
const code = getPageCode();
|
||||||
if (!code) {
|
if (!code) {
|
||||||
@@ -201,12 +231,9 @@ async function loadPageData() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pageCode.value = code;
|
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const page = await getPageByCodeApi(code);
|
const page = await getPageByCodeApi(code);
|
||||||
pageName.value = page.name;
|
|
||||||
const config =
|
const config =
|
||||||
page.page_config && Object.keys(page.page_config).length > 0
|
page.page_config && Object.keys(page.page_config).length > 0
|
||||||
? code === 'main_home'
|
? code === 'main_home'
|
||||||
@@ -225,7 +252,6 @@ onMounted(() => {
|
|||||||
loadPageData();
|
loadPageData();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 监听路由变化
|
|
||||||
watch(
|
watch(
|
||||||
() => route.fullPath,
|
() => route.fullPath,
|
||||||
() => {
|
() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user