feat: 404 page

This commit is contained in:
wangdan-fit2cloud 2025-07-10 17:56:42 +08:00
parent d94972bc9c
commit 3324d4ab35
13 changed files with 114 additions and 104 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 359 KiB

After

Width:  |  Height:  |  Size: 20 KiB

BIN
ui/src/assets/500.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -92,7 +92,8 @@ export default {
selected: 'Selected',
notFound: {
title: '404',
message: 'Unable to Access APP',
NoService: 'Currently unable to access services',
NoPermission: 'No permission to access',
operate: 'Back to Home',
},
custom: 'Custom',

View File

@ -96,7 +96,8 @@ export default {
selected: '已选',
notFound: {
title: '404',
message: '无法访问应用',
NoService: '暂时无法访问服务',
NoPermission:'没有权限访问',
operate: '返回首页',
},
custom: '自定义',

View File

@ -92,7 +92,8 @@ export default {
selected: '已選',
notFound: {
title: '404',
message: '無法訪問應用',
NoService: '暫時無法訪問服務',
NoPermission: '沒有權限訪問',
operate: '返回首頁',
},
custom: '自定義',

View File

@ -77,7 +77,9 @@ instance.interceptors.response.use(
if (err.response?.status === 403 && !err.response.config.url.includes('chat/open')) {
MsgError(
err.response.data && err.response.data.message ? err.response.data.message : '没有权限访问',
err.response.data && err.response.data.message
? err.response.data.message
: 'No permission to access',
)
}
return Promise.reject(err)
@ -249,15 +251,15 @@ export const exportExcel: (
function decodeFilenameBrowser(contentDisposition: string) {
// 提取并解码 Base64 部分
const base64Part = contentDisposition.match(/=\?utf-8\?b\?(.*?)\?=/i)?.[1];
if (!base64Part) return null;
const base64Part = contentDisposition.match(/=\?utf-8\?b\?(.*?)\?=/i)?.[1]
if (!base64Part) return null
// 使用 atob 解码 Base64
const decoded = decodeURIComponent(escape(atob(base64Part)));
const decoded = decodeURIComponent(escape(atob(base64Part)))
// 提取文件名
const filenameMatch = decoded.match(/filename="(.*?)"/i);
return filenameMatch ? filenameMatch[1] : null;
const filenameMatch = decoded.match(/filename="(.*?)"/i)
return filenameMatch ? filenameMatch[1] : null
}
export const exportFile: (
@ -271,37 +273,40 @@ export const exportFile: (
params: any,
loading?: NProgress | Ref<boolean>,
) => {
return promise(request({
url: url,
method: 'get',
params,
responseType: 'blob',
transformResponse: [function (data, headers) {
// 在这里可以访问 headers
// const contentType = headers['content-type'];
const contentDisposition = headers['content-disposition'];
// console.log('Content-Type:', contentType);
// console.log('Content-Disposition:', decodeFilenameBrowser(contentDisposition));
// 如果没有提供文件名,则使用默认名称
fileName = decodeFilenameBrowser(contentDisposition) || fileName;
return data; // 必须返回数据
}]
}), loading).then(
(res: any) => {
if (res) {
const blob = new Blob([res], {
type: 'application/octet-stream',
})
const link = document.createElement('a')
link.href = window.URL.createObjectURL(blob)
link.download = fileName
link.click()
//释放内存
window.URL.revokeObjectURL(link.href)
}
return true
},
)
return promise(
request({
url: url,
method: 'get',
params,
responseType: 'blob',
transformResponse: [
function (data, headers) {
// 在这里可以访问 headers
// const contentType = headers['content-type'];
const contentDisposition = headers['content-disposition']
// console.log('Content-Type:', contentType);
// console.log('Content-Disposition:', decodeFilenameBrowser(contentDisposition));
// 如果没有提供文件名,则使用默认名称
fileName = decodeFilenameBrowser(contentDisposition) || fileName
return data // 必须返回数据
},
],
}),
loading,
).then((res: any) => {
if (res) {
const blob = new Blob([res], {
type: 'application/octet-stream',
})
const link = document.createElement('a')
link.href = window.URL.createObjectURL(blob)
link.download = fileName
link.click()
//释放内存
window.URL.revokeObjectURL(link.href)
}
return true
})
}
export const exportExcelPost: (

View File

@ -17,6 +17,11 @@ export const routes: Array<RouteRecordRaw> = [
{
path: '/404',
name: '404',
component: () => import('@/views/404/index.vue'),
component: () => import('@/views/error/404.vue'),
},
{
path: '/no-service',
name: 'NoService',
component: () => import('@/views/error/NoService.vue'),
},
]

View File

@ -1,6 +1,5 @@
import type { RouteRecordRaw } from 'vue-router'
const modules: any = import.meta.glob('./modules/*.ts', { eager: true })
import { hasPermission, set_next_route } from '@/utils/permission/index'
const rolesRoutes: RouteRecordRaw[] = [...Object.keys(modules).map((key) => modules[key].default)]
@ -20,7 +19,7 @@ export const routes: Array<RouteRecordRaw> = [
{
path: '/no-permission',
name: 'noPermissionD',
component: () => import('@/views/no-permission/index.vue'),
component: () => import('@/views/error/NoPermission.vue'),
},
],
component: () => import('@/layout/layout-template/SimpleLayout.vue'),
@ -69,9 +68,14 @@ export const routes: Array<RouteRecordRaw> = [
name: 'permission',
component: () => import('@/views/Permission.vue'),
},
// {
// path: '/:pathMatch(.*)',
// name: '404',
// component: () => import('@/views/404/index.vue')
// }
{
path: '/no-service',
name: 'NoService',
component: () => import('@/views/error/NoService.vue'),
},
{
path: '/:pathMatch(.*)',
name: '404',
component: () => import('@/views/error/404.vue'),
},
]

View File

@ -1,51 +0,0 @@
<template>
<el-row class="not-found-container">
<el-col class="img" :xs="0" :sm="0" :md="12" :lg="12" :xl="12"> </el-col>
<el-col class="message-container" :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
<div class="title">{{ $t('common.notFound.title') }}</div>
<div class="message">{{ $t('common.notFound.message') }}</div>
<!-- TODO 暂时不处理 -->
<!-- <div class="operate"><el-button type="primary" @click="router.push('/')">{{ $t('views.notFound.operate') }}</el-button></div> -->
</el-col>
</el-row>
</template>
<script setup lang="ts">
import { useRouter } from 'vue-router'
const router = useRouter()
</script>
<style lang="scss" scoped>
.not-found-container {
height: 100vh;
width: 100vw;
display: flex;
justify-content: center;
align-items: center;
.img {
background-image: url('@/assets/404.png');
background-size: 100% 100%;
width: 50%;
height: 100%;
}
.message-container {
color: var(--app-text-color);
.title {
font-size: 50px;
font-weight: 500;
}
.message {
font-size: 20px;
margin: 30px 0 20px 0;
}
}
}
@media only screen and (max-width: 1000px) {
.not-found-container .message-container {
text-align: center;
}
}
</style>

View File

@ -0,0 +1,18 @@
<template>
<div class="not-found-container flex-center">
<div>
<img src="@/assets/404.png" width="250" alt="" />
<h4 class="text-center">{{ $t('common.notFound.title') }}</h4>
</div>
</div>
</template>
<script setup lang="ts">
import { useRouter } from 'vue-router'
const router = useRouter()
</script>
<style lang="scss" scoped>
.not-found-container {
height: 100vh;
width: 100vw;
}
</style>

View File

@ -0,0 +1,13 @@
<template>
<div class="flex-center mt-24">
<div>
<img src="@/assets/500.png" width="250" alt="" />
<h4 class="text-center">{{ $t('common.notFound.NoPermission') }}</h4>
</div>
</div>
</template>
<script setup lang="ts">
import { useRouter } from 'vue-router'
const router = useRouter()
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,18 @@
<template>
<div class="not-found-container flex-center">
<div>
<img src="@/assets/500.png" width="250" alt="" />
<h4 class="text-center">{{ $t('common.notFound.NoService') }}</h4>
</div>
</div>
</template>
<script setup lang="ts">
import { useRouter } from 'vue-router'
const router = useRouter()
</script>
<style lang="scss" scoped>
.not-found-container {
height: 100vh;
width: 100vw;
}
</style>

View File

@ -1,5 +0,0 @@
<template>
<div>没有权限访问</div>
</template>
<script setup lang="ts"></script>
<style lang="scss" scoped></style>