release: update 4.3.0

This commit is contained in:
xiaoxian521
2023-06-05 15:44:49 +08:00
parent 4e95672cb4
commit d9ab1b1198
38 changed files with 2065 additions and 1236 deletions

View File

@@ -15,6 +15,7 @@ const {
onPanel,
pureApp,
username,
userAvatar,
avatarsStyle,
toggleSideBar
} = useNav();
@@ -46,10 +47,7 @@ const {
<!-- 退出登录 -->
<el-dropdown trigger="click">
<span class="el-dropdown-link navbar-bg-hover select-none">
<img
src="https://avatars.githubusercontent.com/u/44761321?v=4"
:style="avatarsStyle"
/>
<img :src="userAvatar" :style="avatarsStyle" />
<p v-if="username" class="dark:text-white">{{ username }}</p>
</span>
<template #dropdown>

View File

@@ -46,8 +46,9 @@ notices.value.map(v => (noticesNum.value += v.list.length));
display: flex;
align-items: center;
justify-content: center;
width: 60px;
width: 40px;
height: 48px;
margin-right: 10px;
cursor: pointer;
.header-notice-icon {

View File

@@ -1,3 +1,17 @@
<script setup lang="ts">
import ArrowUpLine from "@iconify-icons/ri/arrow-up-line";
import ArrowDownLine from "@iconify-icons/ri/arrow-down-line";
import { useNav } from "@/layout/hooks/useNav";
import mdiKeyboardEsc from "@/assets/svg/keyboard_esc.svg?component";
import enterOutlined from "@/assets/svg/enter_outlined.svg?component";
const props = withDefaults(defineProps<{ total: number }>(), {
total: 0
});
const { device } = useNav();
</script>
<template>
<div class="search-footer text-[#333] dark:text-white">
<span class="search-footer-item">
@@ -13,16 +27,15 @@
<mdiKeyboardEsc class="icon" />
关闭
</span>
<p
v-if="device !== 'mobile' && props.total > 0"
class="search-footer-total"
>
{{ props.total }}
</p>
</div>
</template>
<script setup lang="ts">
import ArrowUpLine from "@iconify-icons/ri/arrow-up-line";
import ArrowDownLine from "@iconify-icons/ri/arrow-down-line";
import mdiKeyboardEsc from "@/assets/svg/keyboard_esc.svg?component";
import enterOutlined from "@/assets/svg/enter_outlined.svg?component";
</script>
<style lang="scss" scoped>
.search-footer {
display: flex;
@@ -40,5 +53,10 @@ import enterOutlined from "@/assets/svg/enter_outlined.svg?component";
box-shadow: inset 0 -2px #cdcde6, inset 0 0 1px 1px #fff,
0 1px 2px 1px #1e235a66;
}
.search-footer-total {
position: absolute;
right: 20px;
}
}
</style>

View File

@@ -7,7 +7,7 @@ import { useNav } from "@/layout/hooks/useNav";
import { ref, computed, shallowRef } from "vue";
import { useDebounceFn, onKeyStroke } from "@vueuse/core";
import { usePermissionStoreHook } from "@/store/modules/permission";
import Search from "@iconify-icons/ep/search";
import Search from "@iconify-icons/ri/search-line";
interface Props {
/** 弹窗显隐 */
@@ -24,6 +24,8 @@ const props = withDefaults(defineProps<Props>(), {});
const router = useRouter();
const keyword = ref("");
const scrollbarRef = ref();
const resultRef = ref();
const activePath = ref("");
const inputRef = ref<HTMLInputElement | null>(null);
const resultOptions = shallowRef([]);
@@ -82,6 +84,11 @@ function handleClose() {
}, 200);
}
function scrollTo(index) {
const scrollTop = resultRef.value.handleScroll(index);
scrollbarRef.value.setScrollTop(scrollTop);
}
/** key up */
function handleUp() {
const { length } = resultOptions.value;
@@ -91,8 +98,10 @@ function handleUp() {
);
if (index === 0) {
activePath.value = resultOptions.value[length - 1].path;
scrollTo(resultOptions.value.length - 1);
} else {
activePath.value = resultOptions.value[index - 1].path;
scrollTo(index - 1);
}
}
@@ -108,6 +117,7 @@ function handleDown() {
} else {
activePath.value = resultOptions.value[index + 1].path;
}
scrollTo(index + 1);
}
/** key enter */
@@ -126,41 +136,55 @@ onKeyStroke("ArrowDown", handleDown);
<template>
<el-dialog
top="5vh"
class="pure-search-dialog"
v-model="show"
:width="device === 'mobile' ? '80vw' : '50vw'"
:show-close="false"
:width="device === 'mobile' ? '80vw' : '40vw'"
:before-close="handleClose"
:style="{
borderRadius: '6px'
}"
@opened="inputRef.focus()"
@closed="inputRef.blur()"
>
<el-input
ref="inputRef"
size="large"
v-model="keyword"
clearable
placeholder="请输入关键词搜索"
placeholder="搜索菜单"
@input="handleSearch"
>
<template #prefix>
<span class="el-input__icon">
<IconifyIconOffline :icon="Search" />
</span>
<IconifyIconOffline
:icon="Search"
class="text-primary w-[24px] h-[24px]"
/>
</template>
</el-input>
<div class="search-result-container">
<el-empty v-if="resultOptions.length === 0" description="暂无搜索结果" />
<SearchResult
v-else
v-model:value="activePath"
:options="resultOptions"
@click="handleEnter"
/>
<el-scrollbar ref="scrollbarRef" max-height="calc(90vh - 140px)">
<el-empty
v-if="resultOptions.length === 0"
description="暂无搜索结果"
/>
<SearchResult
v-else
ref="resultRef"
v-model:value="activePath"
:options="resultOptions"
@click="handleEnter"
/>
</el-scrollbar>
</div>
<template #footer>
<SearchFooter />
<SearchFooter :total="resultOptions.length" />
</template>
</el-dialog>
</template>
<style lang="scss" scoped>
.search-result-container {
margin-top: 20px;
margin-top: 12px;
}
</style>

View File

@@ -1,7 +1,8 @@
<script setup lang="ts">
import { computed } from "vue";
import { useResizeObserver } from "@vueuse/core";
import { useEpThemeStoreHook } from "@/store/modules/epTheme";
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import { ref, computed, getCurrentInstance, onMounted } from "vue";
import enterOutlined from "@/assets/svg/enter_outlined.svg?component";
import Bookmark2Line from "@iconify-icons/ri/bookmark-2-line";
@@ -23,8 +24,11 @@ interface Emits {
(e: "enter"): void;
}
const resultRef = ref();
const innerHeight = ref();
const props = withDefaults(defineProps<Props>(), {});
const emit = defineEmits<Emits>();
const instance = getCurrentInstance()!;
const itemStyle = computed(() => {
return item => {
@@ -54,22 +58,46 @@ async function handleMouse(item) {
function handleTo() {
emit("enter");
}
function resizeResult() {
// el-scrollbar max-height="calc(90vh - 140px)"
innerHeight.value = window.innerHeight - window.innerHeight / 10 - 140;
}
useResizeObserver(resultRef, () => {
resizeResult();
});
function handleScroll(index: number) {
const curInstance = instance?.proxy?.$refs[`resultItemRef${index}`];
if (!curInstance) return 0;
const curRef = curInstance[0] as ElRef;
const scrollTop = curRef.offsetTop + 128; // 128 两个result-item56px+56px=112px高度加上下margin8px+8px=16px
return scrollTop > innerHeight.value ? scrollTop - innerHeight.value : 0;
}
onMounted(() => {
resizeResult();
});
defineExpose({ handleScroll });
</script>
<template>
<div class="result">
<template v-for="item in options" :key="item.path">
<div
class="result-item dark:bg-[#1d1d1d]"
:style="itemStyle(item)"
@click="handleTo"
@mouseenter="handleMouse(item)"
>
<component :is="useRenderIcon(item.meta?.icon ?? Bookmark2Line)" />
<span class="result-item-title">{{ item.meta?.title }}</span>
<enterOutlined />
</div>
</template>
<div ref="resultRef" class="result">
<div
v-for="(item, index) in options"
:key="item.path"
:ref="'resultItemRef' + index"
class="result-item dark:bg-[#1d1d1d]"
:style="itemStyle(item)"
@click="handleTo"
@mouseenter="handleMouse(item)"
>
<component :is="useRenderIcon(item.meta?.icon ?? Bookmark2Line)" />
<span class="result-item-title">{{ item.meta?.title }}</span>
<enterOutlined />
</div>
</div>
</template>

View File

@@ -218,7 +218,6 @@ watch($storage, ({ layout }) => {
});
onBeforeMount(() => {
dataThemeChange();
/* 初始化项目配置 */
nextTick(() => {
settings.greyVal &&

View File

@@ -19,6 +19,7 @@ const {
onPanel,
menuSelect,
username,
userAvatar,
avatarsStyle
} = useNav();
@@ -66,10 +67,7 @@ watch(
<!-- 退出登录 -->
<el-dropdown trigger="click">
<span class="el-dropdown-link navbar-bg-hover">
<img
src="https://avatars.githubusercontent.com/u/44761321?v=4"
:style="avatarsStyle"
/>
<img :src="userAvatar" :style="avatarsStyle" />
<p v-if="username" class="dark:text-white">{{ username }}</p>
</span>
<template #dropdown>

View File

@@ -7,7 +7,6 @@ const props = defineProps({
});
const { title } = useNav();
const topPath = getTopMenu().path;
</script>
<template>
@@ -18,7 +17,7 @@ const topPath = getTopMenu().path;
key="props.collapse"
:title="title"
class="sidebar-logo-link"
:to="topPath"
:to="getTopMenu()?.path ?? '/'"
>
<img src="/logo.svg" alt="logo" />
<span class="sidebar-title">{{ title }}</span>
@@ -28,7 +27,7 @@ const topPath = getTopMenu().path;
key="expand"
:title="title"
class="sidebar-logo-link"
:to="topPath"
:to="getTopMenu()?.path ?? '/'"
>
<img src="/logo.svg" alt="logo" />
<span class="sidebar-title">{{ title }}</span>

View File

@@ -22,6 +22,7 @@ const {
menuSelect,
resolvePath,
username,
userAvatar,
getDivStyle,
avatarsStyle
} = useNav();
@@ -97,10 +98,7 @@ watch(
<!-- 退出登录 -->
<el-dropdown trigger="click">
<span class="el-dropdown-link navbar-bg-hover select-none">
<img
src="https://avatars.githubusercontent.com/u/44761321?v=4"
:style="avatarsStyle"
/>
<img :src="userAvatar" :style="avatarsStyle" />
<p v-if="username" class="dark:text-white">{{ username }}</p>
</span>
<template #dropdown>

View File

@@ -48,7 +48,7 @@ const tabDom = ref();
const containerDom = ref();
const scrollbarDom = ref();
const isShowArrow = ref(false);
const topPath = getTopMenu().path;
const topPath = getTopMenu()?.path;
const { VITE_HIDE_HOME } = import.meta.env;
const { isFullscreen, toggle } = useFullscreen();

View File

@@ -2,6 +2,7 @@ import { storeToRefs } from "pinia";
import { getConfig } from "@/config";
import { emitter } from "@/utils/mitt";
import { routeMetaType } from "../types";
import userAvatar from "@/assets/user.jpg";
import { getTopMenu } from "@/router/utils";
import { useGlobal } from "@pureadmin/utils";
import { computed, CSSProperties } from "vue";
@@ -70,7 +71,7 @@ export function useNav() {
}
function backTopMenu() {
router.push(getTopMenu().path);
router.push(getTopMenu()?.path);
}
function onPanel() {
@@ -150,6 +151,7 @@ export function useNav() {
isCollapse,
pureApp,
username,
userAvatar,
avatarsStyle,
tooltipEffect
};

View File

@@ -8,7 +8,15 @@ import { useLayout } from "./hooks/useLayout";
import { useAppStoreHook } from "@/store/modules/app";
import { useSettingStoreHook } from "@/store/modules/settings";
import { deviceDetection, useDark, useGlobal } from "@pureadmin/utils";
import { h, reactive, computed, onMounted, defineComponent } from "vue";
import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";
import {
h,
reactive,
computed,
onMounted,
onBeforeMount,
defineComponent
} from "vue";
import navbar from "./components/navbar.vue";
import tag from "./components/tag/index.vue";
@@ -88,11 +96,12 @@ emitter.on("resize", ({ detail }) => {
toggle("desktop", false);
isAutoCloseSidebar = false;
}
} else if (width > 990) {
if (!set.sidebar.isClickCollapse) {
toggle("desktop", true);
isAutoCloseSidebar = true;
}
} else if (width > 990 && !set.sidebar.isClickCollapse) {
toggle("desktop", true);
isAutoCloseSidebar = true;
} else {
toggle("desktop", false);
isAutoCloseSidebar = false;
}
});
@@ -102,6 +111,10 @@ onMounted(() => {
}
});
onBeforeMount(() => {
useDataThemeChange().dataThemeChange();
});
const layoutHeader = defineComponent({
render() {
return h(