Русифицировать README и интерфейс HY2XS admin
This commit is contained in:
@@ -43,3 +43,5 @@ const tagsViewStore = useTagsViewStore();
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
@@ -12,27 +12,27 @@ const accountStore = useAccountStore();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const { device } = storeToRefs(appStore); // 设备类型:desktop-宽屏设备 || mobile-窄屏设备
|
||||
const { device } = storeToRefs(appStore); // Тип устройства: desktop — широкий экран, mobile — узкий экран
|
||||
|
||||
/**
|
||||
* 左侧菜单栏显示/隐藏
|
||||
* Показать или скрыть левое меню.
|
||||
*/
|
||||
function toggleSideBar() {
|
||||
appStore.toggleSidebar(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* vueUse 全屏
|
||||
* Полноэкранный режим через VueUse.
|
||||
*/
|
||||
const { isFullscreen, toggle } = useFullscreen();
|
||||
|
||||
/**
|
||||
* 注销
|
||||
* Выход из системы.
|
||||
*/
|
||||
function logout() {
|
||||
ElMessageBox.confirm("确定注销并退出系统吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
ElMessageBox.confirm("Выйти из системы?", "Подтверждение", {
|
||||
confirmButtonText: "Выйти",
|
||||
cancelButtonText: "Отмена",
|
||||
type: "warning",
|
||||
}).then(() => {
|
||||
accountStore
|
||||
@@ -48,9 +48,9 @@ function logout() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 顶部导航栏 -->
|
||||
<!-- Верхняя навигационная панель -->
|
||||
<div class="navbar">
|
||||
<!-- 左侧面包屑 -->
|
||||
<!-- Хлебные крошки слева -->
|
||||
<div class="flex">
|
||||
<hamburger
|
||||
:is-active="appStore.sidebar.opened"
|
||||
@@ -59,25 +59,25 @@ function logout() {
|
||||
<breadcrumb />
|
||||
</div>
|
||||
|
||||
<!-- 右侧导航设置 -->
|
||||
<!-- Настройки навигации справа -->
|
||||
<div class="flex">
|
||||
<!-- 导航栏设置(窄屏隐藏)-->
|
||||
<!-- Настройки навигации, скрытые на узком экране -->
|
||||
<div class="setting-container" v-if="device !== 'mobile'">
|
||||
<!--全屏 -->
|
||||
<!-- Полноэкранный режим -->
|
||||
<div class="setting-item" @click="toggle">
|
||||
<svg-icon
|
||||
:icon-class="isFullscreen ? 'exit-fullscreen' : 'fullscreen'"
|
||||
/>
|
||||
</div>
|
||||
<!-- 布局大小 -->
|
||||
<el-tooltip content="布局大小" effect="dark" placement="bottom">
|
||||
<!-- Размер интерфейса -->
|
||||
<el-tooltip content="Размер интерфейса" effect="dark" placement="bottom">
|
||||
<size-select class="setting-item" />
|
||||
</el-tooltip>
|
||||
<!--语言选择-->
|
||||
<!-- Выбор языка -->
|
||||
<lang-select class="setting-item" />
|
||||
</div>
|
||||
|
||||
<!-- 用户头像 -->
|
||||
<!-- Аватар пользователя -->
|
||||
<el-dropdown trigger="click">
|
||||
<div class="avatar-container">
|
||||
<img src="/src/assets/logo.png" />
|
||||
@@ -138,3 +138,5 @@ function logout() {
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
@@ -5,21 +5,21 @@ import IconEpSunny from "~icons/ep/sunny";
|
||||
import IconEpMoon from "~icons/ep/moon";
|
||||
|
||||
/**
|
||||
* 暗黑模式
|
||||
* Тёмная тема.
|
||||
*/
|
||||
const settingsStore = useSettingsStore();
|
||||
const isDark = useDark();
|
||||
const toggleDark = () => useToggle(isDark);
|
||||
|
||||
/**
|
||||
* 切换布局
|
||||
* Переключение layout.
|
||||
*/
|
||||
function changeLayout(layout: string) {
|
||||
settingsStore.changeSetting({ key: "layout", value: layout });
|
||||
window.document.body.setAttribute("layout", settingsStore.layout);
|
||||
}
|
||||
|
||||
// 主题颜色
|
||||
// Цвета темы
|
||||
const themeColors = ref<string[]>([
|
||||
"#409EFF",
|
||||
"#304156",
|
||||
@@ -30,7 +30,7 @@ const themeColors = ref<string[]>([
|
||||
]);
|
||||
|
||||
/**
|
||||
* 切换主题颜色
|
||||
* Переключение цвета темы.
|
||||
*/
|
||||
function changeThemeColor(color: string) {
|
||||
settingsStore.changeSetting({ key: "themeColor", value: color });
|
||||
@@ -51,8 +51,8 @@ onMounted(() => {
|
||||
|
||||
<template>
|
||||
<div class="settings-container">
|
||||
<h3 class="text-base font-bold">项目配置</h3>
|
||||
<el-divider>主题</el-divider>
|
||||
<h3 class="text-base font-bold">Настройки проекта</h3>
|
||||
<el-divider>Тема</el-divider>
|
||||
|
||||
<div class="flex justify-center" @click.stop>
|
||||
<el-switch
|
||||
@@ -66,23 +66,23 @@ onMounted(() => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<el-divider>界面设置</el-divider>
|
||||
<el-divider>Настройки интерфейса</el-divider>
|
||||
<div class="py-[8px] flex justify-between">
|
||||
<span class="text-xs">开启 Tags-View</span>
|
||||
<span class="text-xs">Включить вкладки</span>
|
||||
<el-switch v-model="settingsStore.tagsView" />
|
||||
</div>
|
||||
|
||||
<div class="py-[8px] flex justify-between">
|
||||
<span class="text-xs">固定 Header</span>
|
||||
<span class="text-xs">Зафиксировать header</span>
|
||||
<el-switch v-model="settingsStore.fixedHeader" />
|
||||
</div>
|
||||
|
||||
<div class="py-[8px] flex justify-between">
|
||||
<span class="text-xs">侧边栏 Logo</span>
|
||||
<span class="text-xs">Логотип в боковом меню</span>
|
||||
<el-switch v-model="settingsStore.sidebarLogo" />
|
||||
</div>
|
||||
|
||||
<el-divider>主题颜色</el-divider>
|
||||
<el-divider>Цвет темы</el-divider>
|
||||
|
||||
<ul class="w-full space-x-2 flex justify-center py-2">
|
||||
<li
|
||||
@@ -163,3 +163,5 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
@@ -35,3 +35,5 @@ function push() {
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
|
||||
@@ -51,3 +51,5 @@ const logo = ref(new URL(`../../../assets/logo.png`, import.meta.url).href);
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import SvgIcon from "@/components/SvgIcon/index.vue";
|
||||
|
||||
const props = defineProps({
|
||||
/**
|
||||
* 路由(eg:level_3_1)
|
||||
* Route, например level_3_1
|
||||
*/
|
||||
item: {
|
||||
type: Object,
|
||||
@@ -16,7 +16,7 @@ const props = defineProps({
|
||||
},
|
||||
|
||||
/**
|
||||
* 父层级完整路由路径(eg:/level/level_3/level_3_1)
|
||||
* Полный path родительского уровня, например /level/level_3/level_3_1
|
||||
*/
|
||||
basePath: {
|
||||
type: String,
|
||||
@@ -24,34 +24,34 @@ const props = defineProps({
|
||||
},
|
||||
});
|
||||
|
||||
const onlyOneChild = ref(); // 临时变量,唯一子路由
|
||||
const onlyOneChild = ref(); // Временная переменная для единственного дочернего route
|
||||
|
||||
/**
|
||||
* 判断当前路由是否只有一个子路由
|
||||
* Проверяет, есть ли у текущего route только один дочерний route
|
||||
*
|
||||
* 1:如果只有一个子路由: 返回 true
|
||||
* 2:如果无子路由 :返回 true
|
||||
* 1: если дочерний route один, возвращает true
|
||||
* 2: если дочерних routes нет, возвращает true
|
||||
*
|
||||
* @param children 子路由数组
|
||||
* @param parent 当前路由
|
||||
* @param children Массив дочерних routes
|
||||
* @param parent Текущий route
|
||||
*/
|
||||
function hasOneShowingChild(children = [], parent: any) {
|
||||
// 需要显示的子路由数组
|
||||
// Массив дочерних routes для отображения
|
||||
const showingChildren = children.filter((item: any) => {
|
||||
if (item.meta?.hidden) {
|
||||
return false; // 过滤不显示的子路由
|
||||
return false; // Фильтрация скрытого дочернего route
|
||||
} else {
|
||||
onlyOneChild.value = item; // 唯一子路由赋值(多个子路由情况 onlyOneChild 变量是用不上的)
|
||||
onlyOneChild.value = item; // Запись единственного дочернего route, при нескольких routes переменная onlyOneChild не используется
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// 1:如果只有一个子路由, 返回 true
|
||||
// 1: если дочерний route один, возвращает true
|
||||
if (showingChildren.length === 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 2:如果无子路由, 复制当前路由信息作为其子路由,满足只拥有一个子路由的条件,所以返回 true
|
||||
// 2: если дочерних routes нет, копирует текущий route как дочерний и возвращает true
|
||||
if (showingChildren.length === 0) {
|
||||
onlyOneChild.value = { ...parent, path: "", noShowingChildren: true };
|
||||
return true;
|
||||
@@ -60,9 +60,9 @@ function hasOneShowingChild(children = [], parent: any) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析路径
|
||||
* Разбор path
|
||||
*
|
||||
* @param routePath 路由路径
|
||||
* @param routePath Route path
|
||||
*/
|
||||
function resolvePath(routePath: string) {
|
||||
if (isExternal(routePath)) {
|
||||
@@ -71,14 +71,14 @@ function resolvePath(routePath: string) {
|
||||
if (isExternal(props.basePath)) {
|
||||
return props.basePath;
|
||||
}
|
||||
// 完整路径 = 父级路径(/level/level_3) + 路由路径
|
||||
const fullPath = path.resolve(props.basePath, routePath); // 相对路径 → 绝对路径
|
||||
// Полный path = parent path (/level/level_3) + route path
|
||||
const fullPath = path.resolve(props.basePath, routePath); // Relative path → absolute path
|
||||
return fullPath;
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<div v-if="!item.meta || !item.meta.hidden">
|
||||
<!-- 只包含一个子路由节点的路由,显示其【唯一子路由】 -->
|
||||
<!-- Route с одним дочерним route показывает этот единственный дочерний route -->
|
||||
<template
|
||||
v-if="
|
||||
hasOneShowingChild(item.children, item) &&
|
||||
@@ -98,7 +98,7 @@ function resolvePath(routePath: string) {
|
||||
</app-link>
|
||||
</template>
|
||||
|
||||
<!-- 包含多个子路由 -->
|
||||
<!-- Содержит несколько дочерних routes -->
|
||||
<el-sub-menu v-else :index="resolvePath(item.path)" teleported>
|
||||
<template #title>
|
||||
<svg-icon
|
||||
@@ -119,3 +119,5 @@ function resolvePath(routePath: string) {
|
||||
</el-sub-menu>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
|
||||
@@ -43,3 +43,5 @@ const route = useRoute();
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
|
||||
@@ -119,3 +119,5 @@ defineExpose({
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
@@ -41,12 +41,12 @@ watch(
|
||||
moveToCurrentTag();
|
||||
},
|
||||
{
|
||||
//初始化立即执行
|
||||
// Выполнить сразу при инициализации
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
const tagMenuVisible = ref(false); // 标签操作菜单显示状态
|
||||
const tagMenuVisible = ref(false); // Состояние отображения меню действий с вкладкой
|
||||
watch(tagMenuVisible, (value) => {
|
||||
if (value) {
|
||||
document.body.addEventListener("click", closeTagMenu);
|
||||
@@ -264,7 +264,7 @@ onMounted(() => {
|
||||
</router-link>
|
||||
</scroll-pane>
|
||||
|
||||
<!-- tag标签操作菜单 -->
|
||||
<!-- Меню действий с вкладкой -->
|
||||
<ul
|
||||
v-show="tagMenuVisible"
|
||||
class="tag-menu"
|
||||
@@ -272,27 +272,27 @@ onMounted(() => {
|
||||
>
|
||||
<li @click="refreshSelectedTag(selectedTag)">
|
||||
<svg-icon icon-class="refresh" />
|
||||
刷新
|
||||
Обновить
|
||||
</li>
|
||||
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">
|
||||
<svg-icon icon-class="close" />
|
||||
关闭
|
||||
Закрыть
|
||||
</li>
|
||||
<li @click="closeOtherTags">
|
||||
<svg-icon icon-class="close_other" />
|
||||
关闭其它
|
||||
Закрыть остальные
|
||||
</li>
|
||||
<li v-if="!isFirstView()" @click="closeLeftTags">
|
||||
<svg-icon icon-class="close_left" />
|
||||
关闭左侧
|
||||
Закрыть слева
|
||||
</li>
|
||||
<li v-if="!isLastView()" @click="closeRightTags">
|
||||
<svg-icon icon-class="close_right" />
|
||||
关闭右侧
|
||||
Закрыть справа
|
||||
</li>
|
||||
<li @click="closeAllTags(selectedTag)">
|
||||
<svg-icon icon-class="close_all" />
|
||||
关闭所有
|
||||
Закрыть все
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -371,3 +371,5 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
@@ -2,3 +2,5 @@ export { default as Navbar } from "./Navbar.vue";
|
||||
export { default as AppMain } from "./AppMain.vue";
|
||||
export { default as Settings } from "./Settings/index.vue";
|
||||
export { default as TagsView } from "./TagsView/index.vue";
|
||||
|
||||
|
||||
|
||||
@@ -11,11 +11,11 @@ import { useSettingsStore } from "@/store/modules/settings";
|
||||
const { width } = useWindowSize();
|
||||
|
||||
/**
|
||||
* 响应式布局容器固定宽度
|
||||
* Фиксированная ширина контейнера адаптивного layout
|
||||
*
|
||||
* 大屏(>=1200px)
|
||||
* 中屏(>=992px)
|
||||
* 小屏(>=768px)
|
||||
* Большой экран(>=1200px)
|
||||
* Средний экран(>=992px)
|
||||
* Малый экран(>=768px)
|
||||
*/
|
||||
const WIDTH = 992;
|
||||
|
||||
@@ -41,7 +41,7 @@ watchEffect(() => {
|
||||
appStore.toggleDevice("desktop");
|
||||
|
||||
if (width.value >= 1200) {
|
||||
//大屏
|
||||
//Большой экран
|
||||
appStore.openSideBar(true);
|
||||
} else {
|
||||
appStore.closeSideBar(true);
|
||||
@@ -56,7 +56,7 @@ function handleOutsideClick() {
|
||||
|
||||
<template>
|
||||
<div :class="classObj" class="app-wrapper">
|
||||
<!-- 手机设备侧边栏打开遮罩层 -->
|
||||
<!-- Overlay открытого бокового меню на мобильных устройствах -->
|
||||
<div
|
||||
v-if="classObj.mobile && classObj.openSidebar"
|
||||
class="drawer-bg"
|
||||
@@ -71,10 +71,10 @@ function handleOutsideClick() {
|
||||
<tags-view v-if="showTagsView" />
|
||||
</div>
|
||||
|
||||
<!--主页面-->
|
||||
<!--Основная страница-->
|
||||
<app-main />
|
||||
|
||||
<!-- 设置面板 -->
|
||||
<!-- Панель настроек -->
|
||||
<RightPanel v-if="showSettings">
|
||||
<settings />
|
||||
</RightPanel>
|
||||
@@ -127,3 +127,5 @@ function handleOutsideClick() {
|
||||
opacity: 0.3;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user