2024-03-26 11:09:12 +08:00

382 lines
9.1 KiB
Vue

<template>
<div id="zoom-control">
<ul v-show="show" class="zoom-selecter">
<li v-for="(item, index) in zoomList" :key="index" :class="['zoom-item', { 'zoom-item-active': activezoomIndex === index }]" @click.stop="selectItem(index)">
<!-- <i v-if="item.icon" :class="['iconfont', item.icon]"></i> -->
<span>{{ item.text }}</span>
<i v-if="activezoomIndex === index" class="iconfont icon-selected"></i>
</li>
</ul>
<div v-if="!hideControl" class="zoom-control-wrap">
<div :class="['zoom-icon radius-left', { disable: activezoomIndex === 0 }]" @click.stop="activezoomIndex > 0 ? sub() : ''">
<i class="iconfont icon-sub"></i>
</div>
<div :class="['zoom-text', { 'zoom-text-active': show }]" @click.stop="show = !show">{{ zoom.text }}</div>
<div :class="['zoom-icon radius-right', { disable: otherIndex === otherList.length - 1 }]" @click.stop="otherIndex < otherList.length - 1 ? add() : ''">
<i class="iconfont icon-add"></i>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { useStore } from 'vuex'
import { nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import addMouseWheel from '@/common/methods/addMouseWheel'
import { OtherList, TZoomData, ZoomList } from './data';
import { useSetupMapGetters } from '@/common/hooks/mapGetters';
import { useRoute } from 'vue-router';
const route = useRoute()
const store = useStore()
// 组件大小控制器
let holder: number | undefined
const hideControl = ref(false)
const activezoomIndex = ref(0)
const zoomList = ref<TZoomData[]>(ZoomList)
const show = ref(false)
const zoom = ref<TZoomData>({
value: 0,
text: '',
})
const otherList = ref<TZoomData[]>(OtherList)
const otherIndex = ref(-1)
const bestZoom = ref(0)
const curAction = ref('')
const { dPage, dScreen, zoomScreenChange, dZoom } = useSetupMapGetters(['dPage', 'dScreen', 'zoomScreenChange', 'dZoom'])
watch(
activezoomIndex,
(data) => {
if (data < 0 || data > zoomList.value.length - 1) {
return
}
zoom.value = JSON.parse(JSON.stringify(zoomList.value[data]))
}
)
watch(
otherIndex,
(data) => {
if (data < 0 || data > otherList.value.length - 1) {
return
}
zoom.value = JSON.parse(JSON.stringify(otherList.value[data]))
}
)
watch(
zoom,
(data) => {
let realValue = data.value
if (realValue === -1) {
realValue = calcZoom()
}
store.dispatch('updateZoom', realValue)
// updateZoom(realValue)
autoFixTop()
}
)
watch(
dScreen,
() => {
screenChange()
},
{ deep: true, }
)
watch(
zoomScreenChange,
() => {
activezoomIndex.value = zoomList.value.length - 1
screenChange()
}
)
watch(
dPage,
() => {
screenChange()
},
{ deep: true }
)
onMounted(async () => {
await nextTick()
window.addEventListener('click', close)
if (route.path === '/draw') {
activezoomIndex.value = 3
hideControl.value = true
} else {
activezoomIndex.value = zoomList.value.length - 1
}
// 添加滚轮监听
addMouseWheel('page-design', (isDown: boolean) => {
mousewheelZoom(isDown)
})
// 添加窗口大小监听
window.addEventListener('resize', (event) => {
changeScreen()
})
})
onBeforeUnmount(() => {
window.removeEventListener('click', close)
})
// ...mapActions(['updateZoom', 'updateScreen']),
function changeScreen() {
clearTimeout(holder)
holder = setTimeout(() => {
const screen = document.getElementById('page-design')
if (!screen) return
store.dispatch('updateScreen', {
width: screen.offsetWidth,
height: screen.offsetHeight,
})
// updateScreen({
// width: screen.offsetWidth,
// height: screen.offsetHeight,
// })
}, 300)
}
function screenChange() {
// 弹性尺寸即时修改
if (activezoomIndex.value === zoomList.value.length - 1) {
store.dispatch('updateZoom', calcZoom())
// this.updateZoom(this.calcZoom())
autoFixTop()
}
}
function selectItem(index: number) {
activezoomIndex.value = index
otherIndex.value = -1
show.value = false
}
function close(_: MouseEvent) {
show.value = false
}
function add() {
curAction.value = 'add'
show.value = false
if (
activezoomIndex.value === zoomList.value.length - 2 ||
activezoomIndex.value === zoomList.value.length - 1
) {
activezoomIndex.value = zoomList.value.length
// this.otherIndex += 1
if (bestZoom.value) {
nearZoom(true)
} else {
otherIndex.value += 1
}
return
}
if (activezoomIndex.value != zoomList.value.length) {
activezoomIndex.value++
return
}
if (otherIndex.value < otherList.value.length - 1) {
otherIndex.value++
}
}
function sub() {
curAction.value = ''
show.value = false
if (otherIndex.value === 0) {
otherIndex.value = -1
activezoomIndex.value = zoomList.value.length - 2
return
}
if (otherIndex.value != -1) {
otherIndex.value--
return
}
if (activezoomIndex.value === zoomList.value.length - 1) {
if (bestZoom) {
nearZoom()
} else {
activezoomIndex.value = zoomList.value.length - 2
}
return
}
if (activezoomIndex.value != 0) {
activezoomIndex.value--
}
}
function mousewheelZoom(down: boolean) {
const value = Number(dZoom.value.toFixed(0))
if (down && value <= 1) return
store.dispatch('updateZoom', down ? value - 1 : value + 1)
// updateZoom(down ? value - 1 : value + 1)
zoom.value.text = (value + '%') as any
autoFixTop()
}
function nearZoom(add?: boolean) {
for (let i = 0; i < zoomList.value.length; i++) {
activezoomIndex.value = i
if (zoomList.value[i].value > bestZoom.value) {
if (add) break
} else if (zoomList.value[i].value < bestZoom.value) {
if (!add) break
}
}
bestZoom.value = 0
}
function calcZoom() {
let widthZoom = ((dScreen.value.width - 142) * 100) / dPage.value.width
let heightZoom = ((dScreen.value.height - 122) * 100) / dPage.value.height
bestZoom.value = Math.min(widthZoom, heightZoom)
return bestZoom.value
}
async function autoFixTop() {
await nextTick()
const presetPadding = 60
const el = document.getElementById('out-page')
if (!el) return
const clientHeight = window.innerHeight - 54
// const parentHeight = (el.offsetParent as HTMLElement).offsetHeight - 54
let padding = (clientHeight - el.offsetHeight) / 2
if (typeof curAction.value === 'undefined') {
padding += presetPadding / 2
}
curAction.value === 'add' && (padding -= presetPadding)
store.commit('updatePaddingTop', padding > 0 ? padding : 0)
}
defineExpose({
screenChange
})
</script>
<style lang="less" scoped>
@color-select: #1b1634;
@color1: #ffffff; // 选项板背景
@color2: #ffffff; // Appears 3 times
@color3: #666666; // 文字主颜色
@color4: #c2c2c2; // 禁用
@color5: rgba(0, 0, 0, 0.12); // 高亮选项背景
@z-border-color: #e6e6e6;
#zoom-control {
bottom: 20px;
position: absolute;
right: 302px;
z-index: 1000;
.zoom-control-wrap {
display: flex;
flex-direction: row;
font-size: 14px;
height: 40px;
.radius-left {
border-bottom-left-radius: 50%;
border-top-left-radius: 50%;
border-block-end: 1px solid @z-border-color;
border-block-start: 1px solid @z-border-color;
}
.radius-right {
border-bottom-right-radius: 50%;
border-top-right-radius: 50%;
border-block-end: 1px solid @z-border-color;
border-block-start: 1px solid @z-border-color;
}
.zoom-icon {
align-items: center;
background-color: @color2;
color: @color3;
cursor: pointer;
display: flex;
justify-content: center;
width: 40px;
&:hover {
background-color: @color1;
color: @color-select;
}
}
.disable {
color: @color4;
&:hover {
background-color: @color2;
color: @color4;
cursor: not-allowed;
}
}
.zoom-text {
user-select: none;
align-items: center;
background-color: @color2;
color: @color3;
cursor: pointer;
display: flex;
justify-content: center;
width: 60px;
border-block-end: 1px solid @z-border-color;
border-block-start: 1px solid @z-border-color;
&:hover {
background-color: @color1;
color: @color-select;
}
}
}
.zoom-selecter {
background-color: @color1;
color: @color3;
position: absolute;
top: -8px;
transform: translateY(-100%);
width: 100%;
z-index: 1000;
&:after {
bottom: -8px;
content: '';
left: 50%;
position: absolute;
transform: translateX(-50%);
}
.zoom-item {
align-items: center;
cursor: pointer;
display: flex;
font-size: 14px;
height: 34px;
padding: 10px;
width: 100%;
i {
margin-right: 10px;
&:last-child {
margin-right: 0;
}
}
span {
flex: 1;
}
&:hover {
background-color: @color5;
color: @color-select;
}
}
}
}
// #zoom-control-active {
// background-color: @color1;
// background-color: @color5;
// color: @color-select;
// color: @color-select;
// }
</style>