!64 版本升级
* Merge branch 'dev' of gitee.com:JavaLionLi/plus-ui into ts * 升级依赖 * !61 fix: 删除重复环境变量ElUploadInstance * fix: 删除重复环境变量ElUploadInstance
This commit is contained in:
@ -3,7 +3,7 @@
|
||||
<router-view v-slot="{ Component, route }">
|
||||
<transition :enter-active-class="animante" mode="out-in">
|
||||
<keep-alive :include="tagsViewStore.cachedViews">
|
||||
<component v-if="!route.meta.link" :is="Component" :key="route.path" />
|
||||
<component :is="Component" v-if="!route.meta.link" :key="route.path" />
|
||||
</keep-alive>
|
||||
</transition>
|
||||
</router-view>
|
||||
@ -14,22 +14,25 @@
|
||||
<script setup name="AppMain" lang="ts">
|
||||
import useTagsViewStore from '@/store/modules/tagsView';
|
||||
import useSettingsStore from '@/store/modules/settings';
|
||||
import IframeToggle from './IframeToggle/index.vue'
|
||||
import { ComponentInternalInstance } from "vue";
|
||||
import IframeToggle from './IframeToggle/index.vue';
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const tagsViewStore = useTagsViewStore();
|
||||
|
||||
// 随机动画集合
|
||||
const animante = ref<string>('');
|
||||
const animationEnable = ref(useSettingsStore().animationEnable);
|
||||
watch(()=> useSettingsStore().animationEnable, (val) => {
|
||||
watch(
|
||||
() => useSettingsStore().animationEnable,
|
||||
(val) => {
|
||||
animationEnable.value = val;
|
||||
if (val) {
|
||||
animante.value = proxy?.animate.animateList[Math.round(Math.random() * proxy?.animate.animateList.length)] as string;
|
||||
animante.value = proxy?.animate.animateList[Math.round(Math.random() * proxy?.animate.animateList.length)] as string;
|
||||
} else {
|
||||
animante.value = proxy?.animate.defaultAnimate as string;
|
||||
animante.value = proxy?.animate.defaultAnimate as string;
|
||||
}
|
||||
}, { immediate: true });
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@ -41,7 +44,7 @@ watch(()=> useSettingsStore().animationEnable, (val) => {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.fixed-header+.app-main {
|
||||
.fixed-header + .app-main {
|
||||
padding-top: 50px;
|
||||
}
|
||||
|
||||
@ -51,7 +54,7 @@ watch(()=> useSettingsStore().animationEnable, (val) => {
|
||||
min-height: calc(100vh - 84px);
|
||||
}
|
||||
|
||||
.fixed-header+.app-main {
|
||||
.fixed-header + .app-main {
|
||||
padding-top: 84px;
|
||||
}
|
||||
}
|
||||
|
@ -2,16 +2,16 @@
|
||||
<transition-group name="fade-transform" mode="out-in">
|
||||
<inner-link
|
||||
v-for="(item, index) in tagsViewStore.iframeViews"
|
||||
:key="item.path"
|
||||
:iframeId="'iframe' + index"
|
||||
v-show="route.path === item.path"
|
||||
:key="item.path"
|
||||
:iframe-id="'iframe' + index"
|
||||
:src="iframeUrl(item.meta ? item.meta.link : '', item.query)"
|
||||
></inner-link>
|
||||
</transition-group>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import InnerLink from "../InnerLink/index.vue";
|
||||
import InnerLink from '../InnerLink/index.vue';
|
||||
import useTagsViewStore from '@/store/modules/tagsView';
|
||||
|
||||
const route = useRoute();
|
||||
@ -19,8 +19,10 @@ const tagsViewStore = useTagsViewStore();
|
||||
|
||||
function iframeUrl(url: string, query: any) {
|
||||
if (Object.keys(query).length > 0) {
|
||||
let params = Object.keys(query).map((key) => key + "=" + query[key]).join("&");
|
||||
return url + "?" + params;
|
||||
let params = Object.keys(query)
|
||||
.map((key) => key + '=' + query[key])
|
||||
.join('&');
|
||||
return url + '?' + params;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
@ -5,14 +5,11 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { propTypes } from '@/utils/propTypes';
|
||||
|
||||
const props = defineProps({
|
||||
src: {
|
||||
type: String,
|
||||
default: "/"
|
||||
},
|
||||
iframeId: {
|
||||
type: String
|
||||
}
|
||||
src: propTypes.string.def('/'),
|
||||
iframeId: propTypes.string.isRequired
|
||||
});
|
||||
const height = ref(document.documentElement.clientHeight - 94.5 + "px");
|
||||
const height = ref(document.documentElement.clientHeight - 94.5 + 'px');
|
||||
</script>
|
||||
|
@ -1,18 +1,18 @@
|
||||
<template>
|
||||
<div class="navbar">
|
||||
<hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
|
||||
<breadcrumb id="breadcrumb-container" class="breadcrumb-container" v-if="!settingsStore.topNav" />
|
||||
<top-nav id="topmenu-container" class="topmenu-container" v-if="settingsStore.topNav" />
|
||||
<hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container" @toggle-click="toggleSideBar" />
|
||||
<breadcrumb v-if="!settingsStore.topNav" id="breadcrumb-container" class="breadcrumb-container" />
|
||||
<top-nav v-if="settingsStore.topNav" id="topmenu-container" class="topmenu-container" />
|
||||
|
||||
<div class="right-menu flex align-center">
|
||||
<template v-if="appStore.device !== 'mobile'">
|
||||
<el-select
|
||||
v-if="userId === 1 && tenantEnabled"
|
||||
v-model="companyName"
|
||||
clearable
|
||||
filterable
|
||||
reserve-keyword
|
||||
:placeholder="$t('navbar.selectTenant')"
|
||||
v-if="userId === 1 && tenantEnabled"
|
||||
@change="dynamicTenantEvent"
|
||||
@clear="dynamicClearEvent"
|
||||
>
|
||||
@ -63,17 +63,17 @@
|
||||
</el-tooltip>
|
||||
</template>
|
||||
<div class="avatar-container">
|
||||
<el-dropdown @command="handleCommand" class="right-menu-item hover-effect" trigger="click">
|
||||
<el-dropdown class="right-menu-item hover-effect" trigger="click" @command="handleCommand">
|
||||
<div class="avatar-wrapper">
|
||||
<img :src="userStore.avatar" class="user-avatar" />
|
||||
<el-icon><caret-bottom /></el-icon>
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<router-link to="/user/profile" v-if="!dynamic">
|
||||
<router-link v-if="!dynamic" to="/user/profile">
|
||||
<el-dropdown-item>{{ $t('navbar.personalCenter') }}</el-dropdown-item>
|
||||
</router-link>
|
||||
<el-dropdown-item command="setLayout" v-if="settingsStore.showSettings">
|
||||
<el-dropdown-item v-if="settingsStore.showSettings" command="setLayout">
|
||||
<span>{{ $t('navbar.layoutSetting') }}</span>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item divided command="logout">
|
||||
@ -92,10 +92,9 @@ import SearchMenu from './TopBar/search.vue';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
import useUserStore from '@/store/modules/user';
|
||||
import useSettingsStore from '@/store/modules/settings';
|
||||
import { getTenantList } from "@/api/login";
|
||||
import { dynamicClear, dynamicTenant } from "@/api/system/tenant";
|
||||
import { ComponentInternalInstance } from "vue";
|
||||
import { TenantVO } from "@/api/types";
|
||||
import { getTenantList } from '@/api/login';
|
||||
import { dynamicClear, dynamicTenant } from '@/api/system/tenant';
|
||||
import { TenantVO } from '@/api/types';
|
||||
import notice from './notice/index.vue';
|
||||
import useNoticeStore from '@/store/modules/notice';
|
||||
|
||||
@ -119,7 +118,7 @@ const searchMenuRef = ref<InstanceType<typeof SearchMenu>>();
|
||||
|
||||
const openSearchMenu = () => {
|
||||
searchMenuRef.value?.openSearch();
|
||||
}
|
||||
};
|
||||
|
||||
// 动态切换
|
||||
const dynamicTenantEvent = async (tenantId: string) => {
|
||||
@ -129,14 +128,14 @@ const dynamicTenantEvent = async (tenantId: string) => {
|
||||
proxy?.$tab.closeAllPage();
|
||||
proxy?.$router.push('/');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const dynamicClearEvent = async () => {
|
||||
await dynamicClear();
|
||||
dynamic.value = false;
|
||||
proxy?.$tab.closeAllPage();
|
||||
proxy?.$router.push('/');
|
||||
}
|
||||
};
|
||||
|
||||
/** 租户列表 */
|
||||
const initTenantList = async () => {
|
||||
@ -145,56 +144,58 @@ const initTenantList = async () => {
|
||||
if (tenantEnabled.value) {
|
||||
tenantList.value = data.voList;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
initTenantList,
|
||||
})
|
||||
initTenantList
|
||||
});
|
||||
|
||||
const toggleSideBar = () => {
|
||||
appStore.toggleSideBar(false);
|
||||
}
|
||||
};
|
||||
|
||||
const logout = async () => {
|
||||
await ElMessageBox.confirm('确定注销并退出系统吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
await userStore.logout()
|
||||
location.href = import.meta.env.VITE_APP_CONTEXT_PATH + 'index';
|
||||
}
|
||||
await ElMessageBox.confirm('确定注销并退出系统吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
});
|
||||
await userStore.logout();
|
||||
location.href = import.meta.env.VITE_APP_CONTEXT_PATH + 'index';
|
||||
};
|
||||
|
||||
const emits = defineEmits(['setLayout'])
|
||||
const emits = defineEmits(['setLayout']);
|
||||
const setLayout = () => {
|
||||
emits('setLayout');
|
||||
}
|
||||
emits('setLayout');
|
||||
};
|
||||
// 定义Command方法对象 通过key直接调用方法
|
||||
const commandMap: {[key: string]: any} = {
|
||||
setLayout,
|
||||
logout
|
||||
const commandMap: { [key: string]: any } = {
|
||||
setLayout,
|
||||
logout
|
||||
};
|
||||
const handleCommand = (command: string) => {
|
||||
// 判断是否存在该方法
|
||||
if (commandMap[command]) {
|
||||
commandMap[command]();
|
||||
}
|
||||
}
|
||||
|
||||
// 判断是否存在该方法
|
||||
if (commandMap[command]) {
|
||||
commandMap[command]();
|
||||
}
|
||||
};
|
||||
//用深度监听 消息
|
||||
watch(() => noticeStore.state.value.notices, (newVal, oldVal) => {
|
||||
newNotice.value = newVal.filter((item: any) => !item.read).length;
|
||||
}, { deep: true });
|
||||
watch(
|
||||
() => noticeStore.state.value.notices,
|
||||
(newVal) => {
|
||||
newNotice.value = newVal.filter((item: any) => !item.read).length;
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
:deep(.el-select .el-input__wrapper) {
|
||||
height:30px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
:deep(.el-badge__content.is-fixed){
|
||||
top: 12px;
|
||||
:deep(.el-badge__content.is-fixed) {
|
||||
top: 12px;
|
||||
}
|
||||
|
||||
.flex {
|
||||
|
@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<el-drawer v-model="showSettings" :withHeader="false" direction="rtl" size="300px" close-on-click-modal>
|
||||
<el-drawer v-model="showSettings" :with-header="false" direction="rtl" size="300px" close-on-click-modal>
|
||||
<h3 class="drawer-title">主题风格设置</h3>
|
||||
|
||||
<div class="setting-drawer-block-checbox">
|
||||
<div class="setting-drawer-block-checbox-item" @click="handleTheme(SideThemeEnum.DARK)">
|
||||
<img src="@/assets/images/dark.svg" alt="dark" />
|
||||
<div v-if="sideTheme === 'theme-dark'" class="setting-drawer-block-checbox-selectIcon" style="display: block;">
|
||||
<div v-if="sideTheme === 'theme-dark'" class="setting-drawer-block-checbox-selectIcon" style="display: block">
|
||||
<i aria-label="图标: check" class="anticon anticon-check">
|
||||
<svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true" focusable="false" class>
|
||||
<path
|
||||
@ -17,7 +17,7 @@
|
||||
</div>
|
||||
<div class="setting-drawer-block-checbox-item" @click="handleTheme(SideThemeEnum.LIGHT)">
|
||||
<img src="@/assets/images/light.svg" alt="light" />
|
||||
<div v-if="sideTheme === 'theme-light'" class="setting-drawer-block-checbox-selectIcon" style="display: block;">
|
||||
<div v-if="sideTheme === 'theme-light'" class="setting-drawer-block-checbox-selectIcon" style="display: block">
|
||||
<i aria-label="图标: check" class="anticon anticon-check">
|
||||
<svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true" focusable="false" class>
|
||||
<path
|
||||
@ -37,7 +37,7 @@
|
||||
<div class="drawer-item">
|
||||
<span>深色模式</span>
|
||||
<span class="comp-style">
|
||||
<el-switch v-model="isDark" @change="toggleDark" class="drawer-switch" />
|
||||
<el-switch v-model="isDark" class="drawer-switch" @change="toggleDark" />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@ -88,126 +88,126 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useDynamicTitle } from '@/utils/dynamicTitle'
|
||||
import useAppStore from '@/store/modules/app'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import usePermissionStore from '@/store/modules/permission'
|
||||
import { handleThemeStyle } from '@/utils/theme'
|
||||
import { ComponentInternalInstance } from "vue";
|
||||
import { SettingTypeEnum } from "@/enums/SettingTypeEnum";
|
||||
import { SideThemeEnum } from "@/enums/SideThemeEnum";
|
||||
import { useDynamicTitle } from '@/utils/dynamicTitle';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
import useSettingsStore from '@/store/modules/settings';
|
||||
import usePermissionStore from '@/store/modules/permission';
|
||||
import { handleThemeStyle } from '@/utils/theme';
|
||||
import { SettingTypeEnum } from '@/enums/SettingTypeEnum';
|
||||
import { SideThemeEnum } from '@/enums/SideThemeEnum';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const appStore = useAppStore()
|
||||
const settingsStore = useSettingsStore()
|
||||
const permissionStore = usePermissionStore()
|
||||
|
||||
const appStore = useAppStore();
|
||||
const settingsStore = useSettingsStore();
|
||||
const permissionStore = usePermissionStore();
|
||||
|
||||
const showSettings = ref(false);
|
||||
const theme = ref(settingsStore.theme);
|
||||
const sideTheme = ref(settingsStore.sideTheme);
|
||||
const storeSettings = computed(() => settingsStore);
|
||||
const predefineColors = ref(["#409EFF", "#ff4500", "#ff8c00", "#ffd700", "#90ee90", "#00ced1", "#1e90ff", "#c71585"]);
|
||||
const predefineColors = ref(['#409EFF', '#ff4500', '#ff8c00', '#ffd700', '#90ee90', '#00ced1', '#1e90ff', '#c71585']);
|
||||
|
||||
// 是否暗黑模式
|
||||
const isDark = useDark({
|
||||
storageKey: 'useDarkKey',
|
||||
valueDark: 'dark',
|
||||
valueLight: 'light',
|
||||
valueLight: 'light'
|
||||
});
|
||||
watch(isDark, ()=> {
|
||||
watch(isDark, () => {
|
||||
if (isDark.value) {
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: SideThemeEnum.DARK })
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: SideThemeEnum.DARK });
|
||||
} else {
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: sideTheme.value })
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: sideTheme.value });
|
||||
}
|
||||
})
|
||||
});
|
||||
const toggleDark = () => useToggle(isDark);
|
||||
|
||||
/** 是否需要topNav */
|
||||
const topNav = computed({
|
||||
get: () => storeSettings.value.topNav,
|
||||
set: (val) => {
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.TOP_NAV, value: val })
|
||||
if (!val) {
|
||||
appStore.toggleSideBarHide(false);
|
||||
permissionStore.setSidebarRouters(permissionStore.defaultRoutes);
|
||||
}
|
||||
get: () => storeSettings.value.topNav,
|
||||
set: (val) => {
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.TOP_NAV, value: val });
|
||||
if (!val) {
|
||||
appStore.toggleSideBarHide(false);
|
||||
permissionStore.setSidebarRouters(permissionStore.defaultRoutes);
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
/** 是否需要tagview */
|
||||
const tagsView = computed({
|
||||
get: () => storeSettings.value.tagsView,
|
||||
set: (val) => {
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.TAGS_VIEW, value: val })
|
||||
}
|
||||
})
|
||||
get: () => storeSettings.value.tagsView,
|
||||
set: (val) => {
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.TAGS_VIEW, value: val });
|
||||
}
|
||||
});
|
||||
/**是否需要固定头部 */
|
||||
const fixedHeader = computed({
|
||||
get: () => storeSettings.value.fixedHeader,
|
||||
set: (val) => {
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.FIXED_HEADER, value: val })
|
||||
}
|
||||
})
|
||||
get: () => storeSettings.value.fixedHeader,
|
||||
set: (val) => {
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.FIXED_HEADER, value: val });
|
||||
}
|
||||
});
|
||||
/**是否需要侧边栏的logo */
|
||||
const sidebarLogo = computed({
|
||||
get: () => storeSettings.value.sidebarLogo,
|
||||
set: (val) => {
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.SIDEBAR_LOGO, value: val })
|
||||
}
|
||||
})
|
||||
get: () => storeSettings.value.sidebarLogo,
|
||||
set: (val) => {
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.SIDEBAR_LOGO, value: val });
|
||||
}
|
||||
});
|
||||
/**是否需要侧边栏的动态网页的title */
|
||||
const dynamicTitle = computed({
|
||||
get: () => storeSettings.value.dynamicTitle,
|
||||
set: (val) => {
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.DYNAMIC_TITLE, value: val })
|
||||
// 动态设置网页标题
|
||||
useDynamicTitle()
|
||||
}
|
||||
})
|
||||
get: () => storeSettings.value.dynamicTitle,
|
||||
set: (val) => {
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.DYNAMIC_TITLE, value: val });
|
||||
// 动态设置网页标题
|
||||
useDynamicTitle();
|
||||
}
|
||||
});
|
||||
|
||||
const themeChange = (val: string | null) => {
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.THEME, value: val })
|
||||
theme.value = val;
|
||||
if (val) {
|
||||
handleThemeStyle(val);
|
||||
}
|
||||
}
|
||||
const themeChange = (val: string) => {
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.THEME, value: val });
|
||||
theme.value = val;
|
||||
if (val) {
|
||||
handleThemeStyle(val);
|
||||
}
|
||||
};
|
||||
const handleTheme = (val: string) => {
|
||||
sideTheme.value = val;
|
||||
if (isDark.value && val === SideThemeEnum.LIGHT) {
|
||||
// 暗黑模式颜色不变
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: SideThemeEnum.DARK })
|
||||
return
|
||||
}
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: val })
|
||||
}
|
||||
sideTheme.value = val;
|
||||
if (isDark.value && val === SideThemeEnum.LIGHT) {
|
||||
// 暗黑模式颜色不变
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: SideThemeEnum.DARK });
|
||||
return;
|
||||
}
|
||||
settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: val });
|
||||
};
|
||||
const saveSetting = () => {
|
||||
proxy?.$modal.loading("正在保存到本地,请稍候...");
|
||||
let layoutSetting = {
|
||||
"topNav": storeSettings.value.topNav,
|
||||
"tagsView": storeSettings.value.tagsView,
|
||||
"fixedHeader": storeSettings.value.fixedHeader,
|
||||
"sidebarLogo": storeSettings.value.sidebarLogo,
|
||||
"dynamicTitle": storeSettings.value.dynamicTitle,
|
||||
"sideTheme": storeSettings.value.sideTheme,
|
||||
"theme": storeSettings.value.theme
|
||||
};
|
||||
localStorage.setItem("layout-setting", JSON.stringify(layoutSetting));
|
||||
setTimeout(() => {proxy?.$modal.closeLoading()}, 1000)
|
||||
}
|
||||
proxy?.$modal.loading('正在保存到本地,请稍候...');
|
||||
let layoutSetting = {
|
||||
topNav: storeSettings.value.topNav,
|
||||
tagsView: storeSettings.value.tagsView,
|
||||
fixedHeader: storeSettings.value.fixedHeader,
|
||||
sidebarLogo: storeSettings.value.sidebarLogo,
|
||||
dynamicTitle: storeSettings.value.dynamicTitle,
|
||||
sideTheme: storeSettings.value.sideTheme,
|
||||
theme: storeSettings.value.theme
|
||||
};
|
||||
localStorage.setItem('layout-setting', JSON.stringify(layoutSetting));
|
||||
setTimeout(() => {
|
||||
proxy?.$modal.closeLoading();
|
||||
}, 1000);
|
||||
};
|
||||
const resetSetting = () => {
|
||||
proxy?.$modal.loading("正在清除设置缓存并刷新,请稍候...");
|
||||
localStorage.removeItem("layout-setting")
|
||||
setTimeout("window.location.reload()", 1000)
|
||||
}
|
||||
proxy?.$modal.loading('正在清除设置缓存并刷新,请稍候...');
|
||||
localStorage.removeItem('layout-setting');
|
||||
setTimeout('window.location.reload()', 1000);
|
||||
};
|
||||
const openSetting = () => {
|
||||
showSettings.value = true;
|
||||
}
|
||||
showSettings.value = true;
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
openSetting,
|
||||
})
|
||||
openSetting
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -5,36 +5,36 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { isExternal } from '@/utils/validate'
|
||||
import { isExternal } from '@/utils/validate';
|
||||
|
||||
const props = defineProps({
|
||||
to: {
|
||||
type: [String, Object],
|
||||
required: true
|
||||
}
|
||||
})
|
||||
to: {
|
||||
type: [String, Object],
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const isExt = computed(() => {
|
||||
return isExternal(props.to as string)
|
||||
})
|
||||
return isExternal(props.to as string);
|
||||
});
|
||||
|
||||
const type = computed(() => {
|
||||
if (isExt.value) {
|
||||
return 'a'
|
||||
}
|
||||
return 'router-link'
|
||||
})
|
||||
if (isExt.value) {
|
||||
return 'a';
|
||||
}
|
||||
return 'router-link';
|
||||
});
|
||||
|
||||
function linkProps() {
|
||||
if (isExt.value) {
|
||||
return {
|
||||
href: props.to,
|
||||
target: '_blank',
|
||||
rel: 'noopener'
|
||||
}
|
||||
}
|
||||
if (isExt.value) {
|
||||
return {
|
||||
to: props.to
|
||||
}
|
||||
href: props.to,
|
||||
target: '_blank',
|
||||
rel: 'noopener'
|
||||
};
|
||||
}
|
||||
return {
|
||||
to: props.to
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div
|
||||
class="sidebar-logo-container"
|
||||
:class="{ 'collapse': collapse }"
|
||||
:class="{ collapse: collapse }"
|
||||
:style="{ backgroundColor: sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }"
|
||||
>
|
||||
<transition :enter-active-class="proxy?.animate.logoAnimate.enter" mode="out-in">
|
||||
@ -22,18 +22,17 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import variables from '@/assets/styles/variables.module.scss'
|
||||
import logo from '@/assets/logo/logo.png'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import { ComponentInternalInstance } from "vue";
|
||||
import variables from '@/assets/styles/variables.module.scss';
|
||||
import logo from '@/assets/logo/logo.png';
|
||||
import useSettingsStore from '@/store/modules/settings';
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
defineProps({
|
||||
collapse: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
collapse: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const title = ref('RuoYi-Vue-Plus');
|
||||
const settingsStore = useSettingsStore();
|
||||
@ -77,7 +76,12 @@ const sideTheme = computed(() => settingsStore.sideTheme);
|
||||
font-weight: 600;
|
||||
line-height: 50px;
|
||||
font-size: 14px;
|
||||
font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
|
||||
font-family:
|
||||
Avenir,
|
||||
Helvetica Neue,
|
||||
Arial,
|
||||
Helvetica,
|
||||
sans-serif;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
<el-sub-menu v-else ref="subMenu" :index="resolvePath(item.path)" teleported>
|
||||
<template v-if="item.meta" #title>
|
||||
<svg-icon :icon-class="item.meta ? item.meta.icon : '' " />
|
||||
<svg-icon :icon-class="item.meta ? item.meta.icon : ''" />
|
||||
<span class="menu-title" :title="hasTitle(item.meta?.title)">{{ item.meta?.title }}</span>
|
||||
</template>
|
||||
|
||||
@ -30,79 +30,75 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { isExternal } from '@/utils/validate'
|
||||
import AppLink from './Link.vue'
|
||||
import { getNormalPath } from '@/utils/ruoyi'
|
||||
import { RouteOption } from "vue-router";
|
||||
import { isExternal } from '@/utils/validate';
|
||||
import AppLink from './Link.vue';
|
||||
import { getNormalPath } from '@/utils/ruoyi';
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
const props = defineProps({
|
||||
// route object
|
||||
item: {
|
||||
type: Object as PropType<RouteOption>,
|
||||
required: true
|
||||
},
|
||||
isNest: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
basePath: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
// route object
|
||||
item: {
|
||||
type: Object as PropType<RouteRecordRaw>,
|
||||
required: true
|
||||
},
|
||||
isNest: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
basePath: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
|
||||
const onlyOneChild = ref<any>({});
|
||||
|
||||
const hasOneShowingChild = (parent: RouteOption, children?:RouteOption[]) => {
|
||||
if (!children) {
|
||||
children = [];
|
||||
const hasOneShowingChild = (parent: RouteRecordRaw, children?: RouteRecordRaw[]) => {
|
||||
if (!children) {
|
||||
children = [];
|
||||
}
|
||||
const showingChildren = children.filter((item) => {
|
||||
if (item.hidden) {
|
||||
return false;
|
||||
} else {
|
||||
// Temp set(will be used if only has one showing child)
|
||||
onlyOneChild.value = item;
|
||||
return true;
|
||||
}
|
||||
const showingChildren = children.filter(item => {
|
||||
if (item.hidden) {
|
||||
return false
|
||||
} else {
|
||||
// Temp set(will be used if only has one showing child)
|
||||
onlyOneChild.value = item
|
||||
return true
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// When there is only one child router, the child router is displayed by default
|
||||
if (showingChildren.length === 1) {
|
||||
return true
|
||||
}
|
||||
// When there is only one child router, the child router is displayed by default
|
||||
if (showingChildren.length === 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Show parent if there are no child router to display
|
||||
if (showingChildren.length === 0) {
|
||||
onlyOneChild.value = { ...parent, path: '', noShowingChildren: true }
|
||||
if (parent.name === '2222') {
|
||||
console.log(onlyOneChild.value)
|
||||
}
|
||||
return true
|
||||
}
|
||||
// Show parent if there are no child router to display
|
||||
if (showingChildren.length === 0) {
|
||||
onlyOneChild.value = { ...parent, path: '', noShowingChildren: true };
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false
|
||||
return false;
|
||||
};
|
||||
|
||||
const resolvePath = (routePath:string, routeQuery?:string): any => {
|
||||
if (isExternal(routePath)) {
|
||||
return routePath
|
||||
}
|
||||
if (isExternal(props.basePath)) {
|
||||
return props.basePath
|
||||
}
|
||||
if (routeQuery) {
|
||||
let query = JSON.parse(routeQuery);
|
||||
return { path: getNormalPath(props.basePath + '/' + routePath), query: query }
|
||||
}
|
||||
return getNormalPath(props.basePath + '/' + routePath)
|
||||
}
|
||||
const resolvePath = (routePath: string, routeQuery?: string): any => {
|
||||
if (isExternal(routePath)) {
|
||||
return routePath;
|
||||
}
|
||||
if (isExternal(props.basePath)) {
|
||||
return props.basePath;
|
||||
}
|
||||
if (routeQuery) {
|
||||
let query = JSON.parse(routeQuery);
|
||||
return { path: getNormalPath(props.basePath + '/' + routePath), query: query };
|
||||
}
|
||||
return getNormalPath(props.basePath + '/' + routePath);
|
||||
};
|
||||
|
||||
const hasTitle = (title: string | undefined): string => {
|
||||
if(!title || title.length <= 5) {
|
||||
return "";
|
||||
}
|
||||
return title;
|
||||
}
|
||||
if (!title || title.length <= 5) {
|
||||
return '';
|
||||
}
|
||||
return title;
|
||||
};
|
||||
</script>
|
||||
|
@ -21,35 +21,34 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Logo from './Logo.vue'
|
||||
import SidebarItem from './SidebarItem.vue'
|
||||
import variables from '@/assets/styles/variables.module.scss'
|
||||
import useAppStore from '@/store/modules/app'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import usePermissionStore from '@/store/modules/permission'
|
||||
import { RouteOption } from "vue-router";
|
||||
import Logo from './Logo.vue';
|
||||
import SidebarItem from './SidebarItem.vue';
|
||||
import variables from '@/assets/styles/variables.module.scss';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
import useSettingsStore from '@/store/modules/settings';
|
||||
import usePermissionStore from '@/store/modules/permission';
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
|
||||
const route = useRoute();
|
||||
const appStore = useAppStore()
|
||||
const settingsStore = useSettingsStore()
|
||||
const permissionStore = usePermissionStore()
|
||||
|
||||
const sidebarRouters = computed<RouteOption[]>(() => permissionStore.sidebarRouters);
|
||||
const appStore = useAppStore();
|
||||
const settingsStore = useSettingsStore();
|
||||
const permissionStore = usePermissionStore();
|
||||
const sidebarRouters = computed<RouteRecordRaw[]>(() => permissionStore.sidebarRouters);
|
||||
const showLogo = computed(() => settingsStore.sidebarLogo);
|
||||
const sideTheme = computed(() => settingsStore.sideTheme);
|
||||
const theme = computed(() => settingsStore.theme);
|
||||
const isCollapse = computed(() => !appStore.sidebar.opened);
|
||||
|
||||
const activeMenu = computed(() => {
|
||||
const { meta, path } = route;
|
||||
// if set path, the sidebar will highlight the path you set
|
||||
if (meta.activeMenu) {
|
||||
return meta.activeMenu;
|
||||
}
|
||||
return path;
|
||||
})
|
||||
const { meta, path } = route;
|
||||
// if set path, the sidebar will highlight the path you set
|
||||
if (meta.activeMenu) {
|
||||
return meta.activeMenu;
|
||||
}
|
||||
return path;
|
||||
});
|
||||
|
||||
const bgColor = computed(() => sideTheme.value === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground);
|
||||
const textColor = computed(() => sideTheme.value === 'theme-dark' ? variables.menuColor : variables.menuLightColor);
|
||||
const bgColor = computed(() => (sideTheme.value === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground));
|
||||
const textColor = computed(() => (sideTheme.value === 'theme-dark' ? variables.menuColor : variables.menuLightColor));
|
||||
</script>
|
||||
|
@ -10,7 +10,6 @@ import { LoginData } from '@/api/types';
|
||||
const route = useRoute();
|
||||
const loading = ref(true);
|
||||
|
||||
|
||||
/**
|
||||
* 接收Route传递的参数
|
||||
* @param {Object} route.query.
|
||||
@ -18,8 +17,7 @@ const loading = ref(true);
|
||||
const code = route.query.code as string;
|
||||
const state = route.query.state as string;
|
||||
const source = route.query.source as string;
|
||||
const tenantId = localStorage.getItem("tenantId") ? localStorage.getItem("tenantId") as string : '000000';
|
||||
|
||||
const tenantId = localStorage.getItem('tenantId') ? (localStorage.getItem('tenantId') as string) : '000000';
|
||||
|
||||
const processResponse = async (res: any) => {
|
||||
if (res.code !== 200) {
|
||||
@ -52,7 +50,6 @@ const callbackByCode = async (data: LoginData) => {
|
||||
};
|
||||
|
||||
const loginByCode = async (data: LoginData) => {
|
||||
console.log(2)
|
||||
try {
|
||||
const res = await login(data);
|
||||
await processResponse(res);
|
||||
|
@ -5,84 +5,84 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import useTagsViewStore from '@/store/modules/tagsView'
|
||||
import { TagView } from 'vue-router'
|
||||
import useTagsViewStore from '@/store/modules/tagsView';
|
||||
import { TagView } from 'vue-router';
|
||||
const tagAndTagSpacing = ref(4);
|
||||
|
||||
const scrollContainerRef = ref<ElScrollbarInstance>()
|
||||
const scrollContainerRef = ref<ElScrollbarInstance>();
|
||||
const scrollWrapper = computed(() => scrollContainerRef.value?.$refs.wrapRef as any);
|
||||
|
||||
onMounted(() => {
|
||||
scrollWrapper.value?.addEventListener('scroll', emitScroll, true)
|
||||
})
|
||||
scrollWrapper.value?.addEventListener('scroll', emitScroll, true);
|
||||
});
|
||||
onBeforeUnmount(() => {
|
||||
scrollWrapper.value?.removeEventListener('scroll', emitScroll)
|
||||
})
|
||||
scrollWrapper.value?.removeEventListener('scroll', emitScroll);
|
||||
});
|
||||
|
||||
const handleScroll = (e: WheelEvent) => {
|
||||
const eventDelta = (e as any).wheelDelta || - e.deltaY * 40
|
||||
const $scrollWrapper = scrollWrapper.value;
|
||||
$scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
|
||||
}
|
||||
const emits = defineEmits(['scroll'])
|
||||
const eventDelta = (e as any).wheelDelta || -e.deltaY * 40;
|
||||
const $scrollWrapper = scrollWrapper.value;
|
||||
$scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4;
|
||||
};
|
||||
const emits = defineEmits(['scroll']);
|
||||
const emitScroll = () => {
|
||||
emits('scroll')
|
||||
}
|
||||
emits('scroll');
|
||||
};
|
||||
|
||||
const tagsViewStore = useTagsViewStore()
|
||||
const tagsViewStore = useTagsViewStore();
|
||||
const visitedViews = computed(() => tagsViewStore.visitedViews);
|
||||
|
||||
const moveToTarget = (currentTag: TagView) => {
|
||||
const $container = scrollContainerRef.value?.$el
|
||||
const $containerWidth = $container.offsetWidth
|
||||
const $scrollWrapper = scrollWrapper.value;
|
||||
const $container = scrollContainerRef.value?.$el;
|
||||
const $containerWidth = $container.offsetWidth;
|
||||
const $scrollWrapper = scrollWrapper.value;
|
||||
|
||||
let firstTag = null
|
||||
let lastTag = null
|
||||
let firstTag = null;
|
||||
let lastTag = null;
|
||||
|
||||
// find first tag and last tag
|
||||
if (visitedViews.value.length > 0) {
|
||||
firstTag = visitedViews.value[0]
|
||||
lastTag = visitedViews.value[visitedViews.value.length - 1]
|
||||
// find first tag and last tag
|
||||
if (visitedViews.value.length > 0) {
|
||||
firstTag = visitedViews.value[0];
|
||||
lastTag = visitedViews.value[visitedViews.value.length - 1];
|
||||
}
|
||||
|
||||
if (firstTag === currentTag) {
|
||||
$scrollWrapper.scrollLeft = 0;
|
||||
} else if (lastTag === currentTag) {
|
||||
$scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth;
|
||||
} else {
|
||||
const tagListDom: any = document.getElementsByClassName('tags-view-item');
|
||||
const currentIndex = visitedViews.value.findIndex((item) => item === currentTag);
|
||||
let prevTag = null;
|
||||
let nextTag = null;
|
||||
|
||||
for (const k in tagListDom) {
|
||||
if (k !== 'length' && Object.hasOwnProperty.call(tagListDom, k)) {
|
||||
if (tagListDom[k].dataset.path === visitedViews.value[currentIndex - 1].path) {
|
||||
prevTag = tagListDom[k];
|
||||
}
|
||||
if (tagListDom[k].dataset.path === visitedViews.value[currentIndex + 1].path) {
|
||||
nextTag = tagListDom[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (firstTag === currentTag) {
|
||||
$scrollWrapper.scrollLeft = 0
|
||||
} else if (lastTag === currentTag) {
|
||||
$scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth
|
||||
} else {
|
||||
const tagListDom: any = document.getElementsByClassName('tags-view-item');
|
||||
const currentIndex = visitedViews.value.findIndex(item => item === currentTag)
|
||||
let prevTag = null
|
||||
let nextTag = null
|
||||
// the tag's offsetLeft after of nextTag
|
||||
const afterNextTagOffsetLeft = nextTag.offsetLeft + nextTag.offsetWidth + tagAndTagSpacing.value;
|
||||
|
||||
for (const k in tagListDom) {
|
||||
if (k !== 'length' && Object.hasOwnProperty.call(tagListDom, k)) {
|
||||
if (tagListDom[k].dataset.path === visitedViews.value[currentIndex - 1].path) {
|
||||
prevTag = tagListDom[k];
|
||||
}
|
||||
if (tagListDom[k].dataset.path === visitedViews.value[currentIndex + 1].path) {
|
||||
nextTag = tagListDom[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the tag's offsetLeft after of nextTag
|
||||
const afterNextTagOffsetLeft = nextTag.offsetLeft + nextTag.offsetWidth + tagAndTagSpacing.value
|
||||
|
||||
// the tag's offsetLeft before of prevTag
|
||||
const beforePrevTagOffsetLeft = prevTag.offsetLeft - tagAndTagSpacing.value
|
||||
if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) {
|
||||
$scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth
|
||||
} else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {
|
||||
$scrollWrapper.scrollLeft = beforePrevTagOffsetLeft
|
||||
}
|
||||
// the tag's offsetLeft before of prevTag
|
||||
const beforePrevTagOffsetLeft = prevTag.offsetLeft - tagAndTagSpacing.value;
|
||||
if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) {
|
||||
$scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth;
|
||||
} else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {
|
||||
$scrollWrapper.scrollLeft = beforePrevTagOffsetLeft;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
moveToTarget,
|
||||
})
|
||||
moveToTarget
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -14,36 +14,35 @@
|
||||
>
|
||||
{{ tag.title }}
|
||||
<span v-if="!isAffix(tag)" @click.prevent.stop="closeSelectedTag(tag)">
|
||||
<close class="el-icon-close" style="width: 1em; height: 1em;vertical-align: middle;" />
|
||||
<close class="el-icon-close" style="width: 1em; height: 1em; vertical-align: middle" />
|
||||
</span>
|
||||
</router-link>
|
||||
</scroll-pane>
|
||||
<ul v-show="visible" :style="{ left: left + 'px', top: top + 'px' }" class="contextmenu">
|
||||
<li @click="refreshSelectedTag(selectedTag)"><refresh-right style="width: 1em; height: 1em;" /> 刷新页面</li>
|
||||
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)"><close style="width: 1em; height: 1em;" /> 关闭当前</li>
|
||||
<li @click="closeOthersTags"><circle-close style="width: 1em; height: 1em;" /> 关闭其他</li>
|
||||
<li v-if="!isFirstView()" @click="closeLeftTags"><back style="width: 1em; height: 1em;" /> 关闭左侧</li>
|
||||
<li v-if="!isLastView()" @click="closeRightTags"><right style="width: 1em; height: 1em;" /> 关闭右侧</li>
|
||||
<li @click="closeAllTags(selectedTag)"><circle-close style="width: 1em; height: 1em;" /> 全部关闭</li>
|
||||
<li @click="refreshSelectedTag(selectedTag)"><refresh-right style="width: 1em; height: 1em" /> 刷新页面</li>
|
||||
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)"><close style="width: 1em; height: 1em" /> 关闭当前</li>
|
||||
<li @click="closeOthersTags"><circle-close style="width: 1em; height: 1em" /> 关闭其他</li>
|
||||
<li v-if="!isFirstView()" @click="closeLeftTags"><back style="width: 1em; height: 1em" /> 关闭左侧</li>
|
||||
<li v-if="!isLastView()" @click="closeRightTags"><right style="width: 1em; height: 1em" /> 关闭右侧</li>
|
||||
<li @click="closeAllTags(selectedTag)"><circle-close style="width: 1em; height: 1em" /> 全部关闭</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import ScrollPane from './ScrollPane.vue'
|
||||
import { getNormalPath } from '@/utils/ruoyi'
|
||||
import useTagsViewStore from "@/store/modules/tagsView";
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import usePermissionStore from '@/store/modules/permission'
|
||||
import { ComponentInternalInstance } from "vue";
|
||||
import { RouteOption, TagView, RouteLocationRaw } from "vue-router";
|
||||
import ScrollPane from './ScrollPane.vue';
|
||||
import { getNormalPath } from '@/utils/ruoyi';
|
||||
import useTagsViewStore from '@/store/modules/tagsView';
|
||||
import useSettingsStore from '@/store/modules/settings';
|
||||
import usePermissionStore from '@/store/modules/permission';
|
||||
import { RouteRecordRaw, TagView } from 'vue-router';
|
||||
|
||||
const visible = ref(false);
|
||||
const top = ref(0);
|
||||
const left = ref(0);
|
||||
const selectedTag = ref<TagView>({});
|
||||
const affixTags = ref<TagView[]>([]);
|
||||
const scrollPaneRef = ref(ScrollPane);
|
||||
const scrollPaneRef = ref<InstanceType<typeof ScrollPane>>();
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const route = useRoute();
|
||||
@ -54,186 +53,186 @@ const routes = computed(() => usePermissionStore().routes);
|
||||
const theme = computed(() => useSettingsStore().theme);
|
||||
|
||||
watch(route, () => {
|
||||
addTags();
|
||||
moveToCurrentTag();
|
||||
})
|
||||
addTags();
|
||||
moveToCurrentTag();
|
||||
});
|
||||
watch(visible, (value) => {
|
||||
if (value) {
|
||||
document.body.addEventListener('click', closeMenu);
|
||||
} else {
|
||||
document.body.removeEventListener('click', closeMenu);
|
||||
}
|
||||
})
|
||||
if (value) {
|
||||
document.body.addEventListener('click', closeMenu);
|
||||
} else {
|
||||
document.body.removeEventListener('click', closeMenu);
|
||||
}
|
||||
});
|
||||
|
||||
const isActive = (r: TagView): boolean => {
|
||||
return r.path === route.path;
|
||||
}
|
||||
return r.path === route.path;
|
||||
};
|
||||
const activeStyle = (tag: TagView) => {
|
||||
if (!isActive(tag)) return {};
|
||||
return {
|
||||
"background-color": theme.value,
|
||||
"border-color": theme.value
|
||||
};
|
||||
}
|
||||
if (!isActive(tag)) return {};
|
||||
return {
|
||||
'background-color': theme.value,
|
||||
'border-color': theme.value
|
||||
};
|
||||
};
|
||||
const isAffix = (tag: TagView) => {
|
||||
return tag.meta && tag.meta.affix;
|
||||
}
|
||||
return tag.meta && tag.meta.affix;
|
||||
};
|
||||
const isFirstView = () => {
|
||||
try {
|
||||
return selectedTag.value.fullPath === '/index' || selectedTag.value.fullPath === visitedViews.value[1].fullPath;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
try {
|
||||
return selectedTag.value.fullPath === '/index' || selectedTag.value.fullPath === visitedViews.value[1].fullPath;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
const isLastView = () => {
|
||||
try {
|
||||
return selectedTag.value.fullPath === visitedViews.value[visitedViews.value.length - 1].fullPath;
|
||||
} catch (err) {
|
||||
return false;
|
||||
try {
|
||||
return selectedTag.value.fullPath === visitedViews.value[visitedViews.value.length - 1].fullPath;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
const filterAffixTags = (routes: RouteRecordRaw[], basePath = '') => {
|
||||
let tags: TagView[] = [];
|
||||
|
||||
routes.forEach((route) => {
|
||||
if (route.meta && route.meta.affix) {
|
||||
const tagPath = getNormalPath(basePath + '/' + route.path);
|
||||
tags.push({
|
||||
fullPath: tagPath,
|
||||
path: tagPath,
|
||||
name: route.name as string,
|
||||
meta: { ...route.meta }
|
||||
});
|
||||
}
|
||||
}
|
||||
const filterAffixTags = (routes:RouteOption [], basePath = '') => {
|
||||
let tags:TagView[] = []
|
||||
routes.forEach(route => {
|
||||
if (route.meta && route.meta.affix) {
|
||||
const tagPath = getNormalPath(basePath + '/' + route.path);
|
||||
tags.push({
|
||||
fullPath: tagPath,
|
||||
path: tagPath,
|
||||
name: route.name,
|
||||
meta: { ...route.meta }
|
||||
})
|
||||
}
|
||||
if (route.children) {
|
||||
const tempTags = filterAffixTags(route.children, route.path);
|
||||
if (tempTags.length >= 1) {
|
||||
tags = [...tags, ...tempTags];
|
||||
}
|
||||
}
|
||||
})
|
||||
return tags
|
||||
}
|
||||
if (route.children) {
|
||||
const tempTags = filterAffixTags(route.children, route.path);
|
||||
if (tempTags.length >= 1) {
|
||||
tags = [...tags, ...tempTags];
|
||||
}
|
||||
}
|
||||
});
|
||||
return tags;
|
||||
};
|
||||
const initTags = () => {
|
||||
const res = filterAffixTags(routes.value);
|
||||
affixTags.value = res;
|
||||
for (const tag of res) {
|
||||
// Must have tag name
|
||||
if (tag.name) {
|
||||
useTagsViewStore().addVisitedView(tag);
|
||||
}
|
||||
const res = filterAffixTags(routes.value as any);
|
||||
affixTags.value = res;
|
||||
for (const tag of res) {
|
||||
// Must have tag name
|
||||
if (tag.name) {
|
||||
useTagsViewStore().addVisitedView(tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const addTags = () => {
|
||||
const { name } = route;
|
||||
if(route.query.title) {
|
||||
route.meta.title = route.query.title;
|
||||
}
|
||||
if (name) {
|
||||
useTagsViewStore().addView(route);
|
||||
if (route.meta.link) {
|
||||
useTagsViewStore().addIframeView(route);
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
const moveToCurrentTag = () => {
|
||||
nextTick(() => {
|
||||
for (const r of visitedViews.value) {
|
||||
if (r.path === route.path) {
|
||||
scrollPaneRef.value.moveToTarget(r);
|
||||
// when query is different then update
|
||||
if (r.fullPath !== route.fullPath) {
|
||||
useTagsViewStore().updateVisitedView(route);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
const refreshSelectedTag = (view: TagView) => {
|
||||
proxy?.$tab.refreshPage(view);
|
||||
const { name } = route;
|
||||
if (route.query.title) {
|
||||
route.meta.title = route.query.title as string;
|
||||
}
|
||||
if (name) {
|
||||
useTagsViewStore().addView(route as any);
|
||||
if (route.meta.link) {
|
||||
useTagsViewStore().delIframeView(route);
|
||||
useTagsViewStore().addIframeView(route as any);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const moveToCurrentTag = () => {
|
||||
nextTick(() => {
|
||||
for (const r of visitedViews.value) {
|
||||
if (r.path === route.path) {
|
||||
scrollPaneRef.value?.moveToTarget(r);
|
||||
// when query is different then update
|
||||
if (r.fullPath !== route.fullPath) {
|
||||
useTagsViewStore().updateVisitedView(route);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
const refreshSelectedTag = (view: TagView) => {
|
||||
proxy?.$tab.refreshPage(view);
|
||||
if (route.meta.link) {
|
||||
useTagsViewStore().delIframeView(route as any);
|
||||
}
|
||||
};
|
||||
const closeSelectedTag = (view: TagView) => {
|
||||
proxy?.$tab.closePage(view).then(({ visitedViews }: any) => {
|
||||
if (isActive(view)) {
|
||||
toLastView(visitedViews, view);
|
||||
}
|
||||
})
|
||||
}
|
||||
proxy?.$tab.closePage(view).then(({ visitedViews }: any) => {
|
||||
if (isActive(view)) {
|
||||
toLastView(visitedViews, view);
|
||||
}
|
||||
});
|
||||
};
|
||||
const closeRightTags = () => {
|
||||
proxy?.$tab.closeRightPage(selectedTag.value).then(visitedViews => {
|
||||
if (!visitedViews.find(i => i.fullPath === route.fullPath)) {
|
||||
toLastView(visitedViews);
|
||||
}
|
||||
})
|
||||
}
|
||||
proxy?.$tab.closeRightPage(selectedTag.value).then((visitedViews) => {
|
||||
if (!visitedViews.find((i) => i.fullPath === route.fullPath)) {
|
||||
toLastView(visitedViews);
|
||||
}
|
||||
});
|
||||
};
|
||||
const closeLeftTags = () => {
|
||||
proxy?.$tab.closeLeftPage(selectedTag.value).then(visitedViews => {
|
||||
if (!visitedViews.find(i => i.fullPath === route.fullPath)) {
|
||||
toLastView(visitedViews);
|
||||
}
|
||||
})
|
||||
}
|
||||
proxy?.$tab.closeLeftPage(selectedTag.value).then((visitedViews) => {
|
||||
if (!visitedViews.find((i: TagView) => i.fullPath === route.fullPath)) {
|
||||
toLastView(visitedViews);
|
||||
}
|
||||
});
|
||||
};
|
||||
const closeOthersTags = () => {
|
||||
router.push(selectedTag.value as RouteLocationRaw).catch(() => { });
|
||||
proxy?.$tab.closeOtherPage(selectedTag.value).then(() => {
|
||||
moveToCurrentTag();
|
||||
})
|
||||
}
|
||||
router.push(selectedTag.value).catch(() => {});
|
||||
proxy?.$tab.closeOtherPage(selectedTag.value).then(() => {
|
||||
moveToCurrentTag();
|
||||
});
|
||||
};
|
||||
const closeAllTags = (view: TagView) => {
|
||||
proxy?.$tab.closeAllPage().then(({ visitedViews }) => {
|
||||
if (affixTags.value.some(tag => tag.path === route.path)) {
|
||||
return;
|
||||
}
|
||||
toLastView(visitedViews, view);
|
||||
})
|
||||
}
|
||||
const toLastView = (visitedViews:TagView[], view?: TagView) => {
|
||||
const latestView = visitedViews.slice(-1)[0];
|
||||
if (latestView) {
|
||||
router.push(latestView.fullPath as string);
|
||||
} else {
|
||||
// now the default is to redirect to the home page if there is no tags-view,
|
||||
// you can adjust it according to your needs.
|
||||
if (view?.name === 'Dashboard') {
|
||||
// to reload home page
|
||||
router.replace({ path: '/redirect' + view?.fullPath });
|
||||
} else {
|
||||
router.push('/');
|
||||
}
|
||||
proxy?.$tab.closeAllPage().then(({ visitedViews }) => {
|
||||
if (affixTags.value.some((tag) => tag.path === route.path)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
toLastView(visitedViews, view);
|
||||
});
|
||||
};
|
||||
const toLastView = (visitedViews: TagView[], view?: TagView) => {
|
||||
const latestView = visitedViews.slice(-1)[0];
|
||||
if (latestView) {
|
||||
router.push(latestView.fullPath as string);
|
||||
} else {
|
||||
// now the default is to redirect to the home page if there is no tags-view,
|
||||
// you can adjust it according to your needs.
|
||||
if (view?.name === 'Dashboard') {
|
||||
// to reload home page
|
||||
router.replace({ path: '/redirect' + view?.fullPath });
|
||||
} else {
|
||||
router.push('/');
|
||||
}
|
||||
}
|
||||
};
|
||||
const openMenu = (tag: TagView, e: MouseEvent) => {
|
||||
const menuMinWidth = 105;
|
||||
const offsetLeft = proxy?.$el.getBoundingClientRect().left; // container margin left
|
||||
const offsetWidth = proxy?.$el.offsetWidth; // container width
|
||||
const maxLeft = offsetWidth - menuMinWidth; // left boundary
|
||||
const l = e.clientX - offsetLeft + 15; // 15: margin right
|
||||
const menuMinWidth = 105;
|
||||
const offsetLeft = proxy?.$el.getBoundingClientRect().left; // container margin left
|
||||
const offsetWidth = proxy?.$el.offsetWidth; // container width
|
||||
const maxLeft = offsetWidth - menuMinWidth; // left boundary
|
||||
const l = e.clientX - offsetLeft + 15; // 15: margin right
|
||||
|
||||
if (l > maxLeft) {
|
||||
left.value = maxLeft;
|
||||
} else {
|
||||
left.value = l;
|
||||
}
|
||||
if (l > maxLeft) {
|
||||
left.value = maxLeft;
|
||||
} else {
|
||||
left.value = l;
|
||||
}
|
||||
|
||||
top.value = e.clientY
|
||||
visible.value = true;
|
||||
selectedTag.value = tag;
|
||||
}
|
||||
top.value = e.clientY;
|
||||
visible.value = true;
|
||||
selectedTag.value = tag;
|
||||
};
|
||||
const closeMenu = () => {
|
||||
visible.value = false;
|
||||
}
|
||||
visible.value = false;
|
||||
};
|
||||
const handleScroll = () => {
|
||||
closeMenu();
|
||||
}
|
||||
|
||||
closeMenu();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
initTags();
|
||||
addTags();
|
||||
})
|
||||
initTags();
|
||||
addTags();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@ -242,7 +241,9 @@ onMounted(() => {
|
||||
width: 100%;
|
||||
background-color: var(--el-bg-color);
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
|
||||
box-shadow:
|
||||
0 1px 3px 0 rgba(0, 0, 0, 0.12),
|
||||
0 0 3px 0 rgba(0, 0, 0, 0.04);
|
||||
.tags-view-wrapper {
|
||||
.tags-view-item {
|
||||
display: inline-block;
|
||||
@ -271,7 +272,7 @@ onMounted(() => {
|
||||
color: #fff;
|
||||
border-color: #42b983;
|
||||
&::before {
|
||||
content: "";
|
||||
content: '';
|
||||
background: #fff;
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
|
@ -3,12 +3,12 @@
|
||||
<el-dialog v-model="state.isShowSearch" destroy-on-close :show-close="false">
|
||||
<template #footer>
|
||||
<el-autocomplete
|
||||
ref="layoutMenuAutocompleteRef"
|
||||
v-model="state.menuQuery"
|
||||
:fetch-suggestions="menuSearch"
|
||||
placeholder="搜索"
|
||||
ref="layoutMenuAutocompleteRef"
|
||||
@select="onHandleSelect"
|
||||
:fit-input-width="true"
|
||||
@select="onHandleSelect"
|
||||
>
|
||||
<template #prefix>
|
||||
<svg-icon class-name="search-icon" icon-class="search" />
|
||||
@ -29,130 +29,129 @@
|
||||
import { getNormalPath } from '@/utils/ruoyi';
|
||||
import { isHttp } from '@/utils/validate';
|
||||
import usePermissionStore from '@/store/modules/permission';
|
||||
import { RouteOption } from 'vue-router';
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
type Router = Array<{
|
||||
path: string;
|
||||
icon: string;
|
||||
title: string[];
|
||||
}>
|
||||
path: string;
|
||||
icon: string;
|
||||
title: string[];
|
||||
}>;
|
||||
type SearchState<T = any> = {
|
||||
isShowSearch: boolean;
|
||||
menuQuery: string;
|
||||
menuList: T[];
|
||||
isShowSearch: boolean;
|
||||
menuQuery: string;
|
||||
menuList: T[];
|
||||
};
|
||||
// 定义变量内容
|
||||
const layoutMenuAutocompleteRef = ref();
|
||||
const router = useRouter();
|
||||
const routes = computed(() => usePermissionStore().routes);
|
||||
const state = reactive<SearchState>({
|
||||
isShowSearch: false,
|
||||
menuQuery: '',
|
||||
menuList: [],
|
||||
isShowSearch: false,
|
||||
menuQuery: '',
|
||||
menuList: []
|
||||
});
|
||||
|
||||
// 搜索弹窗打开
|
||||
const openSearch = () => {
|
||||
state.menuQuery = '';
|
||||
state.isShowSearch = true;
|
||||
state.menuList = generateRoutes(routes.value);
|
||||
nextTick(() => {
|
||||
setTimeout(() => {
|
||||
layoutMenuAutocompleteRef.value.focus();
|
||||
});
|
||||
});
|
||||
state.menuQuery = '';
|
||||
state.isShowSearch = true;
|
||||
state.menuList = generateRoutes(routes.value);
|
||||
nextTick(() => {
|
||||
setTimeout(() => {
|
||||
layoutMenuAutocompleteRef.value.focus();
|
||||
});
|
||||
});
|
||||
};
|
||||
// 搜索弹窗关闭
|
||||
const closeSearch = () => {
|
||||
state.isShowSearch = false;
|
||||
state.isShowSearch = false;
|
||||
};
|
||||
// 菜单搜索数据过滤
|
||||
const menuSearch = (queryString: string, cb: Function) => {
|
||||
let options = state.menuList.filter((item) => {
|
||||
return item.title.indexOf(queryString) > -1;
|
||||
});
|
||||
cb(options);
|
||||
let options = state.menuList.filter((item) => {
|
||||
return item.title.indexOf(queryString) > -1;
|
||||
});
|
||||
cb(options);
|
||||
};
|
||||
|
||||
// Filter out the routes that can be displayed in the sidebar
|
||||
// And generate the internationalized title
|
||||
const generateRoutes = (routes: RouteOption[], basePath = '', prefixTitle: string[] = []) => {
|
||||
let res: Router = []
|
||||
routes.forEach(r => {
|
||||
// skip hidden router
|
||||
if (!r.hidden) {
|
||||
const p = r.path.length > 0 && r.path[0] === '/' ? r.path : '/' + r.path;
|
||||
const data: any = {
|
||||
path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path,
|
||||
icon: r.meta?.icon,
|
||||
title: [...prefixTitle]
|
||||
}
|
||||
if (r.meta && r.meta.title) {
|
||||
data.title = [...data.title, r.meta.title];
|
||||
if (r.redirect !== 'noRedirect') {
|
||||
// only push the routes with title
|
||||
// special case: need to exclude parent router without redirect
|
||||
res.push(data);
|
||||
}
|
||||
}
|
||||
// recursive child routes
|
||||
if (r.children) {
|
||||
const tempRoutes = generateRoutes(r.children, data.path, data.title);
|
||||
if (tempRoutes.length >= 1) {
|
||||
res = [...res, ...tempRoutes];
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
res.forEach((item: any) => {
|
||||
if (item.title instanceof Array) {
|
||||
item.title = item.title.join('/');
|
||||
}
|
||||
});
|
||||
return res;
|
||||
}
|
||||
const generateRoutes = (routes: RouteRecordRaw[], basePath = '', prefixTitle: string[] = []) => {
|
||||
let res: Router = [];
|
||||
routes.forEach((r) => {
|
||||
// skip hidden router
|
||||
if (!r.hidden) {
|
||||
const p = r.path.length > 0 && r.path[0] === '/' ? r.path : '/' + r.path;
|
||||
const data: any = {
|
||||
path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path,
|
||||
icon: r.meta?.icon,
|
||||
title: [...prefixTitle]
|
||||
};
|
||||
if (r.meta && r.meta.title) {
|
||||
data.title = [...data.title, r.meta.title];
|
||||
if (r.redirect !== 'noRedirect') {
|
||||
// only push the routes with title
|
||||
// special case: need to exclude parent router without redirect
|
||||
res.push(data);
|
||||
}
|
||||
}
|
||||
// recursive child routes
|
||||
if (r.children) {
|
||||
const tempRoutes = generateRoutes(r.children, data.path, data.title);
|
||||
if (tempRoutes.length >= 1) {
|
||||
res = [...res, ...tempRoutes];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
res.forEach((item: any) => {
|
||||
if (item.title instanceof Array) {
|
||||
item.title = item.title.join('/');
|
||||
}
|
||||
});
|
||||
return res;
|
||||
};
|
||||
// 当前菜单选中时
|
||||
const onHandleSelect = (val: any) => {
|
||||
const paths = val.path;
|
||||
if (isHttp(paths)) {
|
||||
// http(s):// 路径新窗口打开
|
||||
const pindex = paths.indexOf("http");
|
||||
window.open(paths.substring(pindex, paths.length), "_blank");
|
||||
} else {
|
||||
router.push(paths);
|
||||
}
|
||||
state.menuQuery = ''
|
||||
closeSearch();
|
||||
|
||||
const paths = val.path;
|
||||
if (isHttp(paths)) {
|
||||
// http(s):// 路径新窗口打开
|
||||
const pindex = paths.indexOf('http');
|
||||
window.open(paths.substring(pindex, paths.length), '_blank');
|
||||
} else {
|
||||
router.push(paths);
|
||||
}
|
||||
state.menuQuery = '';
|
||||
closeSearch();
|
||||
};
|
||||
|
||||
// 暴露变量
|
||||
defineExpose({
|
||||
openSearch
|
||||
openSearch
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.layout-search-dialog {
|
||||
position: relative;
|
||||
:deep(.el-dialog) {
|
||||
.el-dialog__header,
|
||||
.el-dialog__body {
|
||||
display: none;
|
||||
}
|
||||
.el-dialog__footer {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
top: -53vh;
|
||||
}
|
||||
}
|
||||
:deep(.el-autocomplete) {
|
||||
width: 560px;
|
||||
position: absolute;
|
||||
top: 150px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
position: relative;
|
||||
:deep(.el-dialog) {
|
||||
.el-dialog__header,
|
||||
.el-dialog__body {
|
||||
display: none;
|
||||
}
|
||||
.el-dialog__footer {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
top: -53vh;
|
||||
}
|
||||
}
|
||||
:deep(.el-autocomplete) {
|
||||
width: 560px;
|
||||
position: absolute;
|
||||
top: 150px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<div class="layout-navbars-breadcrumb-user-news" v-loading="state.loading">
|
||||
<div v-loading="state.loading" class="layout-navbars-breadcrumb-user-news">
|
||||
<div class="head-box">
|
||||
<div class="head-box-title">通知公告</div>
|
||||
<div class="head-box-btn" @click="readAll">全部已读</div>
|
||||
</div>
|
||||
<div class="content-box" v-loading="state.loading">
|
||||
<div v-loading="state.loading" class="content-box">
|
||||
<template v-if="newsList.length > 0">
|
||||
<div class="content-box-item" v-for="(v, k) in newsList" :key="k" @click="onNewsClick(k)">
|
||||
<div v-for="(v, k) in newsList" :key="k" class="content-box-item" @click="onNewsClick(k)">
|
||||
<div class="item-conten">
|
||||
<div>{{ v.message }}</div>
|
||||
<div class="content-box-msg"></div>
|
||||
@ -17,26 +17,24 @@
|
||||
<span v-else class="el-tag el-tag--danger el-tag--mini read">未读</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-empty :description="'消息为空'" v-else></el-empty>
|
||||
<el-empty v-else :description="'消息为空'"></el-empty>
|
||||
</div>
|
||||
<div class="foot-box" @click="onGoToGiteeClick" v-if="newsList.length > 0">前往gitee</div>
|
||||
<div v-if="newsList.length > 0" class="foot-box" @click="onGoToGiteeClick">前往gitee</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="layoutBreadcrumbUserNews">
|
||||
import { ref } from "vue";
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { nextTick, onMounted, reactive } from "vue";
|
||||
import { storeToRefs } from 'pinia';
|
||||
import useNoticeStore from '@/store/modules/notice';
|
||||
|
||||
const noticeStore = storeToRefs(useNoticeStore());
|
||||
const {readAll} = useNoticeStore();
|
||||
const { readAll } = useNoticeStore();
|
||||
|
||||
// 定义变量内容
|
||||
const state = reactive({
|
||||
loading: false,
|
||||
loading: false
|
||||
});
|
||||
const newsList =ref([]) as any;
|
||||
const newsList = ref([]) as any;
|
||||
|
||||
/**
|
||||
* 初始化数据
|
||||
@ -48,7 +46,6 @@ const getTableData = async () => {
|
||||
state.loading = false;
|
||||
};
|
||||
|
||||
|
||||
//点击消息,写入已读
|
||||
const onNewsClick = (item: any) => {
|
||||
newsList.value[item].read = true;
|
||||
@ -58,7 +55,7 @@ const onNewsClick = (item: any) => {
|
||||
|
||||
// 前往通知中心点击
|
||||
const onGoToGiteeClick = () => {
|
||||
window.open("https://gitee.com/dromara/RuoYi-Vue-Plus/tree/5.X/");
|
||||
window.open('https://gitee.com/dromara/RuoYi-Vue-Plus/tree/5.X/');
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
|
@ -12,7 +12,7 @@
|
||||
<settings ref="settingRef" />
|
||||
</el-scrollbar> -->
|
||||
<div :class="{ 'fixed-header': fixedHeader }">
|
||||
<navbar ref="navbarRef" @setLayout="setLayout" />
|
||||
<navbar ref="navbarRef" @set-layout="setLayout" />
|
||||
<tags-view v-if="needTagsView" />
|
||||
</div>
|
||||
<app-main />
|
||||
@ -22,12 +22,12 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import SideBar from './components/Sidebar/index.vue'
|
||||
import { AppMain, Navbar, Settings, TagsView } from './components'
|
||||
import useAppStore from '@/store/modules/app'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import SideBar from './components/Sidebar/index.vue';
|
||||
import { AppMain, Navbar, Settings, TagsView } from './components';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
import useSettingsStore from '@/store/modules/settings';
|
||||
|
||||
const settingsStore = useSettingsStore()
|
||||
const settingsStore = useSettingsStore();
|
||||
const theme = computed(() => settingsStore.theme);
|
||||
const sidebar = computed(() => useAppStore().sidebar);
|
||||
const device = computed(() => useAppStore().device);
|
||||
@ -35,48 +35,48 @@ const needTagsView = computed(() => settingsStore.tagsView);
|
||||
const fixedHeader = computed(() => settingsStore.fixedHeader);
|
||||
|
||||
const classObj = computed(() => ({
|
||||
hideSidebar: !sidebar.value.opened,
|
||||
openSidebar: sidebar.value.opened,
|
||||
withoutAnimation: sidebar.value.withoutAnimation,
|
||||
mobile: device.value === 'mobile'
|
||||
}))
|
||||
hideSidebar: !sidebar.value.opened,
|
||||
openSidebar: sidebar.value.opened,
|
||||
withoutAnimation: sidebar.value.withoutAnimation,
|
||||
mobile: device.value === 'mobile'
|
||||
}));
|
||||
|
||||
const { width } = useWindowSize();
|
||||
const WIDTH = 992; // refer to Bootstrap's responsive design
|
||||
|
||||
watchEffect(() => {
|
||||
if (device.value === 'mobile' && sidebar.value.opened) {
|
||||
useAppStore().closeSideBar({ withoutAnimation: false })
|
||||
}
|
||||
if (width.value - 1 < WIDTH) {
|
||||
useAppStore().toggleDevice('mobile')
|
||||
useAppStore().closeSideBar({ withoutAnimation: true })
|
||||
} else {
|
||||
useAppStore().toggleDevice('desktop')
|
||||
}
|
||||
})
|
||||
if (device.value === 'mobile' && sidebar.value.opened) {
|
||||
useAppStore().closeSideBar({ withoutAnimation: false });
|
||||
}
|
||||
if (width.value - 1 < WIDTH) {
|
||||
useAppStore().toggleDevice('mobile');
|
||||
useAppStore().closeSideBar({ withoutAnimation: true });
|
||||
} else {
|
||||
useAppStore().toggleDevice('desktop');
|
||||
}
|
||||
});
|
||||
|
||||
const navbarRef = ref(Navbar);
|
||||
const settingRef = ref(Settings);
|
||||
const navbarRef = ref<InstanceType<typeof Navbar>>();
|
||||
const settingRef = ref<InstanceType<typeof Settings>>();
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
navbarRef.value.initTenantList();
|
||||
})
|
||||
})
|
||||
navbarRef.value?.initTenantList();
|
||||
});
|
||||
});
|
||||
|
||||
const handleClickOutside = () => {
|
||||
useAppStore().closeSideBar({ withoutAnimation: false })
|
||||
}
|
||||
useAppStore().closeSideBar({ withoutAnimation: false });
|
||||
};
|
||||
|
||||
const setLayout = () => {
|
||||
settingRef.value.openSetting();
|
||||
}
|
||||
settingRef.value?.openSetting();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "@/assets/styles/mixin.scss";
|
||||
@import "@/assets/styles/variables.module.scss";
|
||||
@import '@/assets/styles/mixin.scss';
|
||||
@import '@/assets/styles/variables.module.scss';
|
||||
|
||||
.app-wrapper {
|
||||
@include clearfix;
|
||||
|
Reference in New Issue
Block a user