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:
@@ -1,9 +1,26 @@
|
|||||||
{
|
{
|
||||||
"globals": {
|
"globals": {
|
||||||
|
"Component": true,
|
||||||
|
"ComponentPublicInstance": true,
|
||||||
|
"ComputedRef": true,
|
||||||
|
"DirectiveBinding": true,
|
||||||
"EffectScope": true,
|
"EffectScope": true,
|
||||||
"ElForm": true,
|
"ElForm": true,
|
||||||
"ElMessage": true,
|
"ElMessage": true,
|
||||||
"ElMessageBox": 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,
|
"computed": true,
|
||||||
"createApp": true,
|
"createApp": true,
|
||||||
"customRef": true,
|
"customRef": true,
|
||||||
@@ -12,12 +29,14 @@
|
|||||||
"effectScope": true,
|
"effectScope": true,
|
||||||
"getCurrentInstance": true,
|
"getCurrentInstance": true,
|
||||||
"getCurrentScope": true,
|
"getCurrentScope": true,
|
||||||
|
"getCurrentWatcher": true,
|
||||||
"h": true,
|
"h": true,
|
||||||
"inject": true,
|
"inject": true,
|
||||||
"isProxy": true,
|
"isProxy": true,
|
||||||
"isReactive": true,
|
"isReactive": true,
|
||||||
"isReadonly": true,
|
"isReadonly": true,
|
||||||
"isRef": true,
|
"isRef": true,
|
||||||
|
"isShallow": true,
|
||||||
"markRaw": true,
|
"markRaw": true,
|
||||||
"nextTick": true,
|
"nextTick": true,
|
||||||
"onActivated": true,
|
"onActivated": true,
|
||||||
@@ -33,25 +52,29 @@
|
|||||||
"onServerPrefetch": true,
|
"onServerPrefetch": true,
|
||||||
"onUnmounted": true,
|
"onUnmounted": true,
|
||||||
"onUpdated": true,
|
"onUpdated": true,
|
||||||
|
"onWatcherCleanup": true,
|
||||||
"provide": true,
|
"provide": true,
|
||||||
"reactive": true,
|
"reactive": true,
|
||||||
"readonly": true,
|
"readonly": true,
|
||||||
"ref": true,
|
"ref": true,
|
||||||
"resolveComponent": true,
|
"resolveComponent": true,
|
||||||
"resolveDirective": true,
|
|
||||||
"shallowReactive": true,
|
"shallowReactive": true,
|
||||||
"shallowReadonly": true,
|
"shallowReadonly": true,
|
||||||
"shallowRef": true,
|
"shallowRef": true,
|
||||||
"toRaw": true,
|
"toRaw": true,
|
||||||
"toRef": true,
|
"toRef": true,
|
||||||
"toRefs": true,
|
"toRefs": true,
|
||||||
|
"toValue": true,
|
||||||
"triggerRef": true,
|
"triggerRef": true,
|
||||||
"unref": true,
|
"unref": true,
|
||||||
"useAttrs": true,
|
"useAttrs": true,
|
||||||
"useCssModule": true,
|
"useCssModule": true,
|
||||||
"useCssVars": true,
|
"useCssVars": true,
|
||||||
"useFullscreen": true,
|
"useFullscreen": true,
|
||||||
|
"useId": true,
|
||||||
|
"useModel": true,
|
||||||
"useSlots": true,
|
"useSlots": true,
|
||||||
|
"useTemplateRef": true,
|
||||||
"useVModel": true,
|
"useVModel": true,
|
||||||
"watch": true,
|
"watch": true,
|
||||||
"watchEffect": true,
|
"watchEffect": true,
|
||||||
|
|||||||
@@ -25,6 +25,12 @@ module.exports = {
|
|||||||
"custom-property-pattern": null, // Custom property naming rule
|
"custom-property-pattern": null, // Custom property naming rule
|
||||||
"keyframes-name-pattern": null, // Keyframe naming rule
|
"keyframes-name-pattern": null, // Keyframe naming rule
|
||||||
"no-descending-specificity": null, // Allow non-descending specificity
|
"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
|
// Allow global, export and deep pseudo classes
|
||||||
"selector-pseudo-class-no-unknown": [
|
"selector-pseudo-class-no-unknown": [
|
||||||
true,
|
true,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
|
|||||||
+27
-27
@@ -17,7 +17,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@element-plus/icons-vue": "^2.3.2",
|
"@element-plus/icons-vue": "^2.3.2",
|
||||||
"@vueuse/core": "^13.9.0",
|
"@vueuse/core": "^14.4.0",
|
||||||
"axios": "^1.20.0",
|
"axios": "^1.20.0",
|
||||||
"copy-to-clipboard": "^3.3.3",
|
"copy-to-clipboard": "^3.3.3",
|
||||||
"echarts": "^6.1.0",
|
"echarts": "^6.1.0",
|
||||||
@@ -29,39 +29,39 @@
|
|||||||
"vue": "^3.5.42",
|
"vue": "^3.5.42",
|
||||||
"vue-echarts": "^8.1.0",
|
"vue-echarts": "^8.1.0",
|
||||||
"vue-i18n": "^9.14.5",
|
"vue-i18n": "^9.14.5",
|
||||||
"vue-router": "^4.6.4",
|
"vue-router": "^4.6.4"
|
||||||
"vuedraggable": "^4.1.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@iconify-json/ep": "^1.1.8",
|
"@iconify-json/ep": "^1.2.4",
|
||||||
"@types/nprogress": "^0.2.0",
|
"@types/nprogress": "^0.2.0",
|
||||||
"@types/path-browserify": "^1.0.0",
|
"@types/path-browserify": "^1.0.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.51.0",
|
"@typescript-eslint/eslint-plugin": "^8.68.0",
|
||||||
"@typescript-eslint/parser": "^5.51.0",
|
"@typescript-eslint/parser": "^8.68.0",
|
||||||
"@vitejs/plugin-vue": "^4.0.0",
|
"@vitejs/plugin-vue": "^6.0.8",
|
||||||
"autoprefixer": "^10.4.13",
|
"autoprefixer": "^10.4.13",
|
||||||
"eslint": "^8.34.0",
|
"eslint": "^8.57.1",
|
||||||
"eslint-config-prettier": "^8.8.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^5.5.6",
|
||||||
"eslint-plugin-vue": "^9.9.0",
|
"eslint-plugin-vue": "^9.33.0",
|
||||||
"postcss": "^8.5.26",
|
"postcss": "^8.5.26",
|
||||||
"postcss-html": "^1.5.0",
|
"postcss-html": "^2.0.0",
|
||||||
"postcss-scss": "^4.0.6",
|
"postcss-scss": "^4.0.9",
|
||||||
"prettier": "^2.8.7",
|
"prettier": "^3.9.6",
|
||||||
"sass": "^1.103.1",
|
"sass": "^1.103.1",
|
||||||
"stylelint": "^15.5.0",
|
"stylelint": "^17.14.1",
|
||||||
"stylelint-config-html": "^1.1.0",
|
"stylelint-config-html": "^2.0.0",
|
||||||
"stylelint-config-recess-order": "^4.0.0",
|
"stylelint-config-recess-order": "^7.8.0",
|
||||||
"stylelint-config-recommended-scss": "^9.0.1",
|
"stylelint-config-recommended-scss": "^17.0.1",
|
||||||
"stylelint-config-recommended-vue": "^1.4.0",
|
"stylelint-config-recommended-vue": "^2.0.0",
|
||||||
"stylelint-config-standard": "^32.0.0",
|
"stylelint-config-standard": "^40.0.0",
|
||||||
"stylelint-config-standard-scss": "^8.0.0",
|
"stylelint-config-standard-scss": "^17.0.0",
|
||||||
|
"stylelint-order": "8.0.0",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"unocss": "^0.50.1",
|
"unocss": "^66.8.1",
|
||||||
"unplugin-auto-import": "^0.13.0",
|
"unplugin-auto-import": "^21.1.0",
|
||||||
"unplugin-icons": "^0.15.1",
|
"unplugin-icons": "^23.0.1",
|
||||||
"unplugin-vue-components": "^0.23.0",
|
"unplugin-vue-components": "^32.1.0",
|
||||||
"vite": "^4.3.1",
|
"vite": "^7.3.6",
|
||||||
"vite-plugin-svg-icons": "^2.0.1",
|
"vite-plugin-svg-icons": "^2.0.1",
|
||||||
"vue-tsc": "^3.3.11"
|
"vue-tsc": "^3.3.11"
|
||||||
},
|
},
|
||||||
@@ -72,6 +72,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.12.0"
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+2039
-2229
File diff suppressed because it is too large
Load Diff
@@ -50,8 +50,8 @@ import type {
|
|||||||
type DeepRequired<T> = T extends (infer U)[]
|
type DeepRequired<T> = T extends (infer U)[]
|
||||||
? DeepRequired<U>[]
|
? DeepRequired<U>[]
|
||||||
: T extends object
|
: T extends object
|
||||||
? { [K in keyof T]-?: DeepRequired<NonNullable<T[K]>> }
|
? { [K in keyof T]-?: DeepRequired<NonNullable<T[K]>> }
|
||||||
: T;
|
: T;
|
||||||
|
|
||||||
/** Конфиг Hysteria в том виде, в котором его показывает панель. */
|
/** Конфиг Hysteria в том виде, в котором его показывает панель. */
|
||||||
export type Hysteria2ServerConfigView = DeepRequired<Hysteria2ServerConfig>;
|
export type Hysteria2ServerConfigView = DeepRequired<Hysteria2ServerConfig>;
|
||||||
|
|||||||
@@ -1,29 +1,40 @@
|
|||||||
<template>
|
<template>
|
||||||
<draggable class="flex gap-2" :list="tags" item-key="id" animation="200">
|
<div class="flex gap-2">
|
||||||
<template #item="{ element }">
|
<el-tag v-for="item in tags" :key="item" size="large">
|
||||||
<el-tag closable @close="handleClose(element)" size="large">
|
{{ item }}
|
||||||
{{ element }}
|
</el-tag>
|
||||||
</el-tag>
|
<el-text v-if="!tags.length" type="info">—</el-text>
|
||||||
</template>
|
</div>
|
||||||
<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>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import draggable from "vuedraggable";
|
|
||||||
import { ElInput } from "element-plus";
|
|
||||||
import { PropType } from "vue";
|
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({
|
const props = defineProps({
|
||||||
tags: {
|
tags: {
|
||||||
required: false,
|
required: false,
|
||||||
@@ -32,42 +43,7 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const tags = computed(() => props.tags ?? []);
|
||||||
(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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -1,71 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<el-tag
|
<el-tag v-for="entry in entries" :key="entry.key" size="large">
|
||||||
:key="key"
|
{{ entry.key }}: {{ entry.value }}
|
||||||
v-for="(value, key) in mapObject"
|
|
||||||
@close="handleClose(key)"
|
|
||||||
@click="handleInfo(key)"
|
|
||||||
size="large"
|
|
||||||
closable
|
|
||||||
>
|
|
||||||
{{ key }}
|
|
||||||
</el-tag>
|
</el-tag>
|
||||||
|
<el-text v-if="!entries.length" type="info">—</el-text>
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -77,13 +15,22 @@ export default {
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { PropType } from "vue";
|
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({
|
const props = defineProps({
|
||||||
mapObject: {
|
mapObject: {
|
||||||
required: false,
|
required: false,
|
||||||
@@ -92,87 +39,9 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const entries = computed(() =>
|
||||||
(event: "update:mapObject", value: { [key: string]: string }): void;
|
Object.entries(props.mapObject ?? {}).map(([key, value]) => ({ key, value }))
|
||||||
}>();
|
);
|
||||||
|
|
||||||
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();
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export const hasRole: Directive = {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!hasRole) {
|
if (!hasRole) {
|
||||||
el.parentNode && el.parentNode.removeChild(el);
|
el.parentNode?.removeChild(el);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error("need roles! Like v-has-role=\"['admin', 'user']\"");
|
throw new Error("need roles! Like v-has-role=\"['admin', 'user']\"");
|
||||||
|
|||||||
@@ -72,8 +72,8 @@ function handleOutsideClick() {
|
|||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.app-wrapper {
|
.app-wrapper {
|
||||||
&::after {
|
&::after {
|
||||||
display: table;
|
|
||||||
clear: both;
|
clear: both;
|
||||||
|
display: table;
|
||||||
content: "";
|
content: "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,11 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
}
|
}
|
||||||
// Если маршрут не найден, перейти на 404
|
// Если маршрут не найден, перейти на 404
|
||||||
if (to.matched.length === 0) {
|
if (to.matched.length === 0) {
|
||||||
from.name ? next({ name: from.name }) : next("/404");
|
if (from.name) {
|
||||||
|
next({ name: from.name });
|
||||||
|
} else {
|
||||||
|
next("/404");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
@@ -58,7 +62,7 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
next({ ...to, replace: true });
|
next({ ...to, replace: true });
|
||||||
} catch (error) {
|
} catch {
|
||||||
// Удалить token и перейти на страницу входа
|
// Удалить token и перейти на страницу входа
|
||||||
await adminStore.resetToken();
|
await adminStore.resetToken();
|
||||||
next(`/login?redirect=${to.path}`);
|
next(`/login?redirect=${to.path}`);
|
||||||
|
|||||||
@@ -13,7 +13,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.peg {
|
.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);
|
0 0 5px var(--el-color-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ body {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin: 0;
|
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;
|
"Microsoft YaHei", Arial, sans-serif;
|
||||||
line-height: inherit;
|
line-height: inherit;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
|||||||
@@ -20,7 +20,9 @@
|
|||||||
|
|
||||||
// reset element-ui css
|
// reset element-ui css
|
||||||
.horizontal-collapse-transition {
|
.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;
|
0s padding-right ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,10 +180,10 @@
|
|||||||
& > .el-sub-menu__title {
|
& > .el-sub-menu__title {
|
||||||
& > span {
|
& > span {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
visibility: hidden;
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
visibility: hidden;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -206,8 +208,8 @@
|
|||||||
&.hideSidebar {
|
&.hideSidebar {
|
||||||
.sidebar-container {
|
.sidebar-container {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
transition-duration: 0.3s;
|
|
||||||
transform: translate3d(-$sideBarWidth, 0, 0);
|
transform: translate3d(-$sideBarWidth, 0, 0);
|
||||||
|
transition-duration: 0.3s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+146
-155
@@ -1,160 +1,151 @@
|
|||||||
// Generated by 'unplugin-auto-import'
|
/* eslint-disable */
|
||||||
export {};
|
/* prettier-ignore */
|
||||||
|
/* oxlint-disable */
|
||||||
|
/* oxfmt-ignore */
|
||||||
|
// @ts-nocheck
|
||||||
|
// noinspection JSUnusedGlobalSymbols
|
||||||
|
// Generated by unplugin-auto-import
|
||||||
|
// biome-ignore lint: disable
|
||||||
|
export {}
|
||||||
declare global {
|
declare global {
|
||||||
const EffectScope: typeof import("vue")["EffectScope"];
|
const EffectScope: typeof import('vue').EffectScope
|
||||||
const ElForm: typeof import("element-plus/es")["ElForm"];
|
const ElForm: typeof import('element-plus/es').ElForm
|
||||||
const ElMessage: typeof import("element-plus/es")["ElMessage"];
|
const ElMessage: typeof import('element-plus/es').ElMessage
|
||||||
const ElMessageBox: typeof import("element-plus/es")["ElMessageBox"];
|
const ElMessageBox: typeof import('element-plus/es').ElMessageBox
|
||||||
const computed: typeof import("vue")["computed"];
|
const computed: typeof import('vue').computed
|
||||||
const createApp: typeof import("vue")["createApp"];
|
const createApp: typeof import('vue').createApp
|
||||||
const customRef: typeof import("vue")["customRef"];
|
const customRef: typeof import('vue').customRef
|
||||||
const defineAsyncComponent: typeof import("vue")["defineAsyncComponent"];
|
const defineAsyncComponent: typeof import('vue').defineAsyncComponent
|
||||||
const defineComponent: typeof import("vue")["defineComponent"];
|
const defineComponent: typeof import('vue').defineComponent
|
||||||
const effectScope: typeof import("vue")["effectScope"];
|
const effectScope: typeof import('vue').effectScope
|
||||||
const getCurrentInstance: typeof import("vue")["getCurrentInstance"];
|
const getCurrentInstance: typeof import('vue').getCurrentInstance
|
||||||
const getCurrentScope: typeof import("vue")["getCurrentScope"];
|
const getCurrentScope: typeof import('vue').getCurrentScope
|
||||||
const h: typeof import("vue")["h"];
|
const getCurrentWatcher: typeof import('vue').getCurrentWatcher
|
||||||
const inject: typeof import("vue")["inject"];
|
const h: typeof import('vue').h
|
||||||
const isProxy: typeof import("vue")["isProxy"];
|
const inject: typeof import('vue').inject
|
||||||
const isReactive: typeof import("vue")["isReactive"];
|
const isProxy: typeof import('vue').isProxy
|
||||||
const isReadonly: typeof import("vue")["isReadonly"];
|
const isReactive: typeof import('vue').isReactive
|
||||||
const isRef: typeof import("vue")["isRef"];
|
const isReadonly: typeof import('vue').isReadonly
|
||||||
const markRaw: typeof import("vue")["markRaw"];
|
const isRef: typeof import('vue').isRef
|
||||||
const nextTick: typeof import("vue")["nextTick"];
|
const isShallow: typeof import('vue').isShallow
|
||||||
const onActivated: typeof import("vue")["onActivated"];
|
const markRaw: typeof import('vue').markRaw
|
||||||
const onBeforeMount: typeof import("vue")["onBeforeMount"];
|
const nextTick: typeof import('vue').nextTick
|
||||||
const onBeforeUnmount: typeof import("vue")["onBeforeUnmount"];
|
const onActivated: typeof import('vue').onActivated
|
||||||
const onBeforeUpdate: typeof import("vue")["onBeforeUpdate"];
|
const onBeforeMount: typeof import('vue').onBeforeMount
|
||||||
const onDeactivated: typeof import("vue")["onDeactivated"];
|
const onBeforeUnmount: typeof import('vue').onBeforeUnmount
|
||||||
const onErrorCaptured: typeof import("vue")["onErrorCaptured"];
|
const onBeforeUpdate: typeof import('vue').onBeforeUpdate
|
||||||
const onMounted: typeof import("vue")["onMounted"];
|
const onDeactivated: typeof import('vue').onDeactivated
|
||||||
const onRenderTracked: typeof import("vue")["onRenderTracked"];
|
const onErrorCaptured: typeof import('vue').onErrorCaptured
|
||||||
const onRenderTriggered: typeof import("vue")["onRenderTriggered"];
|
const onMounted: typeof import('vue').onMounted
|
||||||
const onScopeDispose: typeof import("vue")["onScopeDispose"];
|
const onRenderTracked: typeof import('vue').onRenderTracked
|
||||||
const onServerPrefetch: typeof import("vue")["onServerPrefetch"];
|
const onRenderTriggered: typeof import('vue').onRenderTriggered
|
||||||
const onUnmounted: typeof import("vue")["onUnmounted"];
|
const onScopeDispose: typeof import('vue').onScopeDispose
|
||||||
const onUpdated: typeof import("vue")["onUpdated"];
|
const onServerPrefetch: typeof import('vue').onServerPrefetch
|
||||||
const provide: typeof import("vue")["provide"];
|
const onUnmounted: typeof import('vue').onUnmounted
|
||||||
const reactive: typeof import("vue")["reactive"];
|
const onUpdated: typeof import('vue').onUpdated
|
||||||
const readonly: typeof import("vue")["readonly"];
|
const onWatcherCleanup: typeof import('vue').onWatcherCleanup
|
||||||
const ref: typeof import("vue")["ref"];
|
const provide: typeof import('vue').provide
|
||||||
const resolveComponent: typeof import("vue")["resolveComponent"];
|
const reactive: typeof import('vue').reactive
|
||||||
const resolveDirective: typeof import("vue")["resolveDirective"];
|
const readonly: typeof import('vue').readonly
|
||||||
const shallowReactive: typeof import("vue")["shallowReactive"];
|
const ref: typeof import('vue').ref
|
||||||
const shallowReadonly: typeof import("vue")["shallowReadonly"];
|
const resolveComponent: typeof import('vue').resolveComponent
|
||||||
const shallowRef: typeof import("vue")["shallowRef"];
|
const shallowReactive: typeof import('vue').shallowReactive
|
||||||
const toRaw: typeof import("vue")["toRaw"];
|
const shallowReadonly: typeof import('vue').shallowReadonly
|
||||||
const toRef: typeof import("vue")["toRef"];
|
const shallowRef: typeof import('vue').shallowRef
|
||||||
const toRefs: typeof import("vue")["toRefs"];
|
const toRaw: typeof import('vue').toRaw
|
||||||
const triggerRef: typeof import("vue")["triggerRef"];
|
const toRef: typeof import('vue').toRef
|
||||||
const unref: typeof import("vue")["unref"];
|
const toRefs: typeof import('vue').toRefs
|
||||||
const useAttrs: typeof import("vue")["useAttrs"];
|
const toValue: typeof import('vue').toValue
|
||||||
const useCssModule: typeof import("vue")["useCssModule"];
|
const triggerRef: typeof import('vue').triggerRef
|
||||||
const useCssVars: typeof import("vue")["useCssVars"];
|
const unref: typeof import('vue').unref
|
||||||
const useFullscreen: typeof import("@vueuse/core")["useFullscreen"];
|
const useAttrs: typeof import('vue').useAttrs
|
||||||
const useSlots: typeof import("vue")["useSlots"];
|
const useCssModule: typeof import('vue').useCssModule
|
||||||
const useVModel: typeof import("@vueuse/core")["useVModel"];
|
const useCssVars: typeof import('vue').useCssVars
|
||||||
const watch: typeof import("vue")["watch"];
|
const useFullscreen: typeof import('@vueuse/core').useFullscreen
|
||||||
const watchEffect: typeof import("vue")["watchEffect"];
|
const useId: typeof import('vue').useId
|
||||||
const watchPostEffect: typeof import("vue")["watchPostEffect"];
|
const useModel: typeof import('vue').useModel
|
||||||
const watchSyncEffect: typeof import("vue")["watchSyncEffect"];
|
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
|
// for vue template auto import
|
||||||
import { UnwrapRef } from "vue";
|
import { UnwrapRef } from 'vue'
|
||||||
declare module "vue" {
|
declare module 'vue' {
|
||||||
|
interface GlobalComponents {}
|
||||||
interface ComponentCustomProperties {
|
interface ComponentCustomProperties {
|
||||||
readonly EffectScope: UnwrapRef<typeof import("vue")["EffectScope"]>;
|
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
|
||||||
readonly ElForm: UnwrapRef<typeof import("element-plus/es")["ElForm"]>;
|
readonly ElForm: UnwrapRef<typeof import('element-plus/es')['ElForm']>
|
||||||
readonly ElMessage: UnwrapRef<
|
readonly ElMessage: UnwrapRef<typeof import('element-plus/es')['ElMessage']>
|
||||||
typeof import("element-plus/es")["ElMessage"]
|
readonly ElMessageBox: UnwrapRef<typeof import('element-plus/es')['ElMessageBox']>
|
||||||
>;
|
readonly computed: UnwrapRef<typeof import('vue')['computed']>
|
||||||
readonly ElMessageBox: UnwrapRef<
|
readonly createApp: UnwrapRef<typeof import('vue')['createApp']>
|
||||||
typeof import("element-plus/es")["ElMessageBox"]
|
readonly customRef: UnwrapRef<typeof import('vue')['customRef']>
|
||||||
>;
|
readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']>
|
||||||
readonly computed: UnwrapRef<typeof import("vue")["computed"]>;
|
readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']>
|
||||||
readonly createApp: UnwrapRef<typeof import("vue")["createApp"]>;
|
readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']>
|
||||||
readonly customRef: UnwrapRef<typeof import("vue")["customRef"]>;
|
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
|
||||||
readonly defineAsyncComponent: UnwrapRef<
|
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
|
||||||
typeof import("vue")["defineAsyncComponent"]
|
readonly getCurrentWatcher: UnwrapRef<typeof import('vue')['getCurrentWatcher']>
|
||||||
>;
|
readonly h: UnwrapRef<typeof import('vue')['h']>
|
||||||
readonly defineComponent: UnwrapRef<
|
readonly inject: UnwrapRef<typeof import('vue')['inject']>
|
||||||
typeof import("vue")["defineComponent"]
|
readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
|
||||||
>;
|
readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
|
||||||
readonly effectScope: UnwrapRef<typeof import("vue")["effectScope"]>;
|
readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']>
|
||||||
readonly getCurrentInstance: UnwrapRef<
|
readonly isRef: UnwrapRef<typeof import('vue')['isRef']>
|
||||||
typeof import("vue")["getCurrentInstance"]
|
readonly isShallow: UnwrapRef<typeof import('vue')['isShallow']>
|
||||||
>;
|
readonly markRaw: UnwrapRef<typeof import('vue')['markRaw']>
|
||||||
readonly getCurrentScope: UnwrapRef<
|
readonly nextTick: UnwrapRef<typeof import('vue')['nextTick']>
|
||||||
typeof import("vue")["getCurrentScope"]
|
readonly onActivated: UnwrapRef<typeof import('vue')['onActivated']>
|
||||||
>;
|
readonly onBeforeMount: UnwrapRef<typeof import('vue')['onBeforeMount']>
|
||||||
readonly h: UnwrapRef<typeof import("vue")["h"]>;
|
readonly onBeforeUnmount: UnwrapRef<typeof import('vue')['onBeforeUnmount']>
|
||||||
readonly inject: UnwrapRef<typeof import("vue")["inject"]>;
|
readonly onBeforeUpdate: UnwrapRef<typeof import('vue')['onBeforeUpdate']>
|
||||||
readonly isProxy: UnwrapRef<typeof import("vue")["isProxy"]>;
|
readonly onDeactivated: UnwrapRef<typeof import('vue')['onDeactivated']>
|
||||||
readonly isReactive: UnwrapRef<typeof import("vue")["isReactive"]>;
|
readonly onErrorCaptured: UnwrapRef<typeof import('vue')['onErrorCaptured']>
|
||||||
readonly isReadonly: UnwrapRef<typeof import("vue")["isReadonly"]>;
|
readonly onMounted: UnwrapRef<typeof import('vue')['onMounted']>
|
||||||
readonly isRef: UnwrapRef<typeof import("vue")["isRef"]>;
|
readonly onRenderTracked: UnwrapRef<typeof import('vue')['onRenderTracked']>
|
||||||
readonly markRaw: UnwrapRef<typeof import("vue")["markRaw"]>;
|
readonly onRenderTriggered: UnwrapRef<typeof import('vue')['onRenderTriggered']>
|
||||||
readonly nextTick: UnwrapRef<typeof import("vue")["nextTick"]>;
|
readonly onScopeDispose: UnwrapRef<typeof import('vue')['onScopeDispose']>
|
||||||
readonly onActivated: UnwrapRef<typeof import("vue")["onActivated"]>;
|
readonly onServerPrefetch: UnwrapRef<typeof import('vue')['onServerPrefetch']>
|
||||||
readonly onBeforeMount: UnwrapRef<typeof import("vue")["onBeforeMount"]>;
|
readonly onUnmounted: UnwrapRef<typeof import('vue')['onUnmounted']>
|
||||||
readonly onBeforeUnmount: UnwrapRef<
|
readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
|
||||||
typeof import("vue")["onBeforeUnmount"]
|
readonly onWatcherCleanup: UnwrapRef<typeof import('vue')['onWatcherCleanup']>
|
||||||
>;
|
readonly provide: UnwrapRef<typeof import('vue')['provide']>
|
||||||
readonly onBeforeUpdate: UnwrapRef<typeof import("vue")["onBeforeUpdate"]>;
|
readonly reactive: UnwrapRef<typeof import('vue')['reactive']>
|
||||||
readonly onDeactivated: UnwrapRef<typeof import("vue")["onDeactivated"]>;
|
readonly readonly: UnwrapRef<typeof import('vue')['readonly']>
|
||||||
readonly onErrorCaptured: UnwrapRef<
|
readonly ref: UnwrapRef<typeof import('vue')['ref']>
|
||||||
typeof import("vue")["onErrorCaptured"]
|
readonly resolveComponent: UnwrapRef<typeof import('vue')['resolveComponent']>
|
||||||
>;
|
readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
|
||||||
readonly onMounted: UnwrapRef<typeof import("vue")["onMounted"]>;
|
readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']>
|
||||||
readonly onRenderTracked: UnwrapRef<
|
readonly shallowRef: UnwrapRef<typeof import('vue')['shallowRef']>
|
||||||
typeof import("vue")["onRenderTracked"]
|
readonly toRaw: UnwrapRef<typeof import('vue')['toRaw']>
|
||||||
>;
|
readonly toRef: UnwrapRef<typeof import('vue')['toRef']>
|
||||||
readonly onRenderTriggered: UnwrapRef<
|
readonly toRefs: UnwrapRef<typeof import('vue')['toRefs']>
|
||||||
typeof import("vue")["onRenderTriggered"]
|
readonly toValue: UnwrapRef<typeof import('vue')['toValue']>
|
||||||
>;
|
readonly triggerRef: UnwrapRef<typeof import('vue')['triggerRef']>
|
||||||
readonly onScopeDispose: UnwrapRef<typeof import("vue")["onScopeDispose"]>;
|
readonly unref: UnwrapRef<typeof import('vue')['unref']>
|
||||||
readonly onServerPrefetch: UnwrapRef<
|
readonly useAttrs: UnwrapRef<typeof import('vue')['useAttrs']>
|
||||||
typeof import("vue")["onServerPrefetch"]
|
readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
|
||||||
>;
|
readonly useCssVars: UnwrapRef<typeof import('vue')['useCssVars']>
|
||||||
readonly onUnmounted: UnwrapRef<typeof import("vue")["onUnmounted"]>;
|
readonly useFullscreen: UnwrapRef<typeof import('@vueuse/core')['useFullscreen']>
|
||||||
readonly onUpdated: UnwrapRef<typeof import("vue")["onUpdated"]>;
|
readonly useId: UnwrapRef<typeof import('vue')['useId']>
|
||||||
readonly provide: UnwrapRef<typeof import("vue")["provide"]>;
|
readonly useModel: UnwrapRef<typeof import('vue')['useModel']>
|
||||||
readonly reactive: UnwrapRef<typeof import("vue")["reactive"]>;
|
readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']>
|
||||||
readonly readonly: UnwrapRef<typeof import("vue")["readonly"]>;
|
readonly useTemplateRef: UnwrapRef<typeof import('vue')['useTemplateRef']>
|
||||||
readonly ref: UnwrapRef<typeof import("vue")["ref"]>;
|
readonly useVModel: UnwrapRef<typeof import('@vueuse/core')['useVModel']>
|
||||||
readonly resolveComponent: UnwrapRef<
|
readonly watch: UnwrapRef<typeof import('vue')['watch']>
|
||||||
typeof import("vue")["resolveComponent"]
|
readonly watchEffect: UnwrapRef<typeof import('vue')['watchEffect']>
|
||||||
>;
|
readonly watchPostEffect: UnwrapRef<typeof import('vue')['watchPostEffect']>
|
||||||
readonly resolveDirective: UnwrapRef<
|
readonly watchSyncEffect: UnwrapRef<typeof import('vue')['watchSyncEffect']>
|
||||||
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"]
|
|
||||||
>;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+61
-57
@@ -1,64 +1,68 @@
|
|||||||
// generated by unplugin-vue-components
|
/* eslint-disable */
|
||||||
// We suggest you to commit this file into source control
|
// @ts-nocheck
|
||||||
|
// biome-ignore lint: disable
|
||||||
|
// oxlint-disable
|
||||||
|
// ------
|
||||||
|
// Generated by unplugin-vue-components
|
||||||
// Read more: https://github.com/vuejs/core/pull/3399
|
// 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 {
|
export interface GlobalComponents {
|
||||||
ElAlert: typeof import("element-plus/es")["ElAlert"];
|
ElAlert: typeof import('element-plus/es')['ElAlert']
|
||||||
ElAvatar: typeof import("element-plus/es")["ElAvatar"];
|
ElAvatar: typeof import('element-plus/es')['ElAvatar']
|
||||||
ElButton: typeof import("element-plus/es")["ElButton"];
|
ElButton: typeof import('element-plus/es')['ElButton']
|
||||||
ElCard: typeof import("element-plus/es")["ElCard"];
|
ElCard: typeof import('element-plus/es')['ElCard']
|
||||||
ElCol: typeof import("element-plus/es")["ElCol"];
|
ElCol: typeof import('element-plus/es')['ElCol']
|
||||||
ElDatePicker: typeof import("element-plus/es")["ElDatePicker"];
|
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
|
||||||
ElDialog: typeof import("element-plus/es")["ElDialog"];
|
ElDialog: typeof import('element-plus/es')['ElDialog']
|
||||||
ElDrawer: typeof import("element-plus/es")["ElDrawer"];
|
ElDrawer: typeof import('element-plus/es')['ElDrawer']
|
||||||
ElDropdown: typeof import("element-plus/es")["ElDropdown"];
|
ElDropdown: typeof import('element-plus/es')['ElDropdown']
|
||||||
ElDropdownItem: typeof import("element-plus/es")["ElDropdownItem"];
|
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
|
||||||
ElDropdownMenu: typeof import("element-plus/es")["ElDropdownMenu"];
|
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
|
||||||
ElEmpty: typeof import("element-plus/es")["ElEmpty"];
|
ElEmpty: typeof import('element-plus/es')['ElEmpty']
|
||||||
ElForm: typeof import("element-plus/es")["ElForm"];
|
ElForm: typeof import('element-plus/es')['ElForm']
|
||||||
ElFormItem: typeof import("element-plus/es")["ElFormItem"];
|
ElFormItem: typeof import('element-plus/es')['ElFormItem']
|
||||||
ElInput: typeof import("element-plus/es")["ElInput"];
|
ElInput: typeof import('element-plus/es')['ElInput']
|
||||||
ElInputNumber: typeof import("element-plus/es")["ElInputNumber"];
|
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
|
||||||
ElMenu: typeof import("element-plus/es")["ElMenu"];
|
ElMenu: typeof import('element-plus/es')['ElMenu']
|
||||||
ElMenuItem: typeof import("element-plus/es")["ElMenuItem"];
|
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
|
||||||
ElOption: typeof import("element-plus/es")["ElOption"];
|
ElOption: typeof import('element-plus/es')['ElOption']
|
||||||
ElPagination: typeof import("element-plus/es")["ElPagination"];
|
ElPagination: typeof import('element-plus/es')['ElPagination']
|
||||||
ElProgress: typeof import("element-plus/es")["ElProgress"];
|
ElProgress: typeof import('element-plus/es')['ElProgress']
|
||||||
ElRadioButton: typeof import("element-plus/es")["ElRadioButton"];
|
ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
|
||||||
ElRadioGroup: typeof import("element-plus/es")["ElRadioGroup"];
|
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
|
||||||
ElRow: typeof import("element-plus/es")["ElRow"];
|
ElRow: typeof import('element-plus/es')['ElRow']
|
||||||
ElScrollbar: typeof import("element-plus/es")["ElScrollbar"];
|
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
|
||||||
ElSelect: typeof import("element-plus/es")["ElSelect"];
|
ElSelect: typeof import('element-plus/es')['ElSelect']
|
||||||
ElSubMenu: typeof import("element-plus/es")["ElSubMenu"];
|
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
|
||||||
ElSwitch: typeof import("element-plus/es")["ElSwitch"];
|
ElSwitch: typeof import('element-plus/es')['ElSwitch']
|
||||||
ElTable: typeof import("element-plus/es")["ElTable"];
|
ElTable: typeof import('element-plus/es')['ElTable']
|
||||||
ElTableColumn: typeof import("element-plus/es")["ElTableColumn"];
|
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
|
||||||
ElTabPane: typeof import("element-plus/es")["ElTabPane"];
|
ElTabPane: typeof import('element-plus/es')['ElTabPane']
|
||||||
ElTabs: typeof import("element-plus/es")["ElTabs"];
|
ElTabs: typeof import('element-plus/es')['ElTabs']
|
||||||
ElTag: typeof import("element-plus/es")["ElTag"];
|
ElTag: typeof import('element-plus/es')['ElTag']
|
||||||
ElText: typeof import("element-plus/es")["ElText"];
|
ElText: typeof import('element-plus/es')['ElText']
|
||||||
ElTooltip: typeof import("element-plus/es")["ElTooltip"];
|
ElTooltip: typeof import('element-plus/es')['ElTooltip']
|
||||||
ElUpload: typeof import("element-plus/es")["ElUpload"];
|
ElUpload: typeof import('element-plus/es')['ElUpload']
|
||||||
Hamburger: typeof import("./../components/Hamburger/index.vue")["default"];
|
Hamburger: typeof import('./../components/Hamburger/index.vue')['default']
|
||||||
IEpCaretBottom: typeof import("~icons/ep/caret-bottom")["default"];
|
IEpCaretBottom: typeof import('~icons/ep/caret-bottom')['default']
|
||||||
IEpDownload: typeof import("~icons/ep/download")["default"];
|
IEpDownload: typeof import('~icons/ep/download')['default']
|
||||||
IEpMoreFilled: typeof import("~icons/ep/more-filled")["default"];
|
IEpMoreFilled: typeof import('~icons/ep/more-filled')['default']
|
||||||
IEpRefresh: typeof import("~icons/ep/refresh")["default"];
|
IEpRefresh: typeof import('~icons/ep/refresh')['default']
|
||||||
IEpUserFilled: typeof import("~icons/ep/user-filled")["default"];
|
IEpUserFilled: typeof import('~icons/ep/user-filled')['default']
|
||||||
ImputMultiple: typeof import("./../components/ImputMultiple/index.vue")["default"];
|
ImputMultiple: typeof import('./../components/ImputMultiple/index.vue')['default']
|
||||||
LangSelect: typeof import("./../components/LangSelect/index.vue")["default"];
|
LangSelect: typeof import('./../components/LangSelect/index.vue')['default']
|
||||||
MapAdd: typeof import("./../components/MapAdd/index.vue")["default"];
|
MapAdd: typeof import('./../components/MapAdd/index.vue')['default']
|
||||||
Pagination: typeof import("./../components/Pagination/index.vue")["default"];
|
Pagination: typeof import('./../components/Pagination/index.vue')['default']
|
||||||
RouterLink: typeof import("vue-router")["RouterLink"];
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
RouterView: typeof import("vue-router")["RouterView"];
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
SvgIcon: typeof import("./../components/SvgIcon/index.vue")["default"];
|
SvgIcon: typeof import('./../components/SvgIcon/index.vue')['default']
|
||||||
UnitSelect: typeof import("./../components/UnitSelect/index.vue")["default"];
|
UnitSelect: typeof import('./../components/UnitSelect/index.vue')['default']
|
||||||
}
|
}
|
||||||
export interface ComponentCustomProperties {
|
export interface GlobalDirectives {
|
||||||
vLoading: typeof import("element-plus/es")["ElLoadingDirective"];
|
vLoading: typeof import('element-plus/es')['ElLoadingDirective']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+6
-2
@@ -2,8 +2,12 @@
|
|||||||
|
|
||||||
declare module "*.vue" {
|
declare module "*.vue" {
|
||||||
import { DefineComponent } from "vue";
|
import { DefineComponent } from "vue";
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
// `object` вместо `{}`: последний допускает любое ненулевое значение,
|
||||||
const component: DefineComponent<{}, {}, any>;
|
// включая 0 и "", то есть не ограничивает ничего. Правило ban-types, под
|
||||||
|
// которое здесь стояло подавление, в @typescript-eslint 8 удалено и
|
||||||
|
// разделено на точечные — само подавление ссылалось на несуществующее имя.
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const component: DefineComponent<object, object, any>;
|
||||||
export default component;
|
export default component;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,8 +29,8 @@
|
|||||||
risk.severity === 'critical'
|
risk.severity === 'critical'
|
||||||
? 'error'
|
? 'error'
|
||||||
: risk.severity === 'warning'
|
: risk.severity === 'warning'
|
||||||
? 'warning'
|
? 'warning'
|
||||||
: 'info'
|
: 'info'
|
||||||
"
|
"
|
||||||
:closable="risk.dismissible"
|
:closable="risk.dismissible"
|
||||||
class="mb-2"
|
class="mb-2"
|
||||||
@@ -247,7 +247,7 @@ const loadDashboard = async () => {
|
|||||||
securityRisks.value = secRes.data;
|
securityRisks.value = secRes.data;
|
||||||
lastSuccessAt.value = Date.now();
|
lastSuccessAt.value = Date.now();
|
||||||
loadError.value = "";
|
loadError.value = "";
|
||||||
} catch (error) {
|
} catch {
|
||||||
loadError.value = t("dashboard.refreshFailed");
|
loadError.value = t("dashboard.refreshFailed");
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
|||||||
@@ -242,8 +242,8 @@ function message() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__return-home {
|
&__return-home {
|
||||||
display: block;
|
|
||||||
float: left;
|
float: left;
|
||||||
|
display: block;
|
||||||
width: 110px;
|
width: 110px;
|
||||||
height: 36px;
|
height: 36px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
|||||||
@@ -222,10 +222,10 @@ const handleLogin = () => {
|
|||||||
|
|
||||||
.el-input__inner {
|
.el-input__inner {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
caret-color: #fff;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: 0;
|
border: 0;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
caret-color: #fff;
|
|
||||||
|
|
||||||
&:-webkit-autofill {
|
&:-webkit-autofill {
|
||||||
box-shadow: 0 0 0 1000px transparent inset !important;
|
box-shadow: 0 0 0 1000px transparent inset !important;
|
||||||
@@ -237,7 +237,9 @@ const handleLogin = () => {
|
|||||||
&:-webkit-autofill:hover,
|
&:-webkit-autofill:hover,
|
||||||
&:-webkit-autofill:focus,
|
&:-webkit-autofill:focus,
|
||||||
&:-webkit-autofill:active {
|
&:-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;
|
transition-delay: 99999s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,8 +84,8 @@
|
|||||||
scope.row.quotaBytes < 0
|
scope.row.quotaBytes < 0
|
||||||
? undefined
|
? undefined
|
||||||
: trafficPercent(peerRow(scope.row)) >= 90
|
: trafficPercent(peerRow(scope.row)) >= 90
|
||||||
? 'exception'
|
? 'exception'
|
||||||
: undefined
|
: undefined
|
||||||
"
|
"
|
||||||
:show-text="false"
|
:show-text="false"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -58,9 +58,7 @@ export default defineConfig((): UserConfig => {
|
|||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
vue(),
|
vue(),
|
||||||
UnoCSS({
|
UnoCSS({/* options */}),
|
||||||
/* options */
|
|
||||||
}),
|
|
||||||
AutoImport({
|
AutoImport({
|
||||||
// Auto import Vue helpers: ref, reactive, toRef, etc.
|
// Auto import Vue helpers: ref, reactive, toRef, etc.
|
||||||
//
|
//
|
||||||
|
|||||||
Reference in New Issue
Block a user