采用第一版picker
This commit is contained in:
parent
a89448cc7d
commit
8e9c8f51dd
@ -22,6 +22,7 @@
|
|||||||
"axios": "^1.4.0",
|
"axios": "^1.4.0",
|
||||||
"clipboard": "^2.0.11",
|
"clipboard": "^2.0.11",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
|
"default-passive-events": "^2.0.0",
|
||||||
"echarts": "^5.5.0",
|
"echarts": "^5.5.0",
|
||||||
"element-plus": "^2.8.3",
|
"element-plus": "^2.8.3",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
|
24
web/src/management/pages/edit/components/Picker/event.ts
Normal file
24
web/src/management/pages/edit/components/Picker/event.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
|
||||||
|
interface IEvent {
|
||||||
|
handleConfirm: () => void,
|
||||||
|
handleCancel: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const useEvent = ({ emit, ctx }: any): IEvent => {
|
||||||
|
const handleConfirm = () => {
|
||||||
|
emit('confirm', ctx.list[ctx.index])
|
||||||
|
emit('update:modelValue', false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
emit('update:modelValue', false)
|
||||||
|
emit('cancel', false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
handleConfirm,
|
||||||
|
handleCancel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useEvent
|
@ -1,468 +1,206 @@
|
|||||||
<template>
|
<template>
|
||||||
<Transition name="fade">
|
<div class="picker-wrapper" @click.stop >
|
||||||
<div class="pickerbox" v-show="props.visible" @click="clickMask">
|
<div class="picker-mask" v-show="modelValue" @click.stop="handleCancel"></div>
|
||||||
<Transition name="toup">
|
<transition name="slide-picker" >
|
||||||
<div class="vue-picker" ref="picker" v-show="props.visible">
|
<div class="x-picker" @click.stop v-show="modelValue">
|
||||||
<PickerHeader v-if="showToolbar" :cancelText="props.cancelText" :confirmText="props.confirmText" :title="props.title" @cancel="cancel"
|
<div class="x-picker__header">
|
||||||
@confirm="confirm" />
|
<p class="x-picker__header-left" @click.stop="handleCancel">取消</p>
|
||||||
<div class="content" :style="{ height: boxHeight + 'px' }">
|
<p class="x-picker__header-right" @click.stop="handleConfirm">确定</p>
|
||||||
<div class="colums">
|
|
||||||
<PickerList :column="state.column1" :boxHeight="boxHeight" :itemHeight="props.itemHeight" :defaultIndex="state.dIndex1"
|
|
||||||
:rowNumber="getRowNumber" @change="change1" />
|
|
||||||
<PickerList v-if="state.column2.length > 0" :column="state.column2" :boxHeight="boxHeight" :itemHeight="props.itemHeight"
|
|
||||||
:defaultIndex="state.dIndex2" :rowNumber="getRowNumber" @change="change2" />
|
|
||||||
<PickerList v-if="state.column3.length > 0" :column="state.column3" :boxHeight="boxHeight" :itemHeight="props.itemHeight"
|
|
||||||
:defaultIndex="state.dIndex3" :rowNumber="getRowNumber" @change="change3" />
|
|
||||||
<PickerList v-if="state.column4.length > 0" :column="state.column4" :boxHeight="boxHeight" :itemHeight="props.itemHeight"
|
|
||||||
:defaultIndex="state.dIndex4" :rowNumber="getRowNumber" @change="change4" />
|
|
||||||
</div>
|
</div>
|
||||||
<div class="mask" :style="maskStyle"></div>
|
<div class="x-picker__content">
|
||||||
<div class="hairline"></div>
|
<div class="x-picker__content-wrapper"></div>
|
||||||
|
<ul class="x-picker__content-box" ref="box" :style="getOffsetY" >
|
||||||
|
<li class="x-picker__content-item" v-for="(item, idx) in list" :key="idx" :style="getStyle(Number(idx))">
|
||||||
|
{{ item?.text }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script>
|
||||||
import { reactive, computed, nextTick, ref, watch,onBeforeMount, onMounted, onBeforeUnmount } from 'vue'
|
import { defineComponent, getCurrentInstance,watch,onMounted,onBeforeUnmount } from 'vue'
|
||||||
import PickerHeader from './pickerHeader.vue'
|
import useList from './list'
|
||||||
import PickerList from './pickerList.vue'
|
import useEvent from './event'
|
||||||
import { DEFTAULT_ITEM_HEIGHT } from './utils'
|
|
||||||
|
|
||||||
const props = defineProps({
|
export default defineComponent({
|
||||||
visible: {
|
props: {
|
||||||
type: Boolean,
|
list: {
|
||||||
default: false
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => []
|
default: () => []
|
||||||
},
|
},
|
||||||
layer: {
|
modelValue: {
|
||||||
type: Number,
|
|
||||||
default: 0
|
|
||||||
},
|
|
||||||
itemHeight: {
|
|
||||||
type: [Number, String],
|
|
||||||
default: DEFTAULT_ITEM_HEIGHT
|
|
||||||
},
|
|
||||||
defaultIndex: {
|
|
||||||
type: [Number, Array],
|
|
||||||
default: 0
|
|
||||||
},
|
|
||||||
cancelText: {
|
|
||||||
type: String,
|
|
||||||
default: '取消'
|
|
||||||
},
|
|
||||||
confirmText: {
|
|
||||||
type: String,
|
|
||||||
default: '确认'
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
showToolbar: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
maskClick: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
rowNumber: {
|
|
||||||
type: Number,
|
|
||||||
default: 5
|
|
||||||
},
|
|
||||||
appendToBody: {
|
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const { ctx } = getCurrentInstance()
|
||||||
|
const $useList = useList(props)
|
||||||
|
const $useEvent = useEvent({ emit, ctx })
|
||||||
|
|
||||||
const emit = defineEmits(['update:visible', 'cancel', 'confirm', 'change'])
|
|
||||||
|
|
||||||
const state = reactive({
|
const hideOverflow = () => {
|
||||||
column1: [],
|
document.documentElement.style.overflow = 'hidden';
|
||||||
column2: [],
|
|
||||||
column3: [],
|
|
||||||
column4: [],
|
|
||||||
dIndex1: 0,
|
|
||||||
dIndex2: 0,
|
|
||||||
dIndex3: 0,
|
|
||||||
dIndex4: 0,
|
|
||||||
indexArr: [],
|
|
||||||
result: [],
|
|
||||||
prevBodyCss: ''
|
|
||||||
})
|
|
||||||
const picker = ref(null)
|
|
||||||
|
|
||||||
const getRowNumber = computed(() => {
|
|
||||||
if (props.rowNumber < 3) {
|
|
||||||
return 3
|
|
||||||
}
|
}
|
||||||
return props.rowNumber % 2 === 0 ? props.rowNumber + 1 : props.rowNumber
|
|
||||||
})
|
|
||||||
|
|
||||||
const boxHeight = computed(() => {
|
const restoreOverflow = () => {
|
||||||
let itemHeight = parseInt(props.itemHeight)
|
document.documentElement.style.overflow = '';
|
||||||
itemHeight = itemHeight ? itemHeight : DEFTAULT_ITEM_HEIGHT
|
|
||||||
return itemHeight * getRowNumber.value
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
const maskStyle = computed(() => {
|
|
||||||
let style = { backgroundSize: '100% 88px' }
|
|
||||||
if (getRowNumber.value === 3) {
|
|
||||||
style = { backgroundSize: '100% 44px' }
|
|
||||||
}
|
}
|
||||||
return style
|
|
||||||
})
|
|
||||||
|
|
||||||
const clickMask = () => {
|
onMounted(() => {
|
||||||
if (props.maskClick) {
|
const { box, handleStart, handleMove, handleEnd,isTouch } = $useList;
|
||||||
looseBody()
|
const boxElement = box.value
|
||||||
emit('update:visible', false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const formateData = () => {
|
if (isTouch) {
|
||||||
if (props.layer > 1) {
|
boxElement.addEventListener('touchstart', handleStart, { passive: true })
|
||||||
setLinkColumn()
|
boxElement.addEventListener('touchmove', handleMove, { passive: true })
|
||||||
|
boxElement.addEventListener('touchend', handleEnd)
|
||||||
} else {
|
} else {
|
||||||
state.column1 = props.data[0] || []
|
boxElement.addEventListener('mousedown', handleStart)
|
||||||
state.column2 = props.data[1] || []
|
boxElement.addEventListener('mousemove', handleMove)
|
||||||
state.column3 = props.data[2] || []
|
boxElement.addEventListener('mouseup', handleEnd)
|
||||||
state.column4 = props.data[3] || []
|
|
||||||
setNormalIndex()
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
const setLinkColumn = () => {
|
onBeforeUnmount(() => {
|
||||||
if (props.layer === 2) {
|
const { box, handleStart, handleMove, handleEnd,isTouch } = $useList;
|
||||||
setLinkLayer2()
|
const boxElement = box.value;
|
||||||
} else if (props.layer === 3) {
|
if (isTouch) {
|
||||||
setLinkLayer2()
|
boxElement.removeEventListener('touchstart', handleStart)
|
||||||
setLinkLayer3()
|
boxElement.removeEventListener('touchmove', handleMove)
|
||||||
} else if (props.layer === 4) {
|
boxElement.removeEventListener('touchend', handleEnd)
|
||||||
setLinkLayer2()
|
|
||||||
setLinkLayer3()
|
|
||||||
setLinkLayer4()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const setLinkLayer2 = () => {
|
|
||||||
const { defaultIndex } = props
|
|
||||||
state.column1 = props.data || []
|
|
||||||
if (typeof defaultIndex === 'number') {
|
|
||||||
state.dIndex1 = defaultIndex
|
|
||||||
state.dIndex2 = 0
|
|
||||||
if (props.data.length > 1 && props.data[0].children) {
|
|
||||||
state.column2 = props.data[0].children || []
|
|
||||||
}
|
|
||||||
} else if (Array.isArray(defaultIndex) && defaultIndex.length > 0) {
|
|
||||||
state.dIndex1 = defaultIndex[0] || 0
|
|
||||||
state.column2 = props.data[state.dIndex1].children || []
|
|
||||||
nextTick(() => {
|
|
||||||
if (state.column2.length - 1 < defaultIndex[1]) {
|
|
||||||
state.dIndex2 = state.column2.length - 1
|
|
||||||
} else {
|
} else {
|
||||||
state.dIndex2 = defaultIndex[1] || 0
|
boxElement.removeEventListener('mousedown', handleStart)
|
||||||
|
boxElement.removeEventListener('mousemove', handleMove)
|
||||||
|
boxElement.removeEventListener('mouseup', handleEnd)
|
||||||
|
}
|
||||||
|
restoreOverflow()
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(()=>props.list,()=>{
|
||||||
|
$useList.resetData(0)
|
||||||
|
$useList.list.value = props.list
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(() => props.modelValue, (val) => {
|
||||||
|
if (val) {
|
||||||
|
hideOverflow()
|
||||||
|
}else {
|
||||||
|
restoreOverflow()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
...$useList,
|
||||||
|
...$useEvent
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const setLinkLayer3 = () => {
|
|
||||||
const { defaultIndex } = props
|
|
||||||
if (typeof defaultIndex === 'number') {
|
|
||||||
state.dIndex3 = 0
|
|
||||||
if (state.column2.length > 1 && state.column2[0].children) {
|
|
||||||
state.column3 = state.column2[0].children || []
|
|
||||||
}
|
|
||||||
} else if (Array.isArray(defaultIndex) && defaultIndex.length > 1) {
|
|
||||||
nextTick(() => {
|
|
||||||
state.column3 = state.column2[state.dIndex2].children || []
|
|
||||||
nextTick(() => {
|
|
||||||
if (state.column3.length - 1 < defaultIndex[2]) {
|
|
||||||
state.dIndex3 = state.column3.length - 1
|
|
||||||
} else {
|
|
||||||
state.dIndex3 = defaultIndex[2] || 0
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const setLinkLayer4 = () => {
|
|
||||||
const { defaultIndex } = props
|
|
||||||
if (typeof defaultIndex === 'number') {
|
|
||||||
state.dIndex4 = 0
|
|
||||||
if (state.column3.length > 1 && state.column3[0].children) {
|
|
||||||
state.column4 = state.column3[0].children || []
|
|
||||||
}
|
|
||||||
} else if (Array.isArray(defaultIndex) && defaultIndex.length > 2) {
|
|
||||||
setTimeout(() => {
|
|
||||||
state.column4 = state.column3[state.dIndex3].children || []
|
|
||||||
nextTick(() => {
|
|
||||||
if (state.column4.length - 1 < defaultIndex[3]) {
|
|
||||||
state.dIndex4 = state.column4.length - 1
|
|
||||||
} else {
|
|
||||||
state.dIndex4 = defaultIndex[3] || 0
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const setNormalIndex = () => {
|
|
||||||
nextTick(() => {
|
|
||||||
const { defaultIndex } = props
|
|
||||||
if (Array.isArray(defaultIndex)) {
|
|
||||||
setDefaultIndex()
|
|
||||||
} else {
|
|
||||||
state.dIndex1 = Number(defaultIndex) || 0
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const setDefaultIndex = () => {
|
|
||||||
const { indexArr } = state
|
|
||||||
function next() {
|
|
||||||
let promise = Promise.resolve()
|
|
||||||
let index = 0
|
|
||||||
while (index < props.data.length) {
|
|
||||||
promise = promise.then(indexArr[index])
|
|
||||||
index++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
next()
|
|
||||||
}
|
|
||||||
|
|
||||||
const change = (index, res) => {
|
|
||||||
state.result[index] = res
|
|
||||||
emit('change', state.result)
|
|
||||||
}
|
|
||||||
|
|
||||||
const change1 = (res) => {
|
|
||||||
if (res) {
|
|
||||||
change(0, res)
|
|
||||||
if (props.layer > 1) {
|
|
||||||
state.dIndex2 = 0
|
|
||||||
changeLink('column2', res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const change2 = (res) => {
|
|
||||||
if (res) {
|
|
||||||
change(1, res)
|
|
||||||
if (props.layer > 2) {
|
|
||||||
state.dIndex3 = 0
|
|
||||||
changeLink('column3', res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const change3 = (res) => {
|
|
||||||
if (res) {
|
|
||||||
change(2, res)
|
|
||||||
if (props.layer > 3) {
|
|
||||||
state.dIndex4 = 0
|
|
||||||
changeLink('column4', res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const change4 = (res) => {
|
|
||||||
if (res) {
|
|
||||||
change(3, res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const changeLink = (key, res) => {
|
|
||||||
if (props.layer) {
|
|
||||||
setTimeout(() => {
|
|
||||||
state[key] = res.children || []
|
|
||||||
}, 1000 / 60)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const cancel = () => {
|
|
||||||
looseBody()
|
|
||||||
emit('cancel')
|
|
||||||
emit('update:visible', false)
|
|
||||||
}
|
|
||||||
|
|
||||||
const confirm = () => {
|
|
||||||
looseBody()
|
|
||||||
emit('confirm', state.result)
|
|
||||||
emit('update:visible', false)
|
|
||||||
}
|
|
||||||
|
|
||||||
const stopPropagation = (e) => {
|
|
||||||
e.stopPropagation()
|
|
||||||
}
|
|
||||||
|
|
||||||
const fixedBody = () => {
|
|
||||||
const scrollTop = document.body.scrollTop || document.documentElement.scrollTop
|
|
||||||
state.prevBodyCss = document.body.style.cssText
|
|
||||||
document.body.style.cssText += 'position:fixed;width:100%;top:-' + scrollTop + 'px;'
|
|
||||||
}
|
|
||||||
|
|
||||||
const looseBody = () => {
|
|
||||||
const body = document.body
|
|
||||||
const top = body.style.top
|
|
||||||
body.style.cssText = state.prevBodyCss
|
|
||||||
body.scrollTop = document.documentElement.scrollTop = -parseInt(top)
|
|
||||||
body.style.top = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const init = () => {
|
|
||||||
state.result = []
|
|
||||||
state.indexArr = [
|
|
||||||
() => state.dIndex1 = props.defaultIndex[0] || 0,
|
|
||||||
() => state.dIndex2 = props.defaultIndex[1] || 0,
|
|
||||||
() => state.dIndex3 = props.defaultIndex[2] || 0,
|
|
||||||
() => state.dIndex4 = props.defaultIndex[3] || 0
|
|
||||||
]
|
|
||||||
formateData()
|
|
||||||
}
|
|
||||||
|
|
||||||
onBeforeMount(() => {
|
|
||||||
init()
|
|
||||||
})
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
picker.value.removeEventListener('click', stopPropagation)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
picker.value.addEventListener('click', stopPropagation)
|
|
||||||
if (props.appendToBody) {
|
|
||||||
// document.body.appendChild(this.$el)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(() => props.visible, (v) => {
|
|
||||||
if (v) {
|
|
||||||
fixedBody()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(() => props.defaultIndex, () => {
|
|
||||||
init()
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(() => props.data, () => {
|
|
||||||
init()
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.pickerbox {
|
.x-picker {
|
||||||
position: fixed;
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background: rgba(0, 0, 0, 0.7);
|
|
||||||
z-index: 9999;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fade-enter-active,
|
|
||||||
.fade-leave-active {
|
|
||||||
transition: opacity 0.5s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fade-enter-from,
|
|
||||||
.fade-leave-to {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toup-enter-active,
|
|
||||||
.toup-leave-active {
|
|
||||||
transition: transform .3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toup-enter,
|
|
||||||
.toup-leave-to {
|
|
||||||
transform: translate3d(0, 100px, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----
|
|
||||||
.vue-picker {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: #fff;
|
position: fixed;
|
||||||
user-select: none;
|
|
||||||
-webkit-text-size-adjust: 100%;
|
|
||||||
-webkit-tap-highlight-color: transparent;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
overflow: hidden;
|
|
||||||
height: 220px;
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.colums {
|
|
||||||
display: flex;
|
|
||||||
overflow: hidden;
|
|
||||||
font-size: 16px;
|
|
||||||
text-align: center;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mask {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
left: 0;
|
||||||
z-index: 2;
|
bottom: 0;
|
||||||
|
background-color: #fff;
|
||||||
|
z-index: 99;
|
||||||
|
}
|
||||||
|
.picker-mask {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-image: linear-gradient(180deg, hsla(0, 0%, 100%, 0.9), hsla(0, 0%, 100%, 0.4)), linear-gradient(0deg, hsla(0, 0%, 100%, 0.9), hsla(0, 0%, 100%, 0.4));
|
display: flex;
|
||||||
background-repeat: no-repeat;
|
flex-direction: column;
|
||||||
background-position: top, bottom;
|
align-items: center;
|
||||||
backface-visibility: hidden;
|
justify-content: center;
|
||||||
pointer-events: none;
|
background: rgba(0,0,0,0.50);
|
||||||
background-size: 100% 88px;
|
z-index: 98;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hairline {
|
.x-picker__header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 10px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.x-picker__header>p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.x-picker__header-left {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #6E707C;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.x-picker__header-center {
|
||||||
|
color: gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.x-picker__header-right {
|
||||||
|
font-size: 14px;
|
||||||
|
color: $primary-color;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.x-picker__content {
|
||||||
|
height: 230px;
|
||||||
|
padding: 15px 0;
|
||||||
|
margin: 0 24px;
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.x-picker__content-box {
|
||||||
|
flex: 1;
|
||||||
|
width: 100%;
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
transition: all .2s;
|
||||||
|
font-size: 20px;
|
||||||
|
transition-timing-function: cubic-bezier(0.23, 1, 0.68, 1);
|
||||||
|
transform: translate(-50%, -20px);
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.x-picker__content-wrapper {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 0;
|
transform: translateY(-18px);
|
||||||
z-index: 3;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
transform: translateY(-50%);
|
height: 40px;
|
||||||
pointer-events: none;
|
border-top: 1px solid #E3E4E8;
|
||||||
height: 44px;
|
border-bottom: 1px solid #E3E4E8;
|
||||||
|
}
|
||||||
|
|
||||||
&::after {
|
.x-picker__content-item {
|
||||||
position: absolute;
|
height: 40px;
|
||||||
box-sizing: border-box;
|
line-height: 40px;
|
||||||
content: ' ';
|
transition: color .5s;
|
||||||
pointer-events: none;
|
text-align: center
|
||||||
top: -50%;
|
}
|
||||||
right: -50%;
|
|
||||||
bottom: -50%;
|
.slide-picker-enter-active,
|
||||||
left: -50%;
|
.slide-picker-leave-active {
|
||||||
border: 0 solid #E3E4E8;
|
transition: all .5s;
|
||||||
-webkit-transform: scale(0.5);
|
}
|
||||||
transform: scale(0.5);
|
|
||||||
border-width: 1.5px 0;
|
.slide-picker-enter-from,
|
||||||
margin:0 34px
|
.slide-picker-leave-to {
|
||||||
}
|
transform: translateY(100%);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
145
web/src/management/pages/edit/components/Picker/list.ts
Normal file
145
web/src/management/pages/edit/components/Picker/list.ts
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
import { ref, computed } from 'vue'
|
||||||
|
import type { Ref } from 'vue'
|
||||||
|
|
||||||
|
interface IList {
|
||||||
|
box: Ref,
|
||||||
|
list: Ref<Array<any>>,
|
||||||
|
getOffsetY: any,
|
||||||
|
getStyle: any,
|
||||||
|
handleMove: (e: TouchEvent) => void,
|
||||||
|
handleStart: (e: TouchEvent) => void,
|
||||||
|
handleEnd: (e: TouchEvent) => void,
|
||||||
|
goItem: (idx: number) => void,
|
||||||
|
resetData: () => void,
|
||||||
|
index: Ref<number>,
|
||||||
|
isTouch:boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const useList = (props: any): IList => {
|
||||||
|
const colors = ['gray', '#ccc', '#ddd', '#eee']
|
||||||
|
const scales = [.96, .9, .88, .84]
|
||||||
|
const isTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0
|
||||||
|
let startY: number, activeIndex = 0, isDragging = false
|
||||||
|
const box = ref()
|
||||||
|
const offY = ref()
|
||||||
|
const index = ref(0)
|
||||||
|
const list = ref(props.list)
|
||||||
|
const getStyle = (idx: number) => {
|
||||||
|
let color = '#000', scale = 1
|
||||||
|
const len = colors.length - 1
|
||||||
|
if (idx > activeIndex) {
|
||||||
|
const _idx = idx - activeIndex > len ? len : idx - activeIndex - 1
|
||||||
|
color = colors[_idx]
|
||||||
|
scale = scales[_idx]
|
||||||
|
} else if (idx < activeIndex) {
|
||||||
|
const _idx = activeIndex - idx > len ? len : activeIndex - idx - 1
|
||||||
|
color = colors[_idx]
|
||||||
|
scale = scales[_idx]
|
||||||
|
}
|
||||||
|
return { color, transform: `scale(${scale})` }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 节流
|
||||||
|
const throttle = function (callback: any, delay = 20) {
|
||||||
|
let timer: any = null
|
||||||
|
return function (args: any) {
|
||||||
|
if (timer) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
callback(args)
|
||||||
|
timer = null
|
||||||
|
}, delay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移动的实现
|
||||||
|
const move = throttle((e: any) => {
|
||||||
|
const clientY = e.touches ? e.touches[0].clientY : e.clientY;
|
||||||
|
offY.value = clientY - startY
|
||||||
|
if (offY.value > 40) {
|
||||||
|
offY.value = 40
|
||||||
|
} else if (offY.value < -box.value.offsetHeight - 40) {
|
||||||
|
offY.value = -box.value.offsetHeight - 40
|
||||||
|
}
|
||||||
|
// 计算当前位置的就近下标
|
||||||
|
index.value = Math.abs(Math.ceil(offY.value / 40))
|
||||||
|
// 判断顶部和底部的一个界限,然后做一个位置的重置
|
||||||
|
if (index.value <= 0 || offY.value > 0) {
|
||||||
|
index.value = 0
|
||||||
|
} else if (index.value > list.value.length - 1 || offY.value < -box.value.offsetHeight - 18) {
|
||||||
|
index.value = list.value.length - 1
|
||||||
|
}
|
||||||
|
activeIndex = index.value
|
||||||
|
})
|
||||||
|
|
||||||
|
const goItem = (idx: number) => {
|
||||||
|
index.value = idx;
|
||||||
|
activeIndex = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
const resetData = () => {
|
||||||
|
startY = 0;
|
||||||
|
activeIndex = 0
|
||||||
|
index.value = 0
|
||||||
|
box.value = null;
|
||||||
|
offY.value = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleStart = (e: MouseEvent | TouchEvent) => {
|
||||||
|
isDragging = true
|
||||||
|
const transform = box.value.style.transform
|
||||||
|
transform.match(/,(.*)px/)
|
||||||
|
const clientY = e instanceof MouseEvent ? e.clientY : e.touches[0].clientY
|
||||||
|
startY = clientY - Number(RegExp.$1)
|
||||||
|
|
||||||
|
document.addEventListener('mousemove', handleMove)
|
||||||
|
document.addEventListener('mouseup', handleEnd)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleMove = (e: MouseEvent | TouchEvent) => {
|
||||||
|
if (isDragging) {
|
||||||
|
move(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEnd = () => {
|
||||||
|
// 重置当前位置,加setTimeout避免出现Bug
|
||||||
|
isDragging = false
|
||||||
|
setTimeout(() => {
|
||||||
|
offY.value = -index.value * 40 - 18
|
||||||
|
}, 100)
|
||||||
|
document.removeEventListener('mousemove', handleMove)
|
||||||
|
document.removeEventListener('mouseup', handleEnd)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getOffsetY = computed(() => {
|
||||||
|
if (typeof offY.value === 'number') {
|
||||||
|
return {
|
||||||
|
transform: `translate(-50%, ${offY.value}px)`
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
transform: 'translate(-50%, -18px)'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
box,
|
||||||
|
list,
|
||||||
|
getOffsetY,
|
||||||
|
getStyle,
|
||||||
|
handleMove,
|
||||||
|
handleStart,
|
||||||
|
handleEnd,
|
||||||
|
goItem,
|
||||||
|
resetData,
|
||||||
|
index,
|
||||||
|
isTouch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useList
|
||||||
|
|
@ -1,83 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="header">
|
|
||||||
<div class="left">
|
|
||||||
<span class="btn cancel" @click="cancel">{{ props.cancelText }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="title">{{ props.title }}</div>
|
|
||||||
<div class="right">
|
|
||||||
<span class="btn confirm" @click="confirm">{{ props.confirmText }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
cancelText: {
|
|
||||||
type: String
|
|
||||||
},
|
|
||||||
confirmText: {
|
|
||||||
type: String
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
type: String
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const emit = defineEmits(['cancel', 'confirm'])
|
|
||||||
|
|
||||||
const cancel = () => {
|
|
||||||
emit('cancel')
|
|
||||||
}
|
|
||||||
|
|
||||||
const confirm = () => {
|
|
||||||
emit('confirm')
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.header {
|
|
||||||
height: 44px;
|
|
||||||
line-height: 44px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
position: absolute;
|
|
||||||
box-sizing: border-box;
|
|
||||||
content: ' ';
|
|
||||||
pointer-events: none;
|
|
||||||
top: -50%;
|
|
||||||
right: -50%;
|
|
||||||
bottom: -50%;
|
|
||||||
left: -50%;
|
|
||||||
border: 0 solid #ebedf0;
|
|
||||||
transform: scale(0.5);
|
|
||||||
border-width: 1px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
max-width: 50%;
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 16px;
|
|
||||||
text-align: center;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
padding: 0 16px;
|
|
||||||
font-size: 14px;
|
|
||||||
background-color: transparent;
|
|
||||||
&.cancel{
|
|
||||||
color: #6E707C;
|
|
||||||
}
|
|
||||||
&.confirm{
|
|
||||||
color:$primary-color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,250 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="list" ref="list">
|
|
||||||
<ul :style="state.ulStyle">
|
|
||||||
<li v-for="(item, index) in props.column" :key="'item' + index" >{{ item.text }}</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { reactive, computed, onMounted, ref, watch, onBeforeUnmount } from 'vue'
|
|
||||||
import { getClient, START_EVENT, MOVE_EVENT, END_EVENT,isPC } from './utils'
|
|
||||||
|
|
||||||
const DEFAULT_DURATION = 200
|
|
||||||
const LIMIT_TIME = 300
|
|
||||||
const LIMIT_DISTANCE = 15
|
|
||||||
const IS_PC = isPC()
|
|
||||||
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
defaultIndex: {
|
|
||||||
type: Number,
|
|
||||||
default: 0
|
|
||||||
},
|
|
||||||
column: {
|
|
||||||
type: Array,
|
|
||||||
default: () => ([])
|
|
||||||
},
|
|
||||||
boxHeight: Number,
|
|
||||||
itemHeight: Number,
|
|
||||||
rowNumber: Number
|
|
||||||
})
|
|
||||||
|
|
||||||
const emit = defineEmits(['change'])
|
|
||||||
|
|
||||||
const state = reactive({
|
|
||||||
ulStyle: {
|
|
||||||
transform: `translate3d(0px, 0px, 0px)`,
|
|
||||||
transitionDuration: `0ms`,
|
|
||||||
transitionProperty: `none`,
|
|
||||||
lineHeight: `${props.itemHeight}px`
|
|
||||||
},
|
|
||||||
bottom: 0,
|
|
||||||
top: 0,
|
|
||||||
startTop: 0,
|
|
||||||
selectIndex: 0,
|
|
||||||
touchStartTime: 0,
|
|
||||||
startY: 0,
|
|
||||||
momentumTop: 0,
|
|
||||||
disY: 0,
|
|
||||||
})
|
|
||||||
|
|
||||||
let wheelTimer = null;
|
|
||||||
|
|
||||||
const list = ref(null)
|
|
||||||
|
|
||||||
const count = computed(() => {
|
|
||||||
return props.column.length
|
|
||||||
})
|
|
||||||
|
|
||||||
const getRoNumber = computed(() => {
|
|
||||||
return Math.floor(props.rowNumber / 2)
|
|
||||||
})
|
|
||||||
|
|
||||||
const init = () => {
|
|
||||||
setTop(props.defaultIndex)
|
|
||||||
const halfBox = (props.boxHeight - props.itemHeight) / 2
|
|
||||||
state.bottom = halfBox + props.itemHeight
|
|
||||||
state.top = halfBox - count.value * props.itemHeight
|
|
||||||
}
|
|
||||||
|
|
||||||
const setTop = (index = 0) => {
|
|
||||||
const { boxHeight, itemHeight } = props;
|
|
||||||
state.startTop = ((boxHeight - itemHeight) / 2) - (index * itemHeight)
|
|
||||||
state.ulStyle.transform = `translate3d(0px, ${state.startTop}px, 0px)`
|
|
||||||
state.selectIndex = index
|
|
||||||
change()
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleStart = (e) => {
|
|
||||||
state.touchStartTime = Date.now()
|
|
||||||
// ----
|
|
||||||
state.startY = getClient(e).y
|
|
||||||
state.momentumTop = state.startTop
|
|
||||||
|
|
||||||
state.ulStyle.transitionDuration = `0ms`
|
|
||||||
state.ulStyle.transitionProperty = `none`
|
|
||||||
if (IS_PC) {
|
|
||||||
document.addEventListener(MOVE_EVENT, handleMove, false)
|
|
||||||
document.addEventListener(END_EVENT, handleEnd, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const handleMove = (e) => {
|
|
||||||
e.preventDefault()
|
|
||||||
e.stopPropagation()
|
|
||||||
state.disY = getClient(e).y - state.startY
|
|
||||||
state.startY = getClient(e).y
|
|
||||||
if (state.startTop >= state.bottom) {
|
|
||||||
state.startTop = state.bottom
|
|
||||||
} else if (state.startTop <= state.top) {
|
|
||||||
state.startTop = state.top
|
|
||||||
} else {
|
|
||||||
state.startTop += state.disY
|
|
||||||
}
|
|
||||||
state.ulStyle.transform = `translate3d(0px, ${state.startTop}px, 0px)`
|
|
||||||
const now = Date.now()
|
|
||||||
|
|
||||||
if (now - state.touchStartTime > LIMIT_TIME) {
|
|
||||||
state.touchStartTime = now
|
|
||||||
state.momentumTop = state.startTop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleEnd = () => {
|
|
||||||
if (IS_PC) {
|
|
||||||
document.removeEventListener(MOVE_EVENT, handleMove, false)
|
|
||||||
document.removeEventListener(END_EVENT, handleEnd, false)
|
|
||||||
}
|
|
||||||
const distance = state.startTop - state.momentumTop
|
|
||||||
const duration = Date.now() - state.touchStartTime
|
|
||||||
const allowMomentum = duration < LIMIT_TIME && Math.abs(distance) > LIMIT_DISTANCE
|
|
||||||
if (allowMomentum) {
|
|
||||||
toMove(distance, duration)
|
|
||||||
} else {
|
|
||||||
setTranfromTop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const setTranfromTop = () => {
|
|
||||||
state.ulStyle.transitionProperty = `all`
|
|
||||||
state.ulStyle.transitionDuration = `${DEFAULT_DURATION}ms`
|
|
||||||
if (state.startTop >= state.bottom - props.itemHeight) {
|
|
||||||
setTop()
|
|
||||||
} else if (state.startTop <= state.top + props.itemHeight) {
|
|
||||||
setTop(count.value - 1)
|
|
||||||
} else {
|
|
||||||
let index = Math.round((state.startTop) / props.itemHeight)
|
|
||||||
state.startTop = index * props.itemHeight
|
|
||||||
if (state.startTop > state.bottom) {
|
|
||||||
state.startTop = state.bottom - props.itemHeight
|
|
||||||
index = -getRoNumber.value
|
|
||||||
} else if (state.startTop < state.top) {
|
|
||||||
state.startTop = state.top + props.itemHeight
|
|
||||||
index = count.value + 1
|
|
||||||
}
|
|
||||||
state.ulStyle.transform = `translate3d(0px, ${state.startTop}px, 0px)`
|
|
||||||
index = getRoNumber.value - index
|
|
||||||
if (state.selectIndex !== index) {
|
|
||||||
state.selectIndex = index
|
|
||||||
change()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const toMove = (distance, duration) => {
|
|
||||||
const speed = Math.abs(distance / duration)
|
|
||||||
distance = state.startTop + (speed / 0.002) * (distance < 0 ? -1 : 1)
|
|
||||||
state.ulStyle.transitionProperty = `all`
|
|
||||||
state.ulStyle.transitionDuration = `1000ms`
|
|
||||||
setTop(Math.min(Math.max(Math.round(-distance / props.itemHeight), 0), count.value - 1))
|
|
||||||
}
|
|
||||||
|
|
||||||
const change = () => {
|
|
||||||
emit('change', props.column[state.selectIndex])
|
|
||||||
}
|
|
||||||
|
|
||||||
const mousewheel = (e) => {
|
|
||||||
e.preventDefault()
|
|
||||||
e.stopPropagation()
|
|
||||||
state.ulStyle.transitionDuration = `0ms`
|
|
||||||
state.ulStyle.transitionProperty = `none`
|
|
||||||
const { deltaX, deltaY } = e
|
|
||||||
if (Math.abs(deltaX) < Math.abs(deltaY)) {
|
|
||||||
state.startTop = state.startTop - deltaY
|
|
||||||
let b = state.bottom - props.itemHeight
|
|
||||||
let t = state.top + props.itemHeight
|
|
||||||
let shouldMove = true
|
|
||||||
if (state.startTop > b) {
|
|
||||||
state.startTop = b
|
|
||||||
shouldMove = false
|
|
||||||
} else if (state.startTop < t) {
|
|
||||||
state.startTop = t
|
|
||||||
shouldMove = false
|
|
||||||
}
|
|
||||||
state.ulStyle.transform = `translate3d(0px, ${state.startTop}px, 0px)`
|
|
||||||
if (shouldMove) {
|
|
||||||
clearInterval(wheelTimer)
|
|
||||||
wheelTimer = setTimeout(setTranfromTop, 100)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
init();
|
|
||||||
list.value.addEventListener(START_EVENT, handleStart, false)
|
|
||||||
if (IS_PC) {
|
|
||||||
list.value.addEventListener('wheel', mousewheel, false)
|
|
||||||
} else {
|
|
||||||
list.value.addEventListener(MOVE_EVENT, handleMove, false)
|
|
||||||
list.value.addEventListener(END_EVENT, handleEnd, false)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
list.value.removeEventListener(START_EVENT, handleStart, false)
|
|
||||||
if (IS_PC) {
|
|
||||||
list.value.removeEventListener('wheel', mousewheel, false)
|
|
||||||
list.value.removeEventListener(MOVE_EVENT, handleMove, false)
|
|
||||||
list.value.removeEventListener(END_EVENT, handleEnd, false)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(() => props.column, () => {
|
|
||||||
init()
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(() => props.defaultIndex, () => {
|
|
||||||
setTop(props.defaultIndex)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.list {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
flex: 1;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
ul {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
transition-timing-function: cubic-bezier(0.23, 1, 0.68, 1);
|
|
||||||
line-height: 44px;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,29 +0,0 @@
|
|||||||
export const DEFTAULT_ITEM_HEIGHT = 44
|
|
||||||
|
|
||||||
// 兼容pc 移动端
|
|
||||||
export const HAS_TOUCH = 'ontouchstart' in window
|
|
||||||
export const START_EVENT = HAS_TOUCH ? 'touchstart' : 'mousedown'
|
|
||||||
export const MOVE_EVENT = HAS_TOUCH ? 'touchmove' : 'mousemove'
|
|
||||||
export const END_EVENT = HAS_TOUCH ? 'touchend' : 'mouseup'
|
|
||||||
|
|
||||||
export const getClient = (e:any) => {
|
|
||||||
const clientX = HAS_TOUCH ? e.changedTouches[0].clientX : e.clientX
|
|
||||||
const clientY = HAS_TOUCH ? e.changedTouches[0].clientY : e.clientY
|
|
||||||
return {
|
|
||||||
x: clientX,
|
|
||||||
y: clientY
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const isPC = () => {
|
|
||||||
const userAgentInfo = navigator.userAgent
|
|
||||||
const Agents = ['Android', 'iPhone', 'SymbianOS', 'Windows Phone', 'iPad', 'iPod']
|
|
||||||
let flag = true
|
|
||||||
for (let v = 0; v < Agents.length; v++) {
|
|
||||||
if (userAgentInfo.indexOf(Agents[v]) > 0) {
|
|
||||||
flag = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return flag
|
|
||||||
}
|
|
@ -23,8 +23,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<Picker :data="listPop"
|
<Picker :list="listPop"
|
||||||
:showToolbar="true" v-model:visible="pickPop" @confirm="onConfirm" @cancel="onCancel" />
|
:showToolbar="true" v-model="pickPop" @confirm="onConfirm" @cancel="onCancel" />
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="multilevel-wrapper-pc">
|
<div v-else class="multilevel-wrapper-pc">
|
||||||
<div v-for="(v, i) in valList" :key="v+i" class="multilevel-select-item">
|
<div v-for="(v, i) in valList" :key="v+i" class="multilevel-select-item">
|
||||||
@ -105,8 +105,8 @@ const handleChange = async(val,i) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const onConfirm = (val) => {
|
const onConfirm = (val) => {
|
||||||
valList.value[pickIndex.value] = val[0];
|
valList.value[pickIndex.value] = val;
|
||||||
handleChange(val[0], pickIndex.value);
|
handleChange(val, pickIndex.value);
|
||||||
pickIndex.value=-1
|
pickIndex.value=-1
|
||||||
}
|
}
|
||||||
const onCancel = () => {
|
const onCancel = () => {
|
||||||
@ -115,12 +115,13 @@ const onCancel = () => {
|
|||||||
|
|
||||||
const showPickPop = (list, index) => {
|
const showPickPop = (list, index) => {
|
||||||
pickPop.value = true;
|
pickPop.value = true;
|
||||||
listPop.value = [list];
|
listPop.value = list;
|
||||||
pickIndex.value = index
|
pickIndex.value = index
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateEquipment = () => {
|
const updateEquipment = () => {
|
||||||
isMobile.value = isInMobile()
|
isMobile.value = isInMobile()
|
||||||
|
pickPop.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
@ -3,6 +3,7 @@ import App from './App.vue'
|
|||||||
import EventBus from './utils/eventbus'
|
import EventBus from './utils/eventbus'
|
||||||
import router from './router'
|
import router from './router'
|
||||||
import { createPinia } from 'pinia'
|
import { createPinia } from 'pinia'
|
||||||
|
import 'default-passive-events'
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
const pinia = createPinia()
|
const pinia = createPinia()
|
||||||
|
Loading…
Reference in New Issue
Block a user