You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ocr-web/src/views/final/aside/Aside.vue

307 lines
7.2 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<script lang="ts" setup>
import {
computed,
inject,
nextTick,
onBeforeMount,
onMounted,
reactive,
ref,
shallowRef,
unref,
watch,
} from "vue";
import { CustomFilterModalVue, FilterModal, NewFilterModal } from "@/views/final/comp";
import Search from "@/views/home/aside/comp/Search.vue";
import AdvanceFilter from "@/views/home/aside/comp/AdvanceFilter.vue";
import { getViewportOffset } from "@/utils/domUtils";
import { useWindowSizeFn } from "@/hooks/event/useWindowSizeFn";
import { useFinal } from "@/store/modules/final";
import type { Filter } from "/#/home";
import type { AsideEntity } from "@/config/aside";
import { asideMap } from "@/config/final";
import type { AsideConfig } from "/#/api";
import emitter from "@/utils/mitt";
const emit = defineEmits(["inputChange"]);
const finalStore = useFinal();
// 所有左侧模块的值
const asideValue: Record<keyof typeof asideMap, any> = reactive({});
// 左侧某个模块是否显示: 个人配置
const asideVisible: Partial<Record<keyof AsideConfig, boolean>> = reactive({});
// 当前显示的模块,按照数组顺序显示
const showItems = shallowRef<{ key: string; config: AsideEntity }[]>([]);
Object.keys(asideMap).forEach((key) => {
const { defaultValue, inFilterList } = asideMap[key];
if (inFilterList !== false) asideValue[key] = defaultValue;
});
const filterModalRef = ref(null);
const newFilterModalRef = ref(null);
const customModalRef = ref(null);
function showModal(modalRef: any) {
const modal = unref(modalRef)! as any;
modal.showModal();
}
const mousetrap = inject("mousetrap") as any;
mousetrap.bind("[", collapseHandler);
onMounted(() => {
nextTick(() => {
computeSlideHeight();
});
});
const collapse = ref(false);
function collapseHandler() {
collapse.value = !collapse.value;
}
const asideWidth = computed(() => {
return collapse.value ? 0 : 308;
});
const asideHeight = ref(500);
const asideStyle = computed(() => {
return {
width: `${asideWidth.value}px`,
height: `${asideHeight.value}px`,
};
});
const collapseIcon = computed(() => {
return collapse.value ? "expand-cir" : "collapse-cir";
});
function computeSlideHeight() {
const headEl = document.querySelector(".aside-header")!;
const { bottomIncludeBody } = getViewportOffset(headEl);
const height = bottomIncludeBody;
asideHeight.value = height - 24;
}
useWindowSizeFn(computeSlideHeight, 280);
onBeforeMount(async () => {
finalStore.fetchCustomConfig();
});
finalStore.$subscribe(() => {
const customConfig = finalStore.getCustomConfig;
if (customConfig === null) return;
const showKeys: string[] = [...customConfig];
const defaultKeys = Object.keys(asideMap).filter(
(key) => asideMap[key].isDefaultFilter
);
showKeys.unshift(...defaultKeys);
Object.keys(asideMap).forEach((key) => {
// 设置显示的或者默认显示的
if (key.startsWith("iz"))
asideVisible[key] =
asideMap[key] && (showKeys.includes(key) || asideMap[key].isDefaultFilter);
});
const items = showKeys.reduce((acc, key) => {
if (asideMap[key]) {
const config = {
key,
config: asideMap[key],
};
return [...acc, config];
} else {
return acc;
}
}, []);
showItems.value = items;
});
watch(asideVisible, (newVal) => {
Object.keys(asideValue).forEach((key) => {
if (newVal[key] === false) asideValue[key] = asideMap[key].defaultValue;
});
});
const asideEnter = ref(false);
const showCollapse = computed(() => {
return collapse.value ? true : asideEnter.value;
});
const showSearch = ref(false);
function setShowSearch(value: boolean) {
showSearch.value = value;
if(!value){
inputChange('')
}
}
// 滚动容器让key对应模块处于可视区域
function scrollHandler(key: string) {
const element = document.querySelector(`#${key}`);
element?.scrollIntoView(true);
}
function filterHandler(searchId: string) {
emitter.emit("filter-final", searchId);
}
function editFilter(filter: any) {
const modal = unref(newFilterModalRef)! as any;
modal.showModal();
modal.edit(filter);
}
watch(asideValue, (newVal) => {
finalStore.setAsideValue(newVal);
});
const inputChange = (keyword) => {
emit("inputChange", keyword);
};
</script>
<template>
<div
class="aside"
:style="asideStyle"
@mouseenter="asideEnter = true"
@mouseleave="asideEnter = false"
>
<div v-show="showCollapse" class="aside-collapse">
<div class="aside-collapse-btn" @click="collapseHandler">
<SvgIcon :name="collapseIcon" size="40" />
</div>
</div>
<n-scrollbar trigger="none">
<div class="aside-header">
<!-- -->
<Search
v-show="showSearch"
@select="scrollHandler"
@close="setShowSearch(false)"
@inputChange="inputChange"
/>
<!-- 高级筛选 -->
<AdvanceFilter
v-show="!showSearch"
:type="1"
@select="filterHandler"
@update:search="setShowSearch(true)"
@show-custom="showModal(customModalRef)"
@show-filter="showModal(filterModalRef)"
/>
</div>
<component
:is="item.config.component"
v-for="(item, index) in showItems"
:id="item.key"
:key="index"
v-model:value="asideValue[item.key]"
:label="item.config.label"
/>
<!-- 筛选 -->
<CustomFilterModalVue ref="customModalRef" />
<!-- 过滤列表 -->
<FilterModal
ref="filterModalRef"
@edit-filter="editFilter"
@show-new-filter="showModal(newFilterModalRef)"
/>
<!-- 新增过滤 -->
<NewFilterModal ref="newFilterModalRef" />
</n-scrollbar>
</div>
</template>
<style lang="less" scoped>
.aside {
display: flex;
position: relative;
flex-direction: column;
background: #fff;
border: 1px solid #efeff5;
border-radius: 3px;
box-sizing: border-box;
&-header {
padding: 10px;
width: 100%;
border-bottom: 1px solid #e8e8e8;
margin-bottom: 15px;
}
&-divider {
width: 100%;
height: 1px;
background-color: #e8e8e8;
}
&-collapse {
width: 2px;
height: 100%;
background: #507afd;
position: absolute;
right: -2px;
top: 0;
z-index: 18;
}
&-collapse-btn {
position: absolute;
cursor: pointer;
width: 40px;
height: 40px;
top: calc(15%);
right: -20px;
}
::v-deep(.n-collapse
.n-collapse-item.n-collapse-item--right-arrow-placement
.n-collapse-item__header
.n-collapse-item-arrow) {
margin-left: 8px;
}
::v-deep(.n-collapse
.n-collapse-item
.n-collapse-item__header
.n-collapse-item__header-main) {
font-weight: bold;
justify-content: space-between;
}
::v-deep(.n-collapse .n-collapse-item .n-collapse-item__header) {
padding: 0px;
}
::v-deep(.n-collapse .n-collapse-item:not(:first-child)) {
border-top: 0px;
}
::v-deep(.n-collapse
.n-collapse-item
.n-collapse-item__content-wrapper
.n-collapse-item__content-inner) {
padding-top: 10px;
}
::v-deep(.n-scrollbar
> .n-scrollbar-rail.n-scrollbar-rail--vertical
> .n-scrollbar-rail__scrollbar, .n-scrollbar
+ .n-scrollbar-rail.n-scrollbar-rail--vertical
> .n-scrollbar-rail__scrollbar) {
width: 0px;
}
}
</style>