布局显示配置

This commit is contained in:
zhang zhuo 2025-06-07 15:40:45 +08:00
parent b01ce00641
commit eb9f60ca0d
16 changed files with 274 additions and 90 deletions

View File

@ -16,11 +16,18 @@
enabled. Please enable it to continue.</strong>
</noscript>
<script type="text/javascript">
var dark = window.localStorage.getItem('APP_DARK');
if (dark) {
document.documentElement.classList.add("dark")
}
var weak = window.localStorage.getItem('APP_WEAK');
var dark = localStorage.getItem('APP_DARK');
if (dark) {
if (dark == 'dark') {
document.documentElement.classList.add("dark")
}else if (dark == 'follow') {
var systemTheme = window.matchMedia('(prefers-color-scheme: dark)')
if (systemTheme.matches) {
document.documentElement.classList.add("dark")
}
}
}
var weak = localStorage.getItem('APP_WEAK');
if (weak) {
document.documentElement.classList.add("weak")
}

BIN
public/images/dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 448 B

BIN
public/images/follow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 509 B

BIN
public/images/light.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 B

View File

@ -0,0 +1,82 @@
<template>
<div class="pi-dialog" ref="piDialog">
<el-dialog ref="dialog" v-model="dialogVisible" :fullscreen="isFullscreen" v-bind="$attrs" :show-close="false">
<template #header>
<slot name="header">
<span class="el-dialog__title">{{ title }}</span>
</slot>
<div class="pi-dialog__headerbtn">
<button v-if="showFullscreen" aria-label="fullscreen" type="button" @click="setFullscreen">
<el-icon v-if="isFullscreen" class="el-dialog__close"><el-icon-bottom-left /></el-icon>
<el-icon v-else class="el-dialog__close"><el-icon-full-screen /></el-icon>
</button>
<button v-if="showClose" aria-label="close" type="button" @click="closeDialog">
<el-icon class="el-dialog__close"><el-icon-close /></el-icon>
</button>
</div>
</template>
<div v-loading="loading">
<slot></slot>
</div>
<template #footer>
<slot name="footer"></slot>
</template>
</el-dialog>
</div>
</template>
<script setup>
import {ref, onMounted, watch} from "vue";
const props = defineProps({
modelValue: { type: Boolean, default: false },
title: { type: String, default: "" },
showClose: { type: Boolean, default: true },
showFullscreen: { type: Boolean, default: true },
loading: { type: Boolean, default: false }
});
let dialogVisible = ref(false)
let isFullscreen = ref(false)
onMounted(() => {
dialogVisible.value = props.modelValue.value
})
watch(() => props.modelValue, () => {
dialogVisible.value = props.modelValue
if(dialogVisible.value){
isFullscreen.value = false
}
})
//
function closeDialog(){
dialogVisible.value = false
}
//
function setDefault(){
isFullscreen.value = !isFullscreen.value
}
//
function setFullscreen(){
isFullscreen.value = !isFullscreen.value
}
//
function setMinimize(){
isFullscreen.value = !isFullscreen.value
}
</script>
<style lang="scss" scoped>
.pi-dialog__headerbtn {position: absolute;top: var(--el-dialog-padding-primary);right: var(--el-dialog-padding-primary);}
.pi-dialog__headerbtn button {padding: 0;background: transparent;border: none;outline: none;cursor: pointer;font-size: var(--el-message-close-size,16px);margin-left: 15px;color: var(--el-color-info);}
.pi-dialog__headerbtn button:hover .el-dialog__close {color: var(--el-color-primary);}
.pi-dialog:deep(.el-dialog).is-fullscreen {display: flex;flex-direction: column;top:0px !important;left:0px !important;}
.pi-dialog:deep(.el-dialog).is-fullscreen .el-dialog__header {}
.pi-dialog:deep(.el-dialog).is-fullscreen .el-dialog__body {flex:1;overflow: auto;}
.pi-dialog:deep(.el-dialog).is-fullscreen .el-dialog__footer {padding-bottom: 10px;border-top: 1px solid var(--el-border-color-base);}
</style>

View File

@ -11,11 +11,13 @@ export default {
REQUEST_CACHE: false,
//语言
LANG: 'zh-cn',
//主题风格 浅色light | 深色dark | 跟随系统follow
DARK: 'light',
//TokenName
TOKEN_NAME: "Authorization",
//Token前缀注意最后有个空格如不需要需设置空字符串
TOKEN_PREFIX: "Bearer ",
//布局 默认default | 通栏header | 经典menu | 功能坞dock
//布局 分栏column | 通栏header | 经典menu | 功能坞dock
//dock将关闭标签和面包屑栏
APP_LAYOUT: 'header',
APP_LAYOUT: 'column',
}

View File

@ -0,0 +1,11 @@
<template>
</template>
<script setup>
</script>
<style scoped>
</style>

View File

@ -36,8 +36,6 @@ let menu = ref([])
let result = ref([])
let history = ref([])
console.log(searchText)
onMounted(() => {
history.value = tools.data.get("SEARCH_HISTORY") || []
const menuTree = tools.data.get("MENU");

View File

@ -1,10 +1,13 @@
<template>
<el-form ref="form" label-width="120px" label-position="left" style="padding:0 20px;">
<el-divider>{{t('user.thememode')}}</el-divider>
<el-row class="pi-theme" :gutter="20">
<el-col :span="8"><el-image src="/images/light.png" class="pi-pic" :class="{'active': dark == 'light'}" @click="themeClick('light')"/><el-text type="info" class="pi-text">浅色</el-text></el-col>
<el-col :span="8"><el-image src="/images/dark.png" class="pi-pic" :class="{'active': dark == 'dark'}" @click="themeClick('dark')"/><el-text type="info" class="pi-text">深色</el-text></el-col>
<el-col :span="8"><el-image src="/images/follow.png" class="pi-pic" :class="{'active': dark == 'follow'}" @click="themeClick('follow')"/><el-text type="info" class="pi-text">跟随系统</el-text></el-col>
</el-row>
<el-divider></el-divider>
<el-form-item :label="t('user.nightmode')">
<el-switch v-model="dark"></el-switch>
</el-form-item>
<el-form-item :label="$t('user.language')">
<el-form-item :label="t('user.language')">
<el-select v-model="lang">
<el-option label="简体中文" value="zh-cn"></el-option>
<el-option label="English" value="en"></el-option>
@ -20,10 +23,11 @@
<el-divider></el-divider>
<el-form-item :label="t('system.layout')">
<el-select v-model="layout" :placeholder="t('system.pleaseSelect')">
<el-option :label="t('system.default')" value="default"></el-option>
<el-option :label="t('system.multiColumn')" value="column"></el-option>
<el-option :label="t('system.fullWidth')" value="header"></el-option>
<el-option :label="t('system.classic')" value="menu"></el-option>
<el-option :label="t('system.dock')" value="dock"></el-option>
<el-option :label="t('system.float')" value="float"></el-option>
</el-select>
</el-form-item>
<el-form-item :label="t('system.collapseMenu')">
@ -32,7 +36,6 @@
<el-form-item :label="t('system.showLabels')">
<el-switch v-model="layoutTags"></el-switch>
</el-form-item>
<el-divider></el-divider>
</el-form>
</template>
@ -50,10 +53,10 @@ let layout = ref(tools.data.get('APP_LAYOUT') || store.state.global.layout);
let menuIsCollapse = ref(store.state.global.menuIsCollapse);
let layoutTags = ref(store.state.global.layoutTags);
let lang = ref(tools.data.get('APP_LANG') || config.LANG);
let dark = ref(tools.data.get('APP_DARK') || false);
let dark = ref(localStorage.getItem('APP_DARK') || config.DARK,);
let colorList = ref(['#409EFF', '#009688', '#536dfe', '#ff5c93', '#c62f2f', '#fd726d']);
let colorPrimary = ref(tools.data.get('APP_COLOR') || '#409EFF');
let weakMode = ref(tools.data.get('APP_WEAK') || false)
let weakMode = ref(localStorage.getItem('APP_WEAK') || false)
watch(layout, (val) => {
tools.data.set("APP_LAYOUT", val);
@ -68,23 +71,13 @@ watch(layoutTags, () => {
store.commit("TOGGLE_layoutTags")
})
watch(dark, (val) => {
if (val) {
document.documentElement.classList.add("dark")
tools.data.set("APP_DARK", val)
} else {
document.documentElement.classList.remove("dark")
tools.data.remove("APP_DARK")
}
})
watch(weakMode, (val) => {
if (val) {
document.documentElement.classList.add("weak")
tools.data.set("APP_WEAK", val)
localStorage.setItem("APP_WEAK", val)
} else {
document.documentElement.classList.remove("weak")
tools.data.remove("APP_WEAK")
localStorage.removeItem("APP_WEAK")
}
})
@ -107,7 +100,30 @@ watch(colorPrimary, (val) => {
}
tools.data.set("APP_COLOR", val);
})
function themeClick(val) {
if (val == 'dark') {
document.documentElement.classList.add("dark")
localStorage.setItem("APP_DARK", val)
} else if(val == 'light') {
document.documentElement.classList.remove("dark")
localStorage.setItem("APP_DARK", val)
} else {
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)')
if (systemTheme.matches) {
document.documentElement.classList.add("dark")
}else {
document.documentElement.classList.remove("dark")
}
localStorage.setItem("APP_DARK", val)
}
dark.value = val
}
</script>
<style scoped>
<style lang="scss" scoped>
.pi-theme {text-align: center;}
.pi-theme .pi-pic {cursor: pointer;margin-bottom: 6px;border-radius: 8px; box-shadow: 0 2px 8px #0003;}
.pi-theme .pi-pic.active {border: 2px solid var(--el-color-primary); cursor: pointer;}
.pi-theme .pi-text {margin-top: 6px; cursor: pointer;}
</style>

View File

@ -60,19 +60,12 @@
<!-- 经典布局 -->
<template v-else-if="layout=='menu'">
<header class="pi-header">
<div class="pi-header-left">
<div class="logo-bar">
<img class="logo" src="/images/logo.png">
<span>{{ config.APP_NAME }}</span>
</div>
</div>
<div class="pi-header-right">
<userbar></userbar>
</div>
</header>
<section class="pi-wrapper">
<div v-if="!ismobile" :class="menuIsCollapse?'pi-side isCollapse':'pi-side'">
<div class="logo-bar">
<img class="logo" src="/images/logo.png">
<span class="title" v-if="!menuIsCollapse">{{ config.APP_NAME }}</span>
</div>
<div class="pi-side-scroll">
<el-scrollbar>
<el-menu :default-active="active" router :collapse="menuIsCollapse"
@ -90,7 +83,9 @@
</div>
<Side-m v-if="ismobile"></Side-m>
<div class="pi-body el-container">
<Topbar v-if="!ismobile"></Topbar>
<Topbar v-if="!ismobile">
<userbar></userbar>
</Topbar>
<Tags v-if="!ismobile && layoutTags"></Tags>
<div class="pi-main" id="pi-main">
<router-view v-slot="{ Component }">
@ -106,26 +101,62 @@
<!-- 功能坞布局 -->
<template v-else-if="layout=='dock'">
<header class="pi-header">
<div class="pi-header-left">
<section class="pi-dock">
<header class="pi-header" :class="ismobile?'':'pi-pad'">
<div class="pi-header-left">
<div class="logo-bar">
<img class="logo" src="/images/logo.png">
<span>{{ config.APP_NAME }}</span>
</div>
</div>
<div class="pi-header-right">
<div v-if="!ismobile" class="pi-header-menu">
<el-menu mode="horizontal" :default-active="active" router background-color="#222b45"
text-color="#fff" active-text-color="var(--el-color-primary)">
<NavMenu :navMenus="menu"></NavMenu>
</el-menu>
</div>
<Side-m v-if="ismobile"></Side-m>
<userbar></userbar>
</div>
</header>
<section class="pi-wrapper">
<div class="pi-body el-container">
<Tags v-if="!ismobile && layoutTags"></Tags>
<div class="pi-main" id="pi-main" :class="ismobile?'':'pi-pad'">
<router-view v-slot="{ Component }">
<keep-alive :include="store.state.keepAlive.keepLiveRoute">
<component :is="Component" :key="route.fullPath" v-if="store.state.keepAlive.routeShow"/>
</keep-alive>
</router-view>
<iframe-view></iframe-view>
</div>
</div>
</section>
</section>
</template>
<!-- 浮动布局 -->
<template v-else-if="layout=='float'">
<section class="pi-wrapper">
<div v-if="!ismobile" class="pi-side isCollapse">
<div class="logo-bar">
<img class="logo" src="/images/logo.png">
<span>{{ config.APP_NAME }}</span>
</div>
<div class="pi-side-scroll">
<el-scrollbar>
<el-menu :default-active="active" router :collapse="true"
:unique-opened="false">
<NavMenu :navMenus="menu"></NavMenu>
</el-menu>
</el-scrollbar>
</div>
</div>
<div class="pi-header-right">
<div v-if="!ismobile" class="pi-header-menu">
<el-menu mode="horizontal" :default-active="active" router background-color="#222b45"
text-color="#fff" active-text-color="var(--el-color-primary)">
<NavMenu :navMenus="menu"></NavMenu>
</el-menu>
</div>
<Side-m v-if="ismobile"></Side-m>
<userbar></userbar>
</div>
</header>
<section class="pi-wrapper">
<Side-m v-if="ismobile"></Side-m>
<div class="pi-body el-container">
<Topbar v-if="!ismobile">
<userbar></userbar>
</Topbar>
<Tags v-if="!ismobile && layoutTags"></Tags>
<div class="pi-main" id="pi-main">
<router-view v-slot="{ Component }">
@ -139,7 +170,7 @@
</section>
</template>
<!-- 默认布局 -->
<!-- 分栏布局 -->
<template v-else>
<section class="pi-wrapper">
<div v-if="!ismobile" class="pi-side-split">

View File

@ -4,15 +4,17 @@ export default {
webTitle: 'Property Management System',
logout: 'Log out',
cancel: 'Cancel',
primaryColor: 'Primary Color',
primaryColor: 'Primary color',
weakMode: 'Weak Mode',
layout: 'Layout',
pageLayout: 'Page Layout',
pleaseSelect: 'Please Select',
pageLayout: 'Page layout',
pleaseSelect: 'Please select',
default: 'Default',
fullWidth: 'Full-width',
fullWidth: 'Full width',
multiColumn: 'Multi column',
classic: 'Classic',
dock: 'Dock',
dock: 'Horizontal',
float: 'Float',
collapseMenu: 'Collapse Menu',
showLabels: 'Show Labels',
},
@ -35,6 +37,7 @@ export default {
dynamic: 'Dynamic',
info: 'User Info',
settings: 'Settings',
thememode: 'Theme Mode',
nightmode: 'Night Mode',
nightmode_msg: 'Suitable for low light environment,The current night mode is beta',
language: 'Language',

View File

@ -11,8 +11,10 @@ export default {
pleaseSelect: '请选择',
default: '默认',
fullWidth: '通栏',
multiColumn: '分栏',
classic: '经典',
dock: '功能坞',
dock: '横向',
float: '浮动',
collapseMenu: '折叠菜单',
showLabels: '显示标签',
},
@ -35,6 +37,7 @@ export default {
dynamic: '近期动态',
info: '个人信息',
settings: '设置',
thememode: '主题风格',
nightmode: '黑夜模式',
nightmode_msg: '适合光线较弱的环境当前黑暗模式为beta版本',
language: '语言',

View File

@ -9,8 +9,13 @@ import copy from './directives/copy'
import drag from './directives/drag'
import errorHandler from "@/utils/errorHandler";
import piDialog from "@/components/piDialog"
export default {
install(app: App) {
// 注册全局组件
app.component('piDialog', piDialog)
//注册全局指令
app.directive('auth', auth)
app.directive('role', role)

View File

@ -10,6 +10,9 @@ a,button,input,textarea{-webkit-tap-highlight-color:rgba(0,0,0,0);box-sizing: bo
/* 大布局样式 */
.pi {display: flex;flex-flow: column;}
.pi-wrapper {display: flex;flex:1;overflow: auto;}
.pi-wrapper .logo-bar {display: flex;align-items: center;height: 50px;justify-content: center;}
.pi-wrapper .logo-bar .logo {width: 35px;height: 35px;}
.pi-wrapper .logo-bar .title {margin-left: 10px;font-size: 14px;font-weight: bold;display: inline-block;vertical-align: middle;white-space: nowrap;}
/* 全局滚动条样式 */
.scrollable {-webkit-overflow-scrolling: touch;}
@ -37,6 +40,11 @@ a,button,input,textarea{-webkit-tap-highlight-color:rgba(0,0,0,0);box-sizing: bo
.pi-header .user-bar .panel-item:hover {background: rgba(255, 255, 255, 0.1)!important;}
.pi-header .user-bar .user label{color: #fff;}
.pi-dock .pi-header.pi-pad {padding: 0 80px}
.pi-dock .pi-wrapper .pi-body .pi-tags {padding: 0 15px;}
.pi-dock .pi-wrapper .pi-body .pi-tags ul {margin: 0 80px;}
.pi-dock .pi-wrapper .pi-body .pi-main.pi-pad{margin: 0 80px}
/* 左侧菜单 */
.pi-side-split {width:65px;flex-shrink:0;background: #222b45;display: flex;flex-flow: column;}
.pi-side-split-top {height: 49px;}

View File

@ -1,11 +1,23 @@
<script setup lang="ts">
<template>
<div>
<el-button plain @click="showClick">点我</el-button>
<pi-dialog v-model="show" ref="dialog"></pi-dialog>
</div>
</template>
<script setup>
import {getCurrentInstance, ref} from "vue";
const {proxy} = getCurrentInstance()
let show = ref(false)
function showClick() {
show.value = true
}
</script>
<template>
</template>
<style scoped>
</style>

View File

@ -4,7 +4,7 @@
</div>
<div class="login_main">
<div class="login_config">
<el-button :icon="config.dark?'el-icon-sunny':'el-icon-moon'" circle type="info"
<el-button :icon="dark?'el-icon-sunny':'el-icon-moon'" circle type="info"
@click="configDark"></el-button>
<el-dropdown trigger="click" placement="bottom-end" @command="configLang">
<el-button circle>
@ -21,8 +21,8 @@
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-for="item in lang" :key="item.value" :command="item"
:class="{'selected':config.lang==item.value}">{{ item.name }}
<el-dropdown-item v-for="item in langs" :key="item.value" :command="item"
:class="{'selected':lang==item.value}">{{ item.name }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
@ -83,12 +83,10 @@ const {proxy} = getCurrentInstance();
const {t, locale} = useI18n()
const router = useRouter();
let config = ref({
lang: tools.data.get('APP_LANG') || sysConfig.LANG,
dark: tools.data.get('APP_DARK') || false
})
let lang = ref(tools.data.get('APP_LANG') || sysConfig.LANG)
let dark = ref(false)
const lang = ref([
const langs = ref([
{
name: '简体中文',
value: 'zh-cn',
@ -120,23 +118,24 @@ const rules = ref({
})
let isLogin = ref(false);
const darkConfig = localStorage.getItem('APP_DARK') || sysConfig.DARK
if (darkConfig == "dark") {
dark.value = true
}else if (darkConfig == 'follow') {
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)')
if (systemTheme.matches) {
dark.value = true
}
}
// mounted
onMounted(() => {
getCode()
rememberMe()
})
watch(() => config.value.dark, (val) => {
if (val) {
document.documentElement.classList.add("dark")
tools.data.set("APP_DARK", val)
} else {
document.documentElement.classList.remove("dark")
tools.data.remove("APP_DARK")
}
})
watch(() => config.value.lang, (val) => {
watch(lang, (val) => {
locale.value = val
tools.data.set("APP_LANG", val)
})
@ -201,10 +200,17 @@ async function getInfo() {
}
function configDark() {
config.value.dark = !config.value.dark
dark.value = !dark.value
if (dark.value) {
document.documentElement.classList.add("dark")
localStorage.setItem("APP_DARK", 'dark')
} else {
document.documentElement.classList.remove("dark")
localStorage.setItem("APP_DARK", 'light')
}
}
function configLang(command) {
config.value.lang = command.value
lang.value = command.value
}
</script>