水平较低,主要是给自己看的。如果要用vue transition,必须每次弹出都初始化better-scroll实例。
<!--
li下必须加子元素包裹,否则选中样式会有延迟。
目前是在任何一列在滚动中都会禁用取消和确认,实际上可以使用stop()方法,不过需要下次弹出前想办法初始化
$data.show控制弹出和收起
$props.type: 场景类型
1.YMD——年月日,默认initYMD是当前年月日,否则传入new Date(yyyy,mm,dd),注意mm为0-11
2.SHX——省市区、三级联动,目前没做初始位置逻辑。由于地区名字可能会很长,由filter做了一些不恰当处理
-->
<template>
<div class="selector">
<div v-show="show" class="shade"></div>
<div class="box" :class="{'up':show,'down':!show}">
<div class="title">
<button type="button" @click="cancel" :disabled="disableBtn">{{lb}}</button>
<div>{{title}}</div>
<button type="button" @click="deal" :disabled="disableBtn">{{rb}}</button>
</div>
<div class="section">
<div class="line"></div>
<div class="line"></div>
<!--数据没有关联性的年月日-->
<div class="lists three" v-if="type==='YMD'">
<div class="wrapper" ref="first">
<ul ref="firstUl">
<li v-for="(v,i) in firstList" :key="i">
<p :class="getClass(firstIndex, i)">{{v}}</p>
</li>
</ul>
</div>
<div class="wrapper" ref="second">
<ul ref="secondUl">
<li v-for="(v,i) in secondList" :key="i" :ref="i===0?'cell':null">
<p :class="getClass(secondIndex, i)">{{v}}</p>
</li>
</ul>
</div>
<div class="wrapper" ref="third">
<ul ref="thirdUl" class="wheel-scroll">
<li v-for="(v,i) in thirdList" :key="i" class="wheel-item">
<p :class="getClass(thirdIndex, i)">{{v}}</p>
</li>
</ul>
</div>
</div>
<!--省市县三级联动-->
<div class="lists threeLink" v-if="type==='SHX'">
<div class="wrapper" ref="first">
<ul ref="firstUl" class="wheel-scroll">
<li v-for="(v,i) in firstList" :key="i" class="wheel-item">
<p :class="getClass(firstIndex, i)">{{v.name | reduceLetter}}</p>
</li>
</ul>
</div>
<div class="wrapper" ref="second">
<ul ref="secondUl">
<li v-for="(v,i) in secondList" :key="i" :ref="i===0?'cell':null">
<p :class="getClass(secondIndex, i)">{{v.name | reduceLetter}}</p>
</li>
</ul>
</div>
<div class="wrapper" ref="third">
<ul ref="thirdUl">
<li v-for="(v,i) in thirdList" :key="i">
<p :class="getClass(thirdIndex, i)">{{v.name | reduceLetter}}</p>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import BScroll from 'better-scroll';
import SHX from 'ass/js/ChinaAreaList';
export default {
name: 'Selector',
data() {
return {
show: false,
firstScroll: Function, // 第一列better-scroll对象
secondScroll: Function,
thirdScroll: Function,
firstIndex: 0, // 第一列选中索引
secondIndex: 0,
thirdIndex: 0,
firstList: null, // 第一列数据
secondList: null,
thirdList: null,
lastDate: Date,
ymdList: Array,
firstScrolling: false,
secondScrolling: false,
thirdScrolling: false
}
},
props: {
type: {
type: String,
default: 'YMD'
},
config: {
type: Object
},
lb: {
type: String,
default: '取消'
},
rb: {
type: String,
default: '确认'
},
title: {
type: String,
default: '日期选择'
},
initYMD: { // 初始时间年月日
type: Date,
default() {
return new Date();
}
},
initSHX: {
type: Array,
default() {
return ['110000', '110000'];
}
},
output: null // 输出对象
},
computed: {
disableBtn() {
if (!this.thirdScrolling && !this.secondScrolling && !this.firstScrolling) {
return false;
} else {
return true;
}
}
},
filters: {
reduceLetter(v) {
const reg = /(省|市|县|自治区|特别行政区|壮族|回族|藏族|土族|维吾尔|自治县|自治州|蒙古族|自治)/g;
return v.replace(reg, '');
}
},
methods: {
getYears(len, init) {
return Array.from({length: len}).map((v, i) => i + init);
},
getMonth() {
return Array.from({length: 12}).map((v, i) => i + 1);
},
createYMD(start, lang) { // start开始年份,lang展示
const d31 = /^(0|2|4|6|7|9|11)$/;
const d30 = /^(3|5|8|10)$/;
return Array.from({length: lang}).map((yv, yi) => {
yv = {};
yv['year'] = yi + start;
yv['first'] = Array.from({length: 12}).map((mv, mi) => {
let len;
mv = {};
if (yv.year % 4 === 0 && mi === 1) {
len = 29;
} else if (yv.year % 4 !== 0 && mi === 1) {
len = 28;
} else if (d30.test(mi)) {
len = 30;
} else if (d31.test(mi)) {
len = 31;
}
mv['month'] = mi + 1;
mv['second'] = Array.from({length: len}).map((dv, di) => {
dv = di + 1;
return dv;
});
return mv;
});
return yv;
});
},
getDate(y, m) {
let len = 28;
const d31 = /^(0|2|4|6|7|9|11)$/;
const d30 = /^(3|5|8|10)$/;
let year = y || this.initYMD.getFullYear();
let mon = m || this.initYMD.getMonth();
if (d31.test(mon)) {
len = 31;
} else if (d30.test(mon)) {
len = 30;
} else if (mon === 1 && year % 4 === 0) {
len = 29;
}
return Array.from({length: len}).map((v, i) => i + 1);
},
getClass(index, key) {
if ((key === index - 1) || (key === index + 1)) {
return 'second';
} else if (key !== index) {
return 'third';
}
},
initListYMD() {
let time;
switch (this.type) {
case 'MD':
time = this.initYMD;
break;
default:
time = this.initYMD;
this.firstList = this.getYears(300, 1900);
this.secondList = this.getMonth();
this.thirdList = this.getDate();
this.firstIndex = time.getFullYear() - 1900;
this.secondIndex = time.getMonth();
this.thirdIndex = time.getDate() -1;
break;
}
},
initScroll(scroll) {
this.$nextTick(() => {
if (!this.$refs[scroll]) return;
this[scroll + 'Scroll'] = new BScroll(this.$refs[scroll], {
probeType: 3,
wheel: {
selectedIndex: this[scroll + 'Index']||0,
wheelWrapperClass: 'wheel-scroll',
wheelItemClass: 'wheel-item',
rotate: 0,
adjustTime: 0
}
});
const ch = this.$refs.cell[0].clientHeight;
this[scroll + 'Scroll'].on('scroll', ({y}) => {
this[scroll + 'Index'] = Math.round(Math.abs(y) / ch);
this[scroll + 'Scrolling'] = true;
});
this[scroll + 'Scroll'].on('scrollEnd', () => {
this[scroll + 'Scrolling'] = false;
this[scroll + 'Index'] = this[scroll + 'Scroll'].getSelectedIndex();
if (this.type === 'YMD' && (scroll === 'first' || scroll === 'second')) {
let len1 = this.thirdList.length;
this.thirdList = this.getDate(this.firstIndex + 1900, this.secondIndex);
let len2 = this.thirdList.length;
if (len1 === len2) return;
this.thirdScroll.refresh();
this.thirdIndex = 0;
this.thirdScroll.scrollTo(0, 0, 300); // 注意:最顶端是0,往下的坐标是负的,时间单位ms
}
if (this.type === 'SHX') {
if (scroll === 'first') {
this.secondIndex = 0;
this.secondList = this.firstList[this.firstIndex].cityList;
this.secondScroll.scrollTo(0, 0, 300);
this.thirdIndex = 0;
this.thirdList = this.secondList[this.secondIndex].areaList;
this.thirdScroll.scrollTo(0, 0, 300);
}
if (scroll === 'second') {
this.thirdIndex = 0;
this.thirdList = this.secondList[this.secondIndex].areaList;
this.thirdScroll.scrollTo(0, 0, 300);
}
}
});
});
},
cancel() {
this.show = false;
this.$emit('cancel',this.lastDate);
},
deal() {
this.show = false;
if (this.type === 'YMD') {
let date = `${this.firstList[this.firstIndex]}-${this.secondList[this.secondIndex]}-${this.thirdList[this.thirdIndex]}`;
this.$emit('update:output',date);
} else if (this.type === 'SHX') {
let arr = [
{
code: this.firstList[this.firstIndex].code,
name: this.firstList[this.firstIndex].name
},
{
code: this.secondList[this.secondIndex].code,
name: this.secondList[this.secondIndex].name
},
this.thirdList[this.thirdIndex]
];
this.$emit('update:output',arr);
}
}
},
created() {
console.error('', this);
switch (this.type) {
case 'SHX':
this.firstList = SHX;
this.secondList = this.firstList[0].cityList;
this.thirdList = this.secondList[0].areaList;
break;
default:
this.initListYMD();
this.lastDate = this.initYMD;
}
this.initScroll('first');
this.initScroll('second');
this.initScroll('third');
this.ymdList = this.createYMD(1900, 300);
}
}
</script>
<style scoped lang="less">
@import "~ass/style/base.less";
.selector{
.shade {
height: 100%;
width: 100%;
background: #333;
opacity: 0.5;
position: fixed;
top: 0;
left: 0;
z-index:9;
}
.box{
height: 491*@rpx;
width: 750*@rpx;
position: absolute;
bottom: -500*@rpx;
z-index: 10;
font-size: 32*@rpx;
background: @cff;
display: flex;
flex-direction: column;
.title{
height: 88*@rpx;
width: 100%;
display: flex;
justify-content: space-between;
border-bottom: 1px solid @ce5;
div{
line-height: 88*@rpx;
}
button{
padding: 0 42*@rpx;
color:@c80;
}
button:last-of-type{
color:@c10;
}
}
.section{
width: 100%;
flex:1;
padding: 0 40*@rpx;
position: relative;
.wrapper{
padding: 160*@rpx 0;
height: 402*@rpx;
overflow: hidden;
ul{
li{
p{
height: 100%;
width: 100%;
line-height: 80*@rpx;
text-align: center;
font-size: 40*@rpx;
white-space:nowrap;
overflow: hidden;
pointer-events: none;
}
.second{
color: @c99;
font-size: 36*@rpx;
}
.third{
color: @ccc;
font-size: 32*@rpx;
}
}
}
}
.lists{
width: 100%;
display: flex;
justify-content: space-between;
}
.three{
padding: 0 40*@rpx 0 80*@rpx;
li{
width: 126*@rpx;
}
}
.threeLink{
li{
width: 220*@rpx;
overflow: hidden;
}
}
.line{
width: 670*@rpx;
height: 0;
border-bottom: 1px solid @ccc;
position: absolute;
top: 160*@rpx;
}
.line:nth-of-type(2){
top: 240*@rpx;
}
}
}
.up{
transform: translateY(-500*@rpx);
transition: transform .3s ease-in-out;
}
.down{
transform: translateY(500*@rpx);
transition: transform .3s ease-in-out;
}
}
</style>
复制代码
<!--HTML font-size,rem-->
var htmlFontSize = document.documentElement.clientWidth / 375 * 100 + 'px';
var bodyFontSize = '16px';
var styleDom = document.createElement('style');
styleDom.innerHTML = 'html{font-size:' + htmlFontSize + '!important;}body{font-size:' + bodyFontSize + '!important;}';
document.getElementsByTagName('head')[0].appendChild(styleDom)
复制代码
<!--SHX格式-->
[{
"code": "820000",
"name": "澳门特别行政区",
"cityList": [
{
"code": "820000",
"name": "澳门特别行政区",
"areaList": [
{
"code": "820000",
"name": "澳门特别行政区"
}
]
}
]
}]
复制代码