恢复修改的数据
This commit is contained in:
@@ -1,70 +1,185 @@
|
||||
<script setup lang="ts">
|
||||
// 快捷入口为固定模块链接(纯 UI,静态数据)
|
||||
const entries = [
|
||||
{ label: '财务台账', path: '/finance/bills', icon: '💰' },
|
||||
{ label: '合同台账', path: '/contract', icon: '📄' },
|
||||
{ label: '客服工作台', path: '/service', icon: '💬' },
|
||||
{ label: '案例库管理', path: '/cases', icon: '📁' },
|
||||
{ label: '工单需求池', path: '/demand', icon: '📋' },
|
||||
import { computed, ref } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useHomeStore } from '@/stores/modules/home'
|
||||
import type { PortalEntryCard } from '@/types/business/home'
|
||||
import { toPortalEntryCards } from '@/utils/entryIcon'
|
||||
import HomeAppDialog from './HomeAppDialog.vue'
|
||||
|
||||
/**
|
||||
* 首页「快捷入口」卡片。
|
||||
*
|
||||
* 数据来自 store.quickEntries(← /vermins/portalEntryUser/list 的 groupType=1 项),
|
||||
* 接口不可用(游客 / 无权限)时回退到内置默认入口,保证卡片不空。
|
||||
* 「更多应用」打开 HomeAppDialog,可在快捷 / 其他两组之间调整入口。
|
||||
*/
|
||||
const homeStore = useHomeStore()
|
||||
const { quickEntries } = storeToRefs(homeStore)
|
||||
const router = useRouter()
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
|
||||
/** 接口不可用时的默认入口(与改造前的静态入口保持一致) */
|
||||
const FALLBACK_QUICK: PortalEntryCard[] = [
|
||||
{ id: 0, label: '财务台账', route: '/finance/bills', groupType: '1', icon: '💰', isImage: false, tone: 'amber' },
|
||||
{ id: 0, label: '合同台账', route: '/contract', groupType: '1', icon: '📄', isImage: false, tone: 'blue' },
|
||||
{ id: 0, label: '客服工作台', route: '/service', groupType: '1', icon: '💬', isImage: false, tone: 'green' },
|
||||
{ id: 0, label: '案例库管理', route: '/cases', groupType: '1', icon: '📁', isImage: false, tone: 'purple' },
|
||||
{ id: 0, label: '工单需求池', route: '/demand', groupType: '1', icon: '📋', isImage: false, tone: 'cyan' },
|
||||
]
|
||||
|
||||
/** 前端已注册的静态路由(route 无效时不跳转,避免出现无匹配路由) */
|
||||
const knownPaths = new Set(router.getRoutes().map((item) => item.path))
|
||||
|
||||
/** 卡片展示的入口(最多 5 个) */
|
||||
const entries = computed<PortalEntryCard[]>(() => {
|
||||
const list = toPortalEntryCards(quickEntries.value)
|
||||
return (list.length > 0 ? list : FALLBACK_QUICK).slice(0, 5)
|
||||
})
|
||||
|
||||
/** 解析可跳转路径:优先后端 route,其次同名默认入口,最后回首页 */
|
||||
function resolvePath(card: PortalEntryCard): string {
|
||||
if (card.route && knownPaths.has(card.route)) return card.route
|
||||
const fallback = FALLBACK_QUICK.find((item) => item.label === card.label)
|
||||
if (fallback && knownPaths.has(fallback.route)) return fallback.route
|
||||
return '/'
|
||||
}
|
||||
|
||||
function onMore(): void {
|
||||
// 打开时同步一次,确保分组是后端最新结果
|
||||
void homeStore.fetchPortalEntries()
|
||||
dialogVisible.value = true
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="home-quick">
|
||||
<h3 class="home-quick__title">
|
||||
快捷入口
|
||||
</h3>
|
||||
<div class="home-quick__head">
|
||||
<span class="home-quick__head-icon">⊞</span>
|
||||
<h3 class="home-quick__title">
|
||||
快捷入口
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div class="home-quick__list">
|
||||
<RouterLink
|
||||
v-for="entry in entries"
|
||||
:key="entry.label"
|
||||
:to="entry.path"
|
||||
:key="`${entry.label}-${entry.id}`"
|
||||
:to="resolvePath(entry)"
|
||||
class="home-quick__item"
|
||||
>
|
||||
<span class="home-quick__icon">{{ entry.icon }}</span>
|
||||
<span class="home-quick__icon" :class="`home-quick__icon--${entry.tone}`">
|
||||
<img v-if="entry.isImage" :src="entry.icon" :alt="entry.label" class="home-quick__img">
|
||||
<template v-else>{{ entry.icon }}</template>
|
||||
</span>
|
||||
<span class="home-quick__label">{{ entry.label }}</span>
|
||||
</RouterLink>
|
||||
</div>
|
||||
|
||||
<button class="home-quick__more link" type="button" @click="onMore">
|
||||
更多应用 >
|
||||
</button>
|
||||
|
||||
<HomeAppDialog v-model:visible="dialogVisible" />
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.home-quick {
|
||||
.flex(column, flex-start, stretch);
|
||||
flex-shrink: 0;
|
||||
width: 320px;
|
||||
gap: @spacing-base;
|
||||
padding: @spacing-lg;
|
||||
border-radius: @radius-lg;
|
||||
background: @white;
|
||||
box-shadow: @shadow-base;
|
||||
|
||||
&__head {
|
||||
.flex(row, flex-start);
|
||||
gap: @spacing-sm;
|
||||
}
|
||||
|
||||
&__head-icon {
|
||||
color: @heading-color;
|
||||
font-size: @font-size-lg;
|
||||
}
|
||||
|
||||
&__title {
|
||||
margin-bottom: @spacing-base;
|
||||
font-size: @font-size-lg;
|
||||
font-weight: 600;
|
||||
color: @heading-color;
|
||||
}
|
||||
|
||||
&__list {
|
||||
.flex(row, space-between);
|
||||
gap: @spacing-base;
|
||||
.flex(row, space-between, flex-start);
|
||||
gap: @spacing-sm;
|
||||
}
|
||||
|
||||
&__item {
|
||||
.flex(column, center);
|
||||
.flex(column, flex-start, center);
|
||||
gap: @spacing-sm;
|
||||
flex: 1;
|
||||
padding: @spacing-base;
|
||||
border-radius: @radius-lg;
|
||||
background: @white;
|
||||
box-shadow: @shadow-base;
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
transition: transform 0.2s;
|
||||
|
||||
&:hover {
|
||||
box-shadow: @shadow-lg;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
}
|
||||
|
||||
&__icon {
|
||||
font-size: 24px;
|
||||
.flex(row, center);
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: @radius-base;
|
||||
font-size: @font-size-xl;
|
||||
|
||||
&--amber {
|
||||
background: #fff3e0;
|
||||
}
|
||||
|
||||
&--blue {
|
||||
background: @primary-light;
|
||||
}
|
||||
|
||||
&--green {
|
||||
background: #e7f8ec;
|
||||
}
|
||||
|
||||
&--purple {
|
||||
background: #f0eaff;
|
||||
}
|
||||
|
||||
&--red {
|
||||
background: #ffeceb;
|
||||
}
|
||||
|
||||
&--cyan {
|
||||
background: #e4f6fa;
|
||||
}
|
||||
}
|
||||
|
||||
&__img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
&__label {
|
||||
font-size: @font-size-base;
|
||||
font-size: @font-size-sm;
|
||||
color: @text-color;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__more {
|
||||
align-self: flex-end;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
font-size: @font-size-sm;
|
||||
color: @text-color-secondary;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user