优化构架
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { createMemoryHistory, createRouter } from 'vue-router'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import ForbiddenView from './ForbiddenView.vue'
|
||||
|
||||
describe('ForbiddenView', () => {
|
||||
it('shows server-account guidance and sanitizes the source path', async () => {
|
||||
const pinia = createPinia()
|
||||
setActivePinia(pinia)
|
||||
const router = createRouter({ history: createMemoryHistory(), routes: [{ path: '/403', component: ForbiddenView }] })
|
||||
await router.push({ path: '/403', query: { from: '//example.com' } })
|
||||
await router.isReady()
|
||||
const wrapper = mount(ForbiddenView, { global: { plugins: [pinia, router] } })
|
||||
expect(wrapper.text()).toContain('当前登录账号无法访问此页面')
|
||||
expect(wrapper.text()).toContain('来源页面:/')
|
||||
expect(wrapper.text()).not.toContain('权限预览')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,28 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { ShieldX } from '@lucide/vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { sanitizeInternalPath } from '@/core/routing/accessDecision'
|
||||
import { useAccessPreviewStore } from '@/core/access/accessPreview'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const access = useAccessPreviewStore()
|
||||
const sourcePath = computed(() => sanitizeInternalPath(route.query.from))
|
||||
|
||||
async function returnToAccessiblePage() {
|
||||
if (access.firstAccessiblePath) await router.replace(access.firstAccessiblePath)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="forbidden-page" aria-labelledby="forbidden-title">
|
||||
<div class="forbidden-icon" aria-hidden="true"><ShieldX :size="32" /></div>
|
||||
<p class="section-kicker">403 · 权限不足</p>
|
||||
<h2 id="forbidden-title">当前登录账号无法访问此页面</h2>
|
||||
<p>请联系平台管理员核对账号角色和权限配置。</p>
|
||||
<p class="forbidden-source">来源页面:{{ sourcePath }}</p>
|
||||
<el-button v-if="access.firstAccessiblePath" type="primary" @click="returnToAccessiblePage">返回可访问页面</el-button>
|
||||
<p v-else>当前账号暂无可访问的业务菜单。</p>
|
||||
</section>
|
||||
</template>
|
||||
@@ -0,0 +1,33 @@
|
||||
<script setup lang="ts">
|
||||
import { Cable } from '@lucide/vue'
|
||||
|
||||
defineProps<{
|
||||
moduleName: string
|
||||
description: string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="surface-panel unavailable-panel" role="status" aria-live="polite">
|
||||
<Cable :size="30" aria-hidden="true" />
|
||||
<p class="section-kicker">接口尚未开放</p>
|
||||
<h2>{{ moduleName }}暂不可用</h2>
|
||||
<p>{{ description }}</p>
|
||||
<p class="secondary-text">为避免产生误导,本页面不会展示演示数据,也不会模拟新增、编辑或导入成功。</p>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.unavailable-panel {
|
||||
display: grid;
|
||||
justify-items: start;
|
||||
gap: 10px;
|
||||
max-width: 720px;
|
||||
padding: 32px;
|
||||
}
|
||||
|
||||
.unavailable-panel h2,
|
||||
.unavailable-panel p {
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,29 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { Construction, Plus, Search } from '@lucide/vue'
|
||||
|
||||
const route = useRoute()
|
||||
const title = computed(() => route.meta.title || '功能模块')
|
||||
const description = computed(() => route.meta.description || '')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="module-page">
|
||||
<div class="module-toolbar">
|
||||
<el-input placeholder="输入关键词搜索" clearable>
|
||||
<template #prefix><Search :size="17" /></template>
|
||||
</el-input>
|
||||
<el-button type="primary"><Plus :size="17" /> 新增</el-button>
|
||||
</div>
|
||||
|
||||
<div class="module-card">
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon"><Construction :size="28" /></div>
|
||||
<h2>{{ title }}</h2>
|
||||
<p>{{ description }}</p>
|
||||
<el-tag effect="plain">页面骨架已完成,业务接口待接入</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
Reference in New Issue
Block a user