Files
HY2XS_flamy/apps/frontend/src/views/error-page/404.vue
T
founder ddd1020a3a build(frontend): Vite 4.3.1 -> 7.3.6 и уборка мёртвых редакторов конфига
Решение «Vite не трогать» пересмотрено по данным, а не по желанию обновиться.
`pnpm audit --prod` (то, на что смотрит gate сборки) был чист, но полный audit
показывал в build-цепочке два critical и rollup GHSA DOM clobbering — а он
затрагивает ГЕНЕРИРУЕМЫЙ bundle, то есть уезжает в production. Vite 4.3.1 тянул
rollup 3 без исправления.

Взята 7.3.6 — последняя линия до Rolldown. Vite 8 по-прежнему не берётся: это
смена бандлера, отдельная работа с собственной приёмкой.

Вместе с Vite обновлена цепочка плагинов (@vitejs/plugin-vue 4 -> 6,
unplugin-* и unocss с версий 2022-2023 годов) и линтеры (@typescript-eslint
5 -> 8, prettier 2 -> 3, stylelint 15 -> 17). Новый @typescript-eslint нашёл 7
настоящих замечаний — исправлены по существу, а не подавлением правил; среди
них подавление несуществующего правила ban-types и `{}` вместо `object`.

Отдельно — три мёртвых редактора. Outbounds уже был разобран, здесь то же
самое для ImputMultiple (ACME-домены, inline ACL) и MapAdd (параметры ACME DNS,
заголовки masquerade): оба редактировали конфиг Hysteria в форме с
:disabled="true", получали значения без v-model и эмитили события, которых
никто не слушает, на странице без единого маршрута записи.

Цена ImputMultiple была измеримой. vuedraggable поставляется UMD-сборкой,
поэтому её require("vue") разрешался в vue/dist/vue.cjs.prod.js — полную сборку
Vue с рантайм-компилятором шаблонов. В бандл уезжало ~500 КБ исходников
(vuedraggable + sortablejs + compiler-core + compiler-dom) ради перетаскивания
тегов в недоступной для редактирования форме.

Разбор bundle через sourcemap нашёл и вторую потерю: @vueuse/core собирался
дважды — наш 13.9.0 и 14.4.0 из element-plus. Версии сведены.

bundle: 2 813 525 против 2 404 202 на исходной базовой линии (+17% за Vue 3.5,
Element Plus 2.14, echarts 6 и rollup 4). Промежуточное состояние без этих двух
исправлений было 3 046 172.

pnpm audit --prod: чисто. typecheck/eslint/stylelint: 0. go build с dist: OK.
2026-08-30 07:29:53 +05:00

277 lines
5.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- setup не задаёт имя компонента, а имя нужно для keepAlive -->
<script lang="ts">
export default {
name: "Page404",
};
</script>
<script setup lang="ts">
import { useI18n } from "vue-i18n";
const { t } = useI18n();
function message() {
return t("errorPage.notFoundHeadline");
}
</script>
<template>
<div class="wscn-http404-container">
<div class="wscn-http404">
<div class="pic-404">
<img
class="pic-404__parent"
src="@/assets/404_images/404.png"
alt="404"
/>
<img
class="pic-404__child left"
src="@/assets/404_images/404_cloud.png"
alt="404"
/>
<img
class="pic-404__child mid"
src="@/assets/404_images/404_cloud.png"
alt="404"
/>
<img
class="pic-404__child right"
src="@/assets/404_images/404_cloud.png"
alt="404"
/>
</div>
<div class="bullshit">
<div class="bullshit__oops">{{ $t("errorPage.oops") }}</div>
<div class="bullshit__headline">{{ message() }}</div>
<div class="bullshit__info">
{{ $t("errorPage.notFoundInfo") }}
</div>
<a href="/#/" class="bullshit__return-home">{{
$t("errorPage.notFoundBackHome")
}}</a>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.wscn-http404-container {
position: absolute;
top: 40%;
left: 50%;
transform: translate(-50%, -50%);
}
.wscn-http404 {
position: relative;
width: 1200px;
padding: 0 50px;
overflow: hidden;
.pic-404 {
position: relative;
float: left;
width: 600px;
overflow: hidden;
&__parent {
width: 100%;
}
&__child {
position: absolute;
&.left {
top: 17px;
left: 220px;
width: 80px;
opacity: 0;
animation-name: cloudLeft;
animation-duration: 2s;
animation-timing-function: linear;
animation-delay: 1s;
animation-fill-mode: forwards;
}
&.mid {
top: 10px;
left: 420px;
width: 46px;
opacity: 0;
animation-name: cloudMid;
animation-duration: 2s;
animation-timing-function: linear;
animation-delay: 1.2s;
animation-fill-mode: forwards;
}
&.right {
top: 100px;
left: 500px;
width: 62px;
opacity: 0;
animation-name: cloudRight;
animation-duration: 2s;
animation-timing-function: linear;
animation-delay: 1s;
animation-fill-mode: forwards;
}
@keyframes cloudLeft {
0% {
top: 17px;
left: 220px;
opacity: 0;
}
20% {
top: 33px;
left: 188px;
opacity: 1;
}
80% {
top: 81px;
left: 92px;
opacity: 1;
}
100% {
top: 97px;
left: 60px;
opacity: 0;
}
}
@keyframes cloudMid {
0% {
top: 10px;
left: 420px;
opacity: 0;
}
20% {
top: 40px;
left: 360px;
opacity: 1;
}
70% {
top: 130px;
left: 180px;
opacity: 1;
}
100% {
top: 160px;
left: 120px;
opacity: 0;
}
}
@keyframes cloudRight {
0% {
top: 100px;
left: 500px;
opacity: 0;
}
20% {
top: 120px;
left: 460px;
opacity: 1;
}
80% {
top: 180px;
left: 340px;
opacity: 1;
}
100% {
top: 200px;
left: 300px;
opacity: 0;
}
}
}
}
.bullshit {
position: relative;
float: left;
width: 300px;
padding: 30px 0;
overflow: hidden;
&__oops {
margin-bottom: 20px;
font-size: 32px;
font-weight: bold;
line-height: 40px;
color: var(--el-color-primary);
opacity: 0;
animation-name: slideUp;
animation-duration: 0.5s;
animation-fill-mode: forwards;
}
&__headline {
margin-bottom: 10px;
font-size: 20px;
font-weight: bold;
line-height: 24px;
color: #222;
opacity: 0;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.1s;
animation-fill-mode: forwards;
}
&__info {
margin-bottom: 30px;
font-size: 13px;
line-height: 21px;
color: grey;
opacity: 0;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.2s;
animation-fill-mode: forwards;
}
&__return-home {
float: left;
display: block;
width: 110px;
height: 36px;
font-size: 14px;
line-height: 36px;
color: #fff;
text-align: center;
cursor: pointer;
background: var(--el-color-primary);
border-radius: 100px;
opacity: 0;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.3s;
animation-fill-mode: forwards;
}
@keyframes slideUp {
0% {
opacity: 0;
transform: translateY(60px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
}
}
</style>