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.
This commit is contained in:
2026-08-30 07:29:53 +05:00
parent 7f4cc10d25
commit ddd1020a3a
22 changed files with 2393 additions and 2702 deletions
+24 -1
View File
@@ -1,9 +1,26 @@
{
"globals": {
"Component": true,
"ComponentPublicInstance": true,
"ComputedRef": true,
"DirectiveBinding": true,
"EffectScope": true,
"ElForm": true,
"ElMessage": true,
"ElMessageBox": true,
"ExtractDefaultPropTypes": true,
"ExtractPropTypes": true,
"ExtractPublicPropTypes": true,
"InjectionKey": true,
"MaybeRef": true,
"MaybeRefOrGetter": true,
"PropType": true,
"Ref": true,
"ShallowRef": true,
"Slot": true,
"Slots": true,
"VNode": true,
"WritableComputedRef": true,
"computed": true,
"createApp": true,
"customRef": true,
@@ -12,12 +29,14 @@
"effectScope": true,
"getCurrentInstance": true,
"getCurrentScope": true,
"getCurrentWatcher": true,
"h": true,
"inject": true,
"isProxy": true,
"isReactive": true,
"isReadonly": true,
"isRef": true,
"isShallow": true,
"markRaw": true,
"nextTick": true,
"onActivated": true,
@@ -33,25 +52,29 @@
"onServerPrefetch": true,
"onUnmounted": true,
"onUpdated": true,
"onWatcherCleanup": true,
"provide": true,
"reactive": true,
"readonly": true,
"ref": true,
"resolveComponent": true,
"resolveDirective": true,
"shallowReactive": true,
"shallowReadonly": true,
"shallowRef": true,
"toRaw": true,
"toRef": true,
"toRefs": true,
"toValue": true,
"triggerRef": true,
"unref": true,
"useAttrs": true,
"useCssModule": true,
"useCssVars": true,
"useFullscreen": true,
"useId": true,
"useModel": true,
"useSlots": true,
"useTemplateRef": true,
"useVModel": true,
"watch": true,
"watchEffect": true,
+6
View File
@@ -25,6 +25,12 @@ module.exports = {
"custom-property-pattern": null, // Custom property naming rule
"keyframes-name-pattern": null, // Keyframe naming rule
"no-descending-specificity": null, // Allow non-descending specificity
// Пустая строка `//` внутри блока комментария — абзацный разделитель, а не
// забытый комментарий. Правило появилось в stylelint-config-recommended-scss
// 17 и требует либо склеивать абзацы, либо разрывать блок пустой строкой;
// и то и другое ухудшает читаемость объяснений, ради которых эти
// комментарии написаны.
"scss/comment-no-empty": null,
// Allow global, export and deep pseudo classes
"selector-pseudo-class-no-unknown": [
true,
+1 -1
View File
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
+27 -27
View File
@@ -17,7 +17,7 @@
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.2",
"@vueuse/core": "^13.9.0",
"@vueuse/core": "^14.4.0",
"axios": "^1.20.0",
"copy-to-clipboard": "^3.3.3",
"echarts": "^6.1.0",
@@ -29,39 +29,39 @@
"vue": "^3.5.42",
"vue-echarts": "^8.1.0",
"vue-i18n": "^9.14.5",
"vue-router": "^4.6.4",
"vuedraggable": "^4.1.0"
"vue-router": "^4.6.4"
},
"devDependencies": {
"@iconify-json/ep": "^1.1.8",
"@iconify-json/ep": "^1.2.4",
"@types/nprogress": "^0.2.0",
"@types/path-browserify": "^1.0.0",
"@typescript-eslint/eslint-plugin": "^5.51.0",
"@typescript-eslint/parser": "^5.51.0",
"@vitejs/plugin-vue": "^4.0.0",
"@typescript-eslint/eslint-plugin": "^8.68.0",
"@typescript-eslint/parser": "^8.68.0",
"@vitejs/plugin-vue": "^6.0.8",
"autoprefixer": "^10.4.13",
"eslint": "^8.34.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-vue": "^9.9.0",
"eslint": "^8.57.1",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.5.6",
"eslint-plugin-vue": "^9.33.0",
"postcss": "^8.5.26",
"postcss-html": "^1.5.0",
"postcss-scss": "^4.0.6",
"prettier": "^2.8.7",
"postcss-html": "^2.0.0",
"postcss-scss": "^4.0.9",
"prettier": "^3.9.6",
"sass": "^1.103.1",
"stylelint": "^15.5.0",
"stylelint-config-html": "^1.1.0",
"stylelint-config-recess-order": "^4.0.0",
"stylelint-config-recommended-scss": "^9.0.1",
"stylelint-config-recommended-vue": "^1.4.0",
"stylelint-config-standard": "^32.0.0",
"stylelint-config-standard-scss": "^8.0.0",
"stylelint": "^17.14.1",
"stylelint-config-html": "^2.0.0",
"stylelint-config-recess-order": "^7.8.0",
"stylelint-config-recommended-scss": "^17.0.1",
"stylelint-config-recommended-vue": "^2.0.0",
"stylelint-config-standard": "^40.0.0",
"stylelint-config-standard-scss": "^17.0.0",
"stylelint-order": "8.0.0",
"typescript": "^5.9.3",
"unocss": "^0.50.1",
"unplugin-auto-import": "^0.13.0",
"unplugin-icons": "^0.15.1",
"unplugin-vue-components": "^0.23.0",
"vite": "^4.3.1",
"unocss": "^66.8.1",
"unplugin-auto-import": "^21.1.0",
"unplugin-icons": "^23.0.1",
"unplugin-vue-components": "^32.1.0",
"vite": "^7.3.6",
"vite-plugin-svg-icons": "^2.0.1",
"vue-tsc": "^3.3.11"
},
@@ -72,6 +72,6 @@
}
},
"engines": {
"node": ">=18.12.0"
"node": "^20.19.0 || >=22.12.0"
}
}
+2039 -2229
View File
File diff suppressed because it is too large Load Diff
@@ -1,29 +1,40 @@
<template>
<draggable class="flex gap-2" :list="tags" item-key="id" animation="200">
<template #item="{ element }">
<el-tag closable @close="handleClose(element)" size="large">
{{ element }}
<div class="flex gap-2">
<el-tag v-for="item in tags" :key="item" size="large">
{{ item }}
</el-tag>
</template>
<template #footer>
<el-input
v-if="inputVisible"
ref="inputRef"
v-model="tag"
class="w-50"
@keyup.enter="handleConfirm"
@blur="handleConfirm"
/>
<el-button v-else @click="showInput">+</el-button>
</template>
</draggable>
<el-text v-if="!tags.length" type="info"></el-text>
</div>
</template>
<script setup lang="ts">
import draggable from "vuedraggable";
import { ElInput } from "element-plus";
import { PropType } from "vue";
/**
* Список строковых значений конфигурации: ACME-домены, inline-правила ACL.
*
* Компонент только показывает список, и это приведение UI в соответствие с тем,
* что продукт умеет, а не сокращение возможностей.
*
* Что было. Здесь жил редактор на `vuedraggable`: перетаскивание тегов,
* closable-теги с удалением, поле ввода нового значения и
* `emit("update:tags")`. Сохранить он ничего не мог — единственное место
* использования — форма конфига Hysteria, отрисованная с `:disabled="true"`,
* причём значения передаются как `:tags="dataForm.acme.domains"`, без v-model,
* то есть слушателя у события не существует. Маршрутов записи серверного
* конфига в API нет.
*
* Цена этого редактора была измеримой. `vuedraggable` поставляется UMD-сборкой,
* поэтому её `require("vue")` разрешался в `vue/dist/vue.cjs.prod.js` — полную
* сборку Vue вместе с рантайм-компилятором шаблонов. В бандл уезжало:
*
* vuedraggable ~155 КБ
* sortablejs ~115 КБ
* @vue/compiler-core + compiler-dom ~230 КБ
*
* то есть около полумегабайта исходников ради перетаскивания тегов в форме,
* которая недоступна для редактирования.
*/
const props = defineProps({
tags: {
required: false,
@@ -32,42 +43,7 @@ const props = defineProps({
},
});
const emit = defineEmits<{
(event: "update:tags", value: string[]): void;
}>();
const tags = useVModel(props, "tags", emit);
const inputRef = ref(ElInput);
const state = reactive({
tag: "",
inputVisible: false,
});
const { tag, inputVisible } = toRefs(state);
const showInput = () => {
state.inputVisible = true;
nextTick(() => {
inputRef.value!.input!.focus();
});
};
const handleConfirm = (): void => {
const newTag = state.tag.trim();
if (newTag && !tags.value?.includes(newTag)) {
tags.value?.push(newTag);
state.tag = "";
}
state.inputVisible = false;
};
const handleClose = (tag: string): void => {
const index = tags.value?.indexOf(tag.trim());
if (index !== -1) {
tags.value?.splice(index, 1);
}
};
const tags = computed(() => props.tags ?? []);
</script>
<style lang="scss" scoped>
+21 -152
View File
@@ -1,71 +1,9 @@
<template>
<div class="flex gap-2">
<el-tag
:key="key"
v-for="(value, key) in mapObject"
@close="handleClose(key)"
@click="handleInfo(key)"
size="large"
closable
>
{{ key }}
<el-tag v-for="entry in entries" :key="entry.key" size="large">
{{ entry.key }}: {{ entry.value }}
</el-tag>
<el-button @click="handleAdd">+</el-button>
<el-dialog
:title="dialog.title"
v-model="dialog.visible"
width="600px"
append-to-body
@close="closeDialog"
>
<el-form
ref="dataFormRef"
:rules="dataFormRules"
label-position="top"
:model="dataForm"
>
<el-form-item label="key" prop="key">
<el-input v-model="dataForm.key" clearable />
</el-form-item>
<el-form-item label="value" prop="value">
<el-input v-model="dataForm.value" clearable />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm"
>{{ $t("common.confirm") }}
</el-button>
<el-button @click="closeDialog">{{ $t("common.cancel") }}</el-button>
</div>
</template>
</el-dialog>
<el-dialog
:title="infoDialog.title"
v-model="infoDialog.visible"
width="600px"
append-to-body
@close="infoDialog.visible = false"
>
<el-form label-position="top">
<el-form-item label="key" prop="key">
<el-tag>{{ dataInfo.key }}</el-tag>
</el-form-item>
<el-form-item label="value" prop="value">
<el-tag>{{ dataInfo.value }}</el-tag>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="infoDialog.visible = false"
>{{ $t("common.cancel") }}
</el-button>
</div>
</template>
</el-dialog>
<el-text v-if="!entries.length" type="info"></el-text>
</div>
</template>
@@ -77,13 +15,22 @@ export default {
<script setup lang="ts">
import { PropType } from "vue";
import { useI18n } from "vue-i18n";
interface Form {
key: string;
value: string;
}
/**
* Словарь «ключ — значение» из конфигурации: параметры ACME DNS-провайдера,
* заголовки masquerade.
*
* Как и ImputMultiple, компонент только показывает содержимое. Здесь был
* редактор с диалогом добавления, closable-тегами и `emit("update:mapObject")`,
* и он ничего не сохранял по тем же трём причинам: форма конфига Hysteria
* отрисована с `:disabled="true"`, значения передаются как
* `:map-object="dataForm.acme.dns.config"` без v-model, маршрутов записи
* серверного конфига в API нет.
*
* Значение показывается рядом с ключом: прежний вариант прятал его во второй
* диалог по клику на тег, что для read-only страницы лишний шаг — увидеть
* настройку сервера нужно сразу.
*/
const props = defineProps({
mapObject: {
required: false,
@@ -92,87 +39,9 @@ const props = defineProps({
},
});
const emit = defineEmits<{
(event: "update:mapObject", value: { [key: string]: string }): void;
}>();
const mapObject = useVModel(props, "mapObject", emit);
const { t } = useI18n();
const dataFormRef = ref(ElForm);
const dataFormRules = {
key: [
{
required: true,
message: t("common.required"),
trigger: ["change", "blur"],
},
],
value: [
{
required: true,
message: t("common.required"),
trigger: ["change", "blur"],
},
],
};
const state = reactive({
dataForm: {
key: "",
value: "",
} as Form,
dialog: {
title: "Add",
visible: false,
} as DialogType,
infoDialog: {
title: "Info",
visible: false,
},
dataInfo: {
key: "",
value: "",
} as Form,
});
const { dataForm, dialog, infoDialog, dataInfo } = toRefs(state);
const handleAdd = () => {
state.dialog.visible = true;
};
const handleClose = (key: string): void => {
delete mapObject.value[key];
};
const handleInfo = (key: string) => {
state.dataInfo = {
key: key,
value: mapObject.value[key] || "",
};
state.infoDialog.visible = true;
};
const submitForm = () => {
dataFormRef.value.validate((valid: any) => {
if (valid) {
if (mapObject.value[state.dataForm.key]) {
ElMessage.error(t("common.invalid"));
return;
}
mapObject.value[state.dataForm.key] = state.dataForm.value;
closeDialog();
}
});
};
const closeDialog = (): void => {
state.dialog.visible = false;
dataFormRef.value.resetFields();
dataFormRef.value.clearValidate();
};
const entries = computed(() =>
Object.entries(props.mapObject ?? {}).map(([key, value]) => ({ key, value }))
);
</script>
<style lang="scss" scoped>
@@ -16,7 +16,7 @@ export const hasRole: Directive = {
});
if (!hasRole) {
el.parentNode && el.parentNode.removeChild(el);
el.parentNode?.removeChild(el);
}
} else {
throw new Error("need roles! Like v-has-role=\"['admin', 'user']\"");
+1 -1
View File
@@ -72,8 +72,8 @@ function handleOutsideClick() {
<style lang="scss" scoped>
.app-wrapper {
&::after {
display: table;
clear: both;
display: table;
content: "";
}
+6 -2
View File
@@ -36,7 +36,11 @@ router.beforeEach(async (to, from, next) => {
}
// Если маршрут не найден, перейти на 404
if (to.matched.length === 0) {
from.name ? next({ name: from.name }) : next("/404");
if (from.name) {
next({ name: from.name });
} else {
next("/404");
}
} else {
next();
}
@@ -58,7 +62,7 @@ router.beforeEach(async (to, from, next) => {
return;
}
next({ ...to, replace: true });
} catch (error) {
} catch {
// Удалить token и перейти на страницу входа
await adminStore.resetToken();
next(`/login?redirect=${to.path}`);
+2 -1
View File
@@ -13,7 +13,8 @@
}
.peg {
box-shadow: 0 0 10px var(--el-color-primary),
box-shadow:
0 0 10px var(--el-color-primary),
0 0 5px var(--el-color-primary);
}
+2 -1
View File
@@ -25,7 +25,8 @@ body {
width: 100%;
height: 100%;
margin: 0;
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB",
font-family:
"Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB",
"Microsoft YaHei", Arial, sans-serif;
line-height: inherit;
-moz-osx-font-smoothing: grayscale;
+5 -3
View File
@@ -20,7 +20,9 @@
// reset element-ui css
.horizontal-collapse-transition {
transition: 0s width ease-in-out, 0s padding-left ease-in-out,
transition:
0s width ease-in-out,
0s padding-left ease-in-out,
0s padding-right ease-in-out;
}
@@ -178,10 +180,10 @@
& > .el-sub-menu__title {
& > span {
display: inline-block;
visibility: hidden;
width: 0;
height: 0;
overflow: hidden;
visibility: hidden;
}
}
}
@@ -206,8 +208,8 @@
&.hideSidebar {
.sidebar-container {
pointer-events: none;
transition-duration: 0.3s;
transform: translate3d(-$sideBarWidth, 0, 0);
transition-duration: 0.3s;
}
}
}
+145 -154
View File
@@ -1,160 +1,151 @@
// Generated by 'unplugin-auto-import'
export {};
/* eslint-disable */
/* prettier-ignore */
/* oxlint-disable */
/* oxfmt-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
// biome-ignore lint: disable
export {}
declare global {
const EffectScope: typeof import("vue")["EffectScope"];
const ElForm: typeof import("element-plus/es")["ElForm"];
const ElMessage: typeof import("element-plus/es")["ElMessage"];
const ElMessageBox: typeof import("element-plus/es")["ElMessageBox"];
const computed: typeof import("vue")["computed"];
const createApp: typeof import("vue")["createApp"];
const customRef: typeof import("vue")["customRef"];
const defineAsyncComponent: typeof import("vue")["defineAsyncComponent"];
const defineComponent: typeof import("vue")["defineComponent"];
const effectScope: typeof import("vue")["effectScope"];
const getCurrentInstance: typeof import("vue")["getCurrentInstance"];
const getCurrentScope: typeof import("vue")["getCurrentScope"];
const h: typeof import("vue")["h"];
const inject: typeof import("vue")["inject"];
const isProxy: typeof import("vue")["isProxy"];
const isReactive: typeof import("vue")["isReactive"];
const isReadonly: typeof import("vue")["isReadonly"];
const isRef: typeof import("vue")["isRef"];
const markRaw: typeof import("vue")["markRaw"];
const nextTick: typeof import("vue")["nextTick"];
const onActivated: typeof import("vue")["onActivated"];
const onBeforeMount: typeof import("vue")["onBeforeMount"];
const onBeforeUnmount: typeof import("vue")["onBeforeUnmount"];
const onBeforeUpdate: typeof import("vue")["onBeforeUpdate"];
const onDeactivated: typeof import("vue")["onDeactivated"];
const onErrorCaptured: typeof import("vue")["onErrorCaptured"];
const onMounted: typeof import("vue")["onMounted"];
const onRenderTracked: typeof import("vue")["onRenderTracked"];
const onRenderTriggered: typeof import("vue")["onRenderTriggered"];
const onScopeDispose: typeof import("vue")["onScopeDispose"];
const onServerPrefetch: typeof import("vue")["onServerPrefetch"];
const onUnmounted: typeof import("vue")["onUnmounted"];
const onUpdated: typeof import("vue")["onUpdated"];
const provide: typeof import("vue")["provide"];
const reactive: typeof import("vue")["reactive"];
const readonly: typeof import("vue")["readonly"];
const ref: typeof import("vue")["ref"];
const resolveComponent: typeof import("vue")["resolveComponent"];
const resolveDirective: typeof import("vue")["resolveDirective"];
const shallowReactive: typeof import("vue")["shallowReactive"];
const shallowReadonly: typeof import("vue")["shallowReadonly"];
const shallowRef: typeof import("vue")["shallowRef"];
const toRaw: typeof import("vue")["toRaw"];
const toRef: typeof import("vue")["toRef"];
const toRefs: typeof import("vue")["toRefs"];
const triggerRef: typeof import("vue")["triggerRef"];
const unref: typeof import("vue")["unref"];
const useAttrs: typeof import("vue")["useAttrs"];
const useCssModule: typeof import("vue")["useCssModule"];
const useCssVars: typeof import("vue")["useCssVars"];
const useFullscreen: typeof import("@vueuse/core")["useFullscreen"];
const useSlots: typeof import("vue")["useSlots"];
const useVModel: typeof import("@vueuse/core")["useVModel"];
const watch: typeof import("vue")["watch"];
const watchEffect: typeof import("vue")["watchEffect"];
const watchPostEffect: typeof import("vue")["watchPostEffect"];
const watchSyncEffect: typeof import("vue")["watchSyncEffect"];
const EffectScope: typeof import('vue').EffectScope
const ElForm: typeof import('element-plus/es').ElForm
const ElMessage: typeof import('element-plus/es').ElMessage
const ElMessageBox: typeof import('element-plus/es').ElMessageBox
const computed: typeof import('vue').computed
const createApp: typeof import('vue').createApp
const customRef: typeof import('vue').customRef
const defineAsyncComponent: typeof import('vue').defineAsyncComponent
const defineComponent: typeof import('vue').defineComponent
const effectScope: typeof import('vue').effectScope
const getCurrentInstance: typeof import('vue').getCurrentInstance
const getCurrentScope: typeof import('vue').getCurrentScope
const getCurrentWatcher: typeof import('vue').getCurrentWatcher
const h: typeof import('vue').h
const inject: typeof import('vue').inject
const isProxy: typeof import('vue').isProxy
const isReactive: typeof import('vue').isReactive
const isReadonly: typeof import('vue').isReadonly
const isRef: typeof import('vue').isRef
const isShallow: typeof import('vue').isShallow
const markRaw: typeof import('vue').markRaw
const nextTick: typeof import('vue').nextTick
const onActivated: typeof import('vue').onActivated
const onBeforeMount: typeof import('vue').onBeforeMount
const onBeforeUnmount: typeof import('vue').onBeforeUnmount
const onBeforeUpdate: typeof import('vue').onBeforeUpdate
const onDeactivated: typeof import('vue').onDeactivated
const onErrorCaptured: typeof import('vue').onErrorCaptured
const onMounted: typeof import('vue').onMounted
const onRenderTracked: typeof import('vue').onRenderTracked
const onRenderTriggered: typeof import('vue').onRenderTriggered
const onScopeDispose: typeof import('vue').onScopeDispose
const onServerPrefetch: typeof import('vue').onServerPrefetch
const onUnmounted: typeof import('vue').onUnmounted
const onUpdated: typeof import('vue').onUpdated
const onWatcherCleanup: typeof import('vue').onWatcherCleanup
const provide: typeof import('vue').provide
const reactive: typeof import('vue').reactive
const readonly: typeof import('vue').readonly
const ref: typeof import('vue').ref
const resolveComponent: typeof import('vue').resolveComponent
const shallowReactive: typeof import('vue').shallowReactive
const shallowReadonly: typeof import('vue').shallowReadonly
const shallowRef: typeof import('vue').shallowRef
const toRaw: typeof import('vue').toRaw
const toRef: typeof import('vue').toRef
const toRefs: typeof import('vue').toRefs
const toValue: typeof import('vue').toValue
const triggerRef: typeof import('vue').triggerRef
const unref: typeof import('vue').unref
const useAttrs: typeof import('vue').useAttrs
const useCssModule: typeof import('vue').useCssModule
const useCssVars: typeof import('vue').useCssVars
const useFullscreen: typeof import('@vueuse/core').useFullscreen
const useId: typeof import('vue').useId
const useModel: typeof import('vue').useModel
const useSlots: typeof import('vue').useSlots
const useTemplateRef: typeof import('vue').useTemplateRef
const useVModel: typeof import('@vueuse/core').useVModel
const watch: typeof import('vue').watch
const watchEffect: typeof import('vue').watchEffect
const watchPostEffect: typeof import('vue').watchPostEffect
const watchSyncEffect: typeof import('vue').watchSyncEffect
}
// for type re-export
declare global {
// @ts-ignore
export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, ShallowRef, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
import('vue')
}
// for vue template auto import
import { UnwrapRef } from "vue";
declare module "vue" {
import { UnwrapRef } from 'vue'
declare module 'vue' {
interface GlobalComponents {}
interface ComponentCustomProperties {
readonly EffectScope: UnwrapRef<typeof import("vue")["EffectScope"]>;
readonly ElForm: UnwrapRef<typeof import("element-plus/es")["ElForm"]>;
readonly ElMessage: UnwrapRef<
typeof import("element-plus/es")["ElMessage"]
>;
readonly ElMessageBox: UnwrapRef<
typeof import("element-plus/es")["ElMessageBox"]
>;
readonly computed: UnwrapRef<typeof import("vue")["computed"]>;
readonly createApp: UnwrapRef<typeof import("vue")["createApp"]>;
readonly customRef: UnwrapRef<typeof import("vue")["customRef"]>;
readonly defineAsyncComponent: UnwrapRef<
typeof import("vue")["defineAsyncComponent"]
>;
readonly defineComponent: UnwrapRef<
typeof import("vue")["defineComponent"]
>;
readonly effectScope: UnwrapRef<typeof import("vue")["effectScope"]>;
readonly getCurrentInstance: UnwrapRef<
typeof import("vue")["getCurrentInstance"]
>;
readonly getCurrentScope: UnwrapRef<
typeof import("vue")["getCurrentScope"]
>;
readonly h: UnwrapRef<typeof import("vue")["h"]>;
readonly inject: UnwrapRef<typeof import("vue")["inject"]>;
readonly isProxy: UnwrapRef<typeof import("vue")["isProxy"]>;
readonly isReactive: UnwrapRef<typeof import("vue")["isReactive"]>;
readonly isReadonly: UnwrapRef<typeof import("vue")["isReadonly"]>;
readonly isRef: UnwrapRef<typeof import("vue")["isRef"]>;
readonly markRaw: UnwrapRef<typeof import("vue")["markRaw"]>;
readonly nextTick: UnwrapRef<typeof import("vue")["nextTick"]>;
readonly onActivated: UnwrapRef<typeof import("vue")["onActivated"]>;
readonly onBeforeMount: UnwrapRef<typeof import("vue")["onBeforeMount"]>;
readonly onBeforeUnmount: UnwrapRef<
typeof import("vue")["onBeforeUnmount"]
>;
readonly onBeforeUpdate: UnwrapRef<typeof import("vue")["onBeforeUpdate"]>;
readonly onDeactivated: UnwrapRef<typeof import("vue")["onDeactivated"]>;
readonly onErrorCaptured: UnwrapRef<
typeof import("vue")["onErrorCaptured"]
>;
readonly onMounted: UnwrapRef<typeof import("vue")["onMounted"]>;
readonly onRenderTracked: UnwrapRef<
typeof import("vue")["onRenderTracked"]
>;
readonly onRenderTriggered: UnwrapRef<
typeof import("vue")["onRenderTriggered"]
>;
readonly onScopeDispose: UnwrapRef<typeof import("vue")["onScopeDispose"]>;
readonly onServerPrefetch: UnwrapRef<
typeof import("vue")["onServerPrefetch"]
>;
readonly onUnmounted: UnwrapRef<typeof import("vue")["onUnmounted"]>;
readonly onUpdated: UnwrapRef<typeof import("vue")["onUpdated"]>;
readonly provide: UnwrapRef<typeof import("vue")["provide"]>;
readonly reactive: UnwrapRef<typeof import("vue")["reactive"]>;
readonly readonly: UnwrapRef<typeof import("vue")["readonly"]>;
readonly ref: UnwrapRef<typeof import("vue")["ref"]>;
readonly resolveComponent: UnwrapRef<
typeof import("vue")["resolveComponent"]
>;
readonly resolveDirective: UnwrapRef<
typeof import("vue")["resolveDirective"]
>;
readonly shallowReactive: UnwrapRef<
typeof import("vue")["shallowReactive"]
>;
readonly shallowReadonly: UnwrapRef<
typeof import("vue")["shallowReadonly"]
>;
readonly shallowRef: UnwrapRef<typeof import("vue")["shallowRef"]>;
readonly toRaw: UnwrapRef<typeof import("vue")["toRaw"]>;
readonly toRef: UnwrapRef<typeof import("vue")["toRef"]>;
readonly toRefs: UnwrapRef<typeof import("vue")["toRefs"]>;
readonly triggerRef: UnwrapRef<typeof import("vue")["triggerRef"]>;
readonly unref: UnwrapRef<typeof import("vue")["unref"]>;
readonly useAttrs: UnwrapRef<typeof import("vue")["useAttrs"]>;
readonly useCssModule: UnwrapRef<typeof import("vue")["useCssModule"]>;
readonly useCssVars: UnwrapRef<typeof import("vue")["useCssVars"]>;
readonly useFullscreen: UnwrapRef<
typeof import("@vueuse/core")["useFullscreen"]
>;
readonly useSlots: UnwrapRef<typeof import("vue")["useSlots"]>;
readonly useVModel: UnwrapRef<typeof import("@vueuse/core")["useVModel"]>;
readonly watch: UnwrapRef<typeof import("vue")["watch"]>;
readonly watchEffect: UnwrapRef<typeof import("vue")["watchEffect"]>;
readonly watchPostEffect: UnwrapRef<
typeof import("vue")["watchPostEffect"]
>;
readonly watchSyncEffect: UnwrapRef<
typeof import("vue")["watchSyncEffect"]
>;
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
readonly ElForm: UnwrapRef<typeof import('element-plus/es')['ElForm']>
readonly ElMessage: UnwrapRef<typeof import('element-plus/es')['ElMessage']>
readonly ElMessageBox: UnwrapRef<typeof import('element-plus/es')['ElMessageBox']>
readonly computed: UnwrapRef<typeof import('vue')['computed']>
readonly createApp: UnwrapRef<typeof import('vue')['createApp']>
readonly customRef: UnwrapRef<typeof import('vue')['customRef']>
readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']>
readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']>
readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']>
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
readonly getCurrentWatcher: UnwrapRef<typeof import('vue')['getCurrentWatcher']>
readonly h: UnwrapRef<typeof import('vue')['h']>
readonly inject: UnwrapRef<typeof import('vue')['inject']>
readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']>
readonly isRef: UnwrapRef<typeof import('vue')['isRef']>
readonly isShallow: UnwrapRef<typeof import('vue')['isShallow']>
readonly markRaw: UnwrapRef<typeof import('vue')['markRaw']>
readonly nextTick: UnwrapRef<typeof import('vue')['nextTick']>
readonly onActivated: UnwrapRef<typeof import('vue')['onActivated']>
readonly onBeforeMount: UnwrapRef<typeof import('vue')['onBeforeMount']>
readonly onBeforeUnmount: UnwrapRef<typeof import('vue')['onBeforeUnmount']>
readonly onBeforeUpdate: UnwrapRef<typeof import('vue')['onBeforeUpdate']>
readonly onDeactivated: UnwrapRef<typeof import('vue')['onDeactivated']>
readonly onErrorCaptured: UnwrapRef<typeof import('vue')['onErrorCaptured']>
readonly onMounted: UnwrapRef<typeof import('vue')['onMounted']>
readonly onRenderTracked: UnwrapRef<typeof import('vue')['onRenderTracked']>
readonly onRenderTriggered: UnwrapRef<typeof import('vue')['onRenderTriggered']>
readonly onScopeDispose: UnwrapRef<typeof import('vue')['onScopeDispose']>
readonly onServerPrefetch: UnwrapRef<typeof import('vue')['onServerPrefetch']>
readonly onUnmounted: UnwrapRef<typeof import('vue')['onUnmounted']>
readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
readonly onWatcherCleanup: UnwrapRef<typeof import('vue')['onWatcherCleanup']>
readonly provide: UnwrapRef<typeof import('vue')['provide']>
readonly reactive: UnwrapRef<typeof import('vue')['reactive']>
readonly readonly: UnwrapRef<typeof import('vue')['readonly']>
readonly ref: UnwrapRef<typeof import('vue')['ref']>
readonly resolveComponent: UnwrapRef<typeof import('vue')['resolveComponent']>
readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']>
readonly shallowRef: UnwrapRef<typeof import('vue')['shallowRef']>
readonly toRaw: UnwrapRef<typeof import('vue')['toRaw']>
readonly toRef: UnwrapRef<typeof import('vue')['toRef']>
readonly toRefs: UnwrapRef<typeof import('vue')['toRefs']>
readonly toValue: UnwrapRef<typeof import('vue')['toValue']>
readonly triggerRef: UnwrapRef<typeof import('vue')['triggerRef']>
readonly unref: UnwrapRef<typeof import('vue')['unref']>
readonly useAttrs: UnwrapRef<typeof import('vue')['useAttrs']>
readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
readonly useCssVars: UnwrapRef<typeof import('vue')['useCssVars']>
readonly useFullscreen: UnwrapRef<typeof import('@vueuse/core')['useFullscreen']>
readonly useId: UnwrapRef<typeof import('vue')['useId']>
readonly useModel: UnwrapRef<typeof import('vue')['useModel']>
readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']>
readonly useTemplateRef: UnwrapRef<typeof import('vue')['useTemplateRef']>
readonly useVModel: UnwrapRef<typeof import('@vueuse/core')['useVModel']>
readonly watch: UnwrapRef<typeof import('vue')['watch']>
readonly watchEffect: UnwrapRef<typeof import('vue')['watchEffect']>
readonly watchPostEffect: UnwrapRef<typeof import('vue')['watchPostEffect']>
readonly watchSyncEffect: UnwrapRef<typeof import('vue')['watchSyncEffect']>
}
}
+61 -57
View File
@@ -1,64 +1,68 @@
// generated by unplugin-vue-components
// We suggest you to commit this file into source control
/* eslint-disable */
// @ts-nocheck
// biome-ignore lint: disable
// oxlint-disable
// ------
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
import "@vue/runtime-core";
export {};
export {}
declare module "@vue/runtime-core" {
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
ElAlert: typeof import("element-plus/es")["ElAlert"];
ElAvatar: typeof import("element-plus/es")["ElAvatar"];
ElButton: typeof import("element-plus/es")["ElButton"];
ElCard: typeof import("element-plus/es")["ElCard"];
ElCol: typeof import("element-plus/es")["ElCol"];
ElDatePicker: typeof import("element-plus/es")["ElDatePicker"];
ElDialog: typeof import("element-plus/es")["ElDialog"];
ElDrawer: typeof import("element-plus/es")["ElDrawer"];
ElDropdown: typeof import("element-plus/es")["ElDropdown"];
ElDropdownItem: typeof import("element-plus/es")["ElDropdownItem"];
ElDropdownMenu: typeof import("element-plus/es")["ElDropdownMenu"];
ElEmpty: typeof import("element-plus/es")["ElEmpty"];
ElForm: typeof import("element-plus/es")["ElForm"];
ElFormItem: typeof import("element-plus/es")["ElFormItem"];
ElInput: typeof import("element-plus/es")["ElInput"];
ElInputNumber: typeof import("element-plus/es")["ElInputNumber"];
ElMenu: typeof import("element-plus/es")["ElMenu"];
ElMenuItem: typeof import("element-plus/es")["ElMenuItem"];
ElOption: typeof import("element-plus/es")["ElOption"];
ElPagination: typeof import("element-plus/es")["ElPagination"];
ElProgress: typeof import("element-plus/es")["ElProgress"];
ElRadioButton: typeof import("element-plus/es")["ElRadioButton"];
ElRadioGroup: typeof import("element-plus/es")["ElRadioGroup"];
ElRow: typeof import("element-plus/es")["ElRow"];
ElScrollbar: typeof import("element-plus/es")["ElScrollbar"];
ElSelect: typeof import("element-plus/es")["ElSelect"];
ElSubMenu: typeof import("element-plus/es")["ElSubMenu"];
ElSwitch: typeof import("element-plus/es")["ElSwitch"];
ElTable: typeof import("element-plus/es")["ElTable"];
ElTableColumn: typeof import("element-plus/es")["ElTableColumn"];
ElTabPane: typeof import("element-plus/es")["ElTabPane"];
ElTabs: typeof import("element-plus/es")["ElTabs"];
ElTag: typeof import("element-plus/es")["ElTag"];
ElText: typeof import("element-plus/es")["ElText"];
ElTooltip: typeof import("element-plus/es")["ElTooltip"];
ElUpload: typeof import("element-plus/es")["ElUpload"];
Hamburger: typeof import("./../components/Hamburger/index.vue")["default"];
IEpCaretBottom: typeof import("~icons/ep/caret-bottom")["default"];
IEpDownload: typeof import("~icons/ep/download")["default"];
IEpMoreFilled: typeof import("~icons/ep/more-filled")["default"];
IEpRefresh: typeof import("~icons/ep/refresh")["default"];
IEpUserFilled: typeof import("~icons/ep/user-filled")["default"];
ImputMultiple: typeof import("./../components/ImputMultiple/index.vue")["default"];
LangSelect: typeof import("./../components/LangSelect/index.vue")["default"];
MapAdd: typeof import("./../components/MapAdd/index.vue")["default"];
Pagination: typeof import("./../components/Pagination/index.vue")["default"];
RouterLink: typeof import("vue-router")["RouterLink"];
RouterView: typeof import("vue-router")["RouterView"];
SvgIcon: typeof import("./../components/SvgIcon/index.vue")["default"];
UnitSelect: typeof import("./../components/UnitSelect/index.vue")["default"];
ElAlert: typeof import('element-plus/es')['ElAlert']
ElAvatar: typeof import('element-plus/es')['ElAvatar']
ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard']
ElCol: typeof import('element-plus/es')['ElCol']
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDrawer: typeof import('element-plus/es')['ElDrawer']
ElDropdown: typeof import('element-plus/es')['ElDropdown']
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
ElEmpty: typeof import('element-plus/es')['ElEmpty']
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElInput: typeof import('element-plus/es')['ElInput']
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElProgress: typeof import('element-plus/es')['ElProgress']
ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
ElRow: typeof import('element-plus/es')['ElRow']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
ElSwitch: typeof import('element-plus/es')['ElSwitch']
ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTabPane: typeof import('element-plus/es')['ElTabPane']
ElTabs: typeof import('element-plus/es')['ElTabs']
ElTag: typeof import('element-plus/es')['ElTag']
ElText: typeof import('element-plus/es')['ElText']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElUpload: typeof import('element-plus/es')['ElUpload']
Hamburger: typeof import('./../components/Hamburger/index.vue')['default']
IEpCaretBottom: typeof import('~icons/ep/caret-bottom')['default']
IEpDownload: typeof import('~icons/ep/download')['default']
IEpMoreFilled: typeof import('~icons/ep/more-filled')['default']
IEpRefresh: typeof import('~icons/ep/refresh')['default']
IEpUserFilled: typeof import('~icons/ep/user-filled')['default']
ImputMultiple: typeof import('./../components/ImputMultiple/index.vue')['default']
LangSelect: typeof import('./../components/LangSelect/index.vue')['default']
MapAdd: typeof import('./../components/MapAdd/index.vue')['default']
Pagination: typeof import('./../components/Pagination/index.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
SvgIcon: typeof import('./../components/SvgIcon/index.vue')['default']
UnitSelect: typeof import('./../components/UnitSelect/index.vue')['default']
}
export interface ComponentCustomProperties {
vLoading: typeof import("element-plus/es")["ElLoadingDirective"];
export interface GlobalDirectives {
vLoading: typeof import('element-plus/es')['ElLoadingDirective']
}
}
+6 -2
View File
@@ -2,8 +2,12 @@
declare module "*.vue" {
import { DefineComponent } from "vue";
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
const component: DefineComponent<{}, {}, any>;
// `object` вместо `{}`: последний допускает любое ненулевое значение,
// включая 0 и "", то есть не ограничивает ничего. Правило ban-types, под
// которое здесь стояло подавление, в @typescript-eslint 8 удалено и
// разделено на точечные — само подавление ссылалось на несуществующее имя.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const component: DefineComponent<object, object, any>;
export default component;
}
+1 -1
View File
@@ -247,7 +247,7 @@ const loadDashboard = async () => {
securityRisks.value = secRes.data;
lastSuccessAt.value = Date.now();
loadError.value = "";
} catch (error) {
} catch {
loadError.value = t("dashboard.refreshFailed");
} finally {
loading.value = false;
+1 -1
View File
@@ -242,8 +242,8 @@ function message() {
}
&__return-home {
display: block;
float: left;
display: block;
width: 110px;
height: 36px;
font-size: 14px;
+4 -2
View File
@@ -222,10 +222,10 @@ const handleLogin = () => {
.el-input__inner {
color: #fff;
caret-color: #fff;
background: transparent;
border: 0;
border-radius: 0;
caret-color: #fff;
&:-webkit-autofill {
box-shadow: 0 0 0 1000px transparent inset !important;
@@ -237,7 +237,9 @@ const handleLogin = () => {
&:-webkit-autofill:hover,
&:-webkit-autofill:focus,
&:-webkit-autofill:active {
transition: color 99999s ease-out, background-color 99999s ease-out;
transition:
color 99999s ease-out,
background-color 99999s ease-out;
transition-delay: 99999s;
}
}
+1 -3
View File
@@ -58,9 +58,7 @@ export default defineConfig((): UserConfig => {
},
plugins: [
vue(),
UnoCSS({
/* options */
}),
UnoCSS({/* options */}),
AutoImport({
// Auto import Vue helpers: ref, reactive, toRef, etc.
//