[impovement] datepicker 使用原生 picker-view 重写 (#294)

* feat: cell 自定义组件

* [new feature] DatetimePicker:新增时间选择组件

* fix: support picker view

* improvement:支持 picker-view

* fix: issue #286
This commit is contained in:
刘建东 2018-06-04 14:50:55 +08:00 committed by Yao
parent 82d1a8ae98
commit d3eee374dd
11 changed files with 400 additions and 475 deletions

View File

@ -1,7 +1,16 @@
Page({
onLoad () {
setTimeout(() => {
this.setData({
date: new Date('2300/12/12').getTime()
})
}, 100)
},
customChange ({detail}) {
this.setData({pickerView1: detail.value.join('-')})
},
nativeChange ({detail}) {
this.setData({pickerView2: detail.value.join('-')})
}

View File

@ -3,21 +3,13 @@
<zan-cell-group>
<zan-cell title="选择时间">
<zan-date-picker
native
slot="footer"
date="{{ date }}"
bindchange="change"
bindcancel="cancel"
placeholder="原生 picker"
placeholder-class="my-customer-class-name" />
</zan-cell>
<zan-cell title="选择时间">
<zan-date-picker
slot="footer"
bindchange="change"
bindcancel="cancel"
placeholder="自定义 picker"
placeholder-class="my-customer-class-name" />
placeholder="异步初始值"
placeholder-class="my-customer-class-name"
/>
</zan-cell>
<zan-cell title="选择时间">
@ -27,7 +19,7 @@
bindchange="change"
bindcancel="cancel"
placeholder="只显示部分列的 picker"
placeholder-class="my-customer-class-name" />
/>
</zan-cell>
<zan-cell title="选择时间">
@ -36,8 +28,8 @@
bindchange="change"
bindcancel="cancel"
placeholder="自定义显示格式的 picker"
format="选择的是YY-MM-DD HH:mm:ss"
placeholder-class="my-customer-class-name" />
format="选择的是YYYY-MM-DD HH:mm:ss"
/>
</zan-cell>
</zan-cell-group>
</zan-panel>
@ -49,20 +41,17 @@
picker-view
bindchange="customChange"
bindcancel="cancel"
placeholder="自定义显示格式的 picker"
format="选择的是YY-MM-DD HH:mm:ss"
placeholder-class="my-customer-class" />
/>
</zan-panel>
<zan-panel class="picker-panel-demo">
<zan-cell title="选择时间" value="{{ pickerView2 || '原生组件'}}"></zan-cell>
<zan-cell title="选择时间" value="{{ pickerView2 || '只显示部分列'}}"></zan-cell>
<zan-date-picker
native
picker-view
picker-view
bindchange="nativeChange"
bindcancel="cancel"
placeholder="组件内原生 picker"
placeholder-class="my-customer-class" />
not-use="{{['years', 'seconds']}}"
/>
</zan-panel>
</doc-page>

View File

@ -1,6 +1,6 @@
## DatetimePicker 时间选择
使用 picker 组件开发的时间日期组件,弥补小程序 picker 自身对于快速时间选择的不支持
使用 picker 组件开发的时间日期组件,弥补小程序 picker 自身对于快速时间选择的不支持
### 示例代码
@ -26,11 +26,10 @@
| 名称 | 类型 | 是否必须 | 默认 | 描述 |
| ----------------- | --------- | -------- | ------------------- | ----------------------------------------------------------------------------------------------------- |
| value | null | `否` | 当前时间 | 初始化时间,传入的值会被 Date 构造函数转换为一个 Date 对象,不合法的值将抛出一个错误 |
| date | `String` | `否` | 当前时间 | 初始化时间,传入的值会被 Date 构造函数转换为一个 Date 对象,不合法的值将抛出一个错误 |
| placeholder-class | `String` | `否` | 无 | 自定义类,可改变 placeholder 样式,其他类无效,`picker-view` 为 true 时不支持 |
| placeholder | `String` | `否` | 请选择时间 | 设置 picker 的 placeholder |
| placeholder | `String` | `否` | 请选择时间 | 设置 picker 的 placeholder,`picker-view` 为 true 时不支持 |
| not-use | `Array` | `否` | 无 | 不需要显示的列 可选择`years`, `months`, `days`, `hours`, `minutes`, `seconds`中的多个 |
| native | `Boolean` | `否` | 无 | 使用原生 picker还是自定义的 picker自定义 picker 滚动不如原生) |
| picker-view | `Boolean` | `否` | 无 | 如果为 true相当于 picker-view 组件 |
| format | `String` | `否` | YYYY-MM-DD HH:mm:ss | 设置选中的时间显示的格式,支持 _YYYYyyyyYYyyMMMDDddDdHH hhHhmmmsss_ |
| bindchange | `String` | `是` | 无 | 用户点击`确认`触发该事件,返回值为按“年,月,日,时,分,秒”顺序的数组,可以通过`detail.value`获取 |
@ -38,11 +37,10 @@
### 方法
| 名称 | 参数 | 描述 |
| ------------ | ---- | ---- |
| getFormatStr | | 返回 `format` 格式的字符串,在 `picker-view` 为 true 时比较实用 |
| 名称 | 参数 | 描述 |
| ------------ | ---- | --------------------------------------------------------------- |
| getFormatStr | 无 | 返回 `format` 格式的字符串,在 `picker-view` 为 true 时比较实用 |
### 增强优化
* 支持可选择时间区域限制
* 滚动优化

View File

@ -0,0 +1,35 @@
<import src="./picker-view-column.wxml" />
<template name="date-picker-view">
<picker-view
value="{{ selected }}"
bindchange="change"
indicator-style="height: 50px;"
class="picker-visible">
<template
is="picker-view-column"
data="{{ data: dataList[0], className: 'year-view-column', hidden: !use['years'], tip: '年' }}"
/>
<template
is="picker-view-column"
data="{{ data: dataList[1], hidden: !use['months'], tip: '月' }}"
/>
<template
is="picker-view-column"
data="{{ data: dataList[2], hidden: !use['days'], tip: '日' }}"
/>
<template
is="picker-view-column"
data="{{ data: dataList[3], hidden: !use['hours'], tip: '时' }}"
/>
<template
is="picker-view-column"
data="{{ data: dataList[4], hidden: !use['minutes'], tip: '分' }}"
/>
<template
is="picker-view-column"
data="{{ data: dataList[5], hidden: !use['seconds'], tip: '秒' }}"
/>
</picker-view>
</template>

View File

@ -0,0 +1,98 @@
const LIMIT_YEAR_COUNT = 50;
const { genNumber, iso2utc } = require('./utils')
module.exports = class DatePicker {
constructor(date = new Date()) {
this.types = ['year', 'month', 'day', 'hour', 'minute', 'second'];
this.months = genNumber(1, 12, 2);
this.hours = genNumber(0, 23, 2);
this.seconds = genNumber(0, 59, 2);
this.minutes = genNumber(0, 59, 2);
this._date = date
}
getYears(year) {
let mid = Math.floor(LIMIT_YEAR_COUNT / 2);
let min = year - mid;
let max = year + (LIMIT_YEAR_COUNT - mid);
return genNumber(min, max, 4);
}
lastDay(year, month) {
return month !== 12 ? new Date(
new Date(`${year}/${month + 1}/1`).getTime() - (24 * 60 * 60 * 1000)
).getDate() : 31;
}
getData (date) {
date = date || this._date || new Date
// toUTCString ISO 格式部分 ios 手机会失败
if (typeof date === 'string' && date.indexOf('-') > 0) {
date = iso2utc(date)
}
let d = new Date(date);
console.log(d);
let y = d.getFullYear();
let m = d.getMonth() + 1;
let years = this.getYears(y);
let lastDay = this.lastDay(y, m);
let days = genNumber(1, lastDay, 2);
this._years = years;
this._dataList = [years, this.months, days, this.hours, this.minutes, this.seconds];
this._indexs = [25, m - 1, d.getDate() - 1, d.getHours(), d.getMinutes(), d.getSeconds()];
return {
dataList: this._dataList,
selected: this._indexs
}
}
update(col, index) {
let type = this.types[col];
switch (type) {
case 'year':
return this._updateYear(col, index);
case 'month':
return this._updateMonth(col, index);
default:
this._indexs[col] = index;
return [{ col, index }]
}
}
_updateYear(col, index, cb) {
let years = this._dataList[col];
let year = years[index];
this._dataList[col] = this.getYears(+year);
this._indexs[col] = Math.floor(LIMIT_YEAR_COUNT / 2);
return [{ col: 0, index: this._indexs[col], data: this._dataList[col] }]
}
_updateMonth(col, index) {
let month = this._dataList[col][index];
let year = this._dataList[0][this._indexs[0]];
let lastDay = this.lastDay(+year, +month);
this._indexs[col] = index;
this._dataList[2] = genNumber(1, lastDay, 2);
this._indexs[2] = this._indexs[2] >= this._dataList[2].length ? this._dataList[2].length - 1 : this._indexs[2];
return [
{
col: 1,
index
},
{
col: 2,
index: this._indexs[2],
data: this._dataList[2]
}
]
}
}

View File

@ -1,145 +1,7 @@
function partStartWithZero(num, strlen) {
let zeros = '';
while (zeros.length < strlen) {
zeros += '0';
}
return (zeros + num).slice(-strlen);
}
const DatePicker = require('./date-picker')
const { genNumber, moment } = require('./utils')
function genNumber(begin, end, strlen) {
let nums = [];
while (begin <= end) {
nums.push(partStartWithZero(begin, strlen));
begin++;
}
return nums;
}
function moment(date, formatStr = 'YYYY:MM:DD') {
if (!date && date !== 0) date = new Date();
date = new Date(date);
if (date.toString() === 'Invalid Date') throw new Error('Invalid Date');
let getDateValue = (method, fn) => (fn ? fn(date[`get${method}`]()) : date[`get${method}`]());
let map = new Map();
map.set(/(Y+)/i, () => getDateValue('FullYear', year => (year + '').substr(4 - RegExp.$1.length)));
map.set(/(M+)/, () => getDateValue('Month', month => partStartWithZero(month + 1, RegExp.$1.length)));
map.set(/(D+)/i, () => getDateValue('Date', date => partStartWithZero(date, RegExp.$1.length)));
map.set(/(H+)/i, () => getDateValue('Hours', hour => partStartWithZero(hour, RegExp.$1.length)));
map.set(/(m+)/, () => getDateValue('Minutes', minute => partStartWithZero(minute, RegExp.$1.length)));
map.set(/(s+)/, () => getDateValue('Seconds', second => partStartWithZero(second, RegExp.$1.length)));
for (const [reg, fn] of map) {
if (reg.test(formatStr)) {
formatStr = formatStr.replace(RegExp.$1, fn.call(null));
}
}
return formatStr;
}
const LIMIT_YEAR_COUNT = 50;
class DatePicker {
constructor(format, date = new Date(), cb) {
this.types = ['year', 'month', 'day', 'hour', 'minute', 'second'];
this.months = genNumber(1, 12, 2);
this.hours = genNumber(0, 23, 2);
this.seconds = genNumber(0, 59, 2);
this.minutes = genNumber(0, 59, 2);
// this.format(format);
this.init(date, cb);
}
getYears(year) {
let mid = Math.floor(LIMIT_YEAR_COUNT / 2);
let min = year - mid;
let max = year + (LIMIT_YEAR_COUNT - mid);
return genNumber(min, max, 4);
}
lastDay(year, month) {
return month !== 12 ? new Date(
new Date(`${year}/${month + 1}/1`).getTime() - (24 * 60 * 60 * 1000)
).getDate() : 31;
}
init(date, cb) {
let d = new Date(date);
let y = d.getFullYear();
let m = d.getMonth() + 1;
let years = this.getYears(y);
let lastDay = this.lastDay(y, m);
let days = genNumber(1, lastDay, 2);
this._years = years;
this._dataList = [years, this.months, days, this.hours, this.minutes, this.seconds];
this._indexs = [25, m - 1, d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds()];
cb && cb({
dataList: this._dataList,
indexs: this._indexs
});
}
update(col, index, cb) {
let type = this.types[col];
switch (type) {
case 'year':
this._updateYear(col, index, cb);
break;
case 'month':
this._updateMonth(col, index, cb);
break;
default:
this._indexs[col] = index;
cb && cb({
dataList: this._dataList,
indexs: this._indexs,
updateColumn: col,
updateIndex: index
});
}
}
_updateYear(col, index, cb) {
let years = this._dataList[col];
let year = years[index];
this._dataList[col] = this.getYears(+year);
this._indexs[col] = Math.floor(LIMIT_YEAR_COUNT / 2);
cb && cb({
dataList: this._dataList,
indexs: this._indexs,
updateColumn: col
});
}
_updateMonth(col, index, cb) {
let month = this._dataList[col][index];
let year = this._dataList[0][this._indexs[0]];
let lastDay = this.lastDay(+year, +month);
this._indexs[col] = index;
this._dataList[2] = genNumber(1, lastDay, 2);
this._indexs[2] = this._indexs[2] >= this._dataList[2].length ? this._dataList[2].length - 1 : this._indexs[2];
cb && cb({
dataList: this._dataList,
indexs: this._indexs,
updateColumn: 2,
updateIndex: this._indexs[2]
});
cb && cb({
dataList: this._dataList,
indexs: this._indexs,
updateColumn: 1,
updateIndex: index
});
}
}
// 组件内使用 this.indexs 好像有问题
let _indexs = [];
Component({
properties: {
placeholder: {
@ -150,15 +12,20 @@ Component({
type: String,
value: 'YYYY-MM-DD HH:mm:ss'
},
native: {
type: Boolean
},
pickerView: {
type: Boolean
},
date: {
type: String,
value: new Date()
observer(value) {
if (value === ({}).toString()) {
throw new Error('参数必须是一个字符串')
}
if (/^[0-9]+$/.test(value)) {
value = +value
}
this.updateDate(value)
}
},
notUse: {
type: Array
@ -169,142 +36,133 @@ Component({
transPos: [0, 0, 0, 0, 0, 0]
},
attached() {
this.use = {}
;['years', 'months', 'days', 'hours', 'minutes', 'seconds'].forEach((item) => {
if ((this.data.notUse || []).indexOf(item) === -1) { this.use[item] = true }
this.use = {};
['years', 'months', 'days', 'hours', 'minutes', 'seconds'].forEach((item) => {
if ((this.data.notUse || []).indexOf(item) === -1) {
this.use[item] = true
}
});
this.setData({ use: this.use });
this.data.pickerView && !this.data.native && this.showPicker();
},
ready() {
// 微信 bug如果不先定义会导致不能选中
this.picker = new DatePicker(this.data.date)
let { dataList, selected } = this.picker.getData(this.data.date)
// 鬼他么知道为什么 dataList, selected 不能一起 setData
this.setData({
"dataList":[
[
"2018","2019","2020","2021","2022","2023","2024","2025","2026","2027","2028","2029","2030","2031","2032","2033","2034","2035","2036","2037","2038","2039","2040","2041","2042","2043"
],
genNumber(1, 12, 2),
genNumber(0, 31, 2),
genNumber(0, 23, 2),
genNumber(0, 59, 2),
genNumber(0, 59, 2)
]
})
this.picker = new DatePicker(this.data.format, this.data.date, this.updatePicker.bind(this));
use: this.use,
dataList
}, () => {
this.setData({
selected
})
});
this._indexs = selected
},
methods: {
updatePicker({ dataList, indexs, updateColumn, updateIndex }) {
let updateData = {};
_indexs = indexs;
// 指定更新某列数据,表示某列数据更新
if (updateColumn) {
updateData[`transPos[${updateColumn}]`] = -36 * _indexs[updateColumn];
updateData[`dataList[${updateColumn}]`] = dataList[updateColumn];
}
// 指定更新某列索引,表示某列数据选中的索引已更新
if (typeof updateIndex !== 'undefined') {
updateData[`transPos[${updateColumn}]`] = -36 * _indexs[updateColumn];
updateData[`selected[${updateColumn}]`] = indexs[updateColumn];
updatePicker(updateData = []) {
let _updateData = {};
for (const { col, index, data } of updateData) {
if (~index && this._indexs[col] !== index || col === 0) {
_updateData[`selected[${col}]`] = index // 更新索引
this._indexs[col] = index
}
if (data) {
_updateData[`dataList[${col}]`] = data;
}
}
// 只在初始化时设置全部的值,其他的都局部更新
if (!updateColumn && typeof updateIndex === 'undefined') {
updateData = { dataList, selected: indexs };
_indexs.forEach((item, index) => {
updateData[`transPos[${index}]`] = -item * 36;
});
}
this.setData(_updateData);
},
this.setData(updateData);
updateDate (date) {
let { dataList, selected } = this.picker.getData(date)
this._indexs = selected
// 好像必须要等到 datalist 完成
this.setData({ dataList }, () => {
this.setData({
selected,
text: this.getFormatStr()
})
})
},
touchmove(e) {
let { changedTouches, target } = e;
let col = target.dataset.col;
let { clientY } = changedTouches[0];
if (!col) return;
let updateData = {};
let itemLength = this.data.dataList[col].length;
updateData[`transPos[${col}]`] = this.startTransPos + (clientY - this.startY);
if (updateData[`transPos[${col}]`] >= 0) {
updateData[`transPos[${col}]`] = 0;
} else if (-(itemLength - 1) * 36 >= updateData[`transPos[${col}]`]) {
updateData[`transPos[${col}]`] = -(itemLength - 1) * 36;
}
this.setData(updateData);
},
touchStart(e) {
let { target, changedTouches } = e;
let col = target.dataset.col;
let touchData = changedTouches[0];
if (!col) return;
this.startY = touchData.clientY;
this.startTime = e.timeStamp;
this.startTransPos = this.data.transPos[col];
},
touchEnd(e) {
let { col } = e.target.dataset;
if (!col) return;
let pos = this.data.transPos[col];
let itemIndex = Math.round(pos / 36);
this.columnchange({ detail: { column: +col, value: -itemIndex } });
},
columnchange(e) {
let { column, value } = e.detail;
_indexs[column] = value;
this.picker.update(column, value, this.updatePicker.bind(this));
this.data.pickerView && !this.data.native && this.change({detail: {value: _indexs}})
},
getFormatStr() {
let date = new Date()
;['FullYear', 'Month', 'Date', 'Hours', 'Minutes', 'Seconds'].forEach((key, index) => {
let value = this.data.dataList[index][_indexs[index]];
let date = new Date();
['FullYear', 'Month', 'Date', 'Hours', 'Minutes', 'Seconds'].forEach((key, index) => {
let value = this.data.dataList[index][this._indexs[index]];
if (key === 'Month') {
value = +this.data.dataList[index][_indexs[index]] - 1;
value = +this.data.dataList[index][this._indexs[index]] - 1;
}
date[`set${key}`](+value);
});
return moment(date, this.data.format);
},
showPicker() {
this.setData({ show: true });
this.setData({
show: true
});
},
hidePicker(e) {
let { action } = e.currentTarget.dataset;
this.setData({ show: false });
this.setData({
show: false
});
if (action === 'cancel') {
this.cancel({ detail: {} });
this.cancel({
detail: {}
});
} else {
this.change({ detail: { value: _indexs } });
this.change({
detail: {
value: this._indexs
}
});
}
},
columnchange(e) {
let { column, value } = e.detail;
let updateData = this.picker.update(column, value);
this.updatePicker(updateData)
},
change(e) {
let { value } = e.detail;
let data = this.data.dataList.map((item, index) => {
return +item[value[index]];
});
this.triggerEvent('change', { value: data });
this.triggerEvent('change', {
value: data
});
// 为了支持原生 picker view每次 change 都需要手动 columnchange
if (this.data.pickerView && this.data.native) {
for (let index = 0; index < value.length; index++) {
if (_indexs[index] !== value[index]) {
this.columnchange({
detail: {
column: index, value: value[index]
}
})
break // 这里每次只处理一列,否则会出现日期值为 undefined 的情况
}
// 手动触发 columnchange
for (let index = 0; index < value.length; index++) {
if (this._indexs[index] !== value[index]) {
this.columnchange({
detail: {
column: index,
value: value[index]
}
})
}
}
this.setData({ text: this.getFormatStr() });
this.setData({
text: this.getFormatStr()
});
},
cancel(e) {
this.triggerEvent('cancel', e.detail);

View File

@ -1,3 +1,6 @@
{
"component": true
"component": true,
"usingComponents": {
"pop-manager": "../common/pop-manager/index"
}
}

View File

@ -1,111 +1,18 @@
<block wx:if="{{ pickerView }}">
<picker-view
wx:if="{{ native }}"
value="{{ selected }}"
bindchange="change"
indicator-style="height: 50px;"
class="picker-visible">
<picker-view-column>
<view wx:for="{{dataList[0]}}" style="line-height: 50px" wx:key="*this">{{item}}</view>
</picker-view-column>
<import src="./date-picker-view.wxml" />
<picker-view-column>
<view wx:for="{{dataList[1]}}" style="line-height: 50px" wx:key="*this">{{item}}</view>
</picker-view-column>
<view wx:if="{{ !pickerView }}" bindtap="showPicker" class="placeholder-class">{{text || placeholder}}</view>
<picker-view-column>
<view wx:for="{{dataList[2]}}" style="line-height: 50px" wx:key="*this">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view wx:for="{{dataList[3]}}" style="line-height: 50px" wx:key="*this">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view wx:for="{{dataList[4]}}" style="line-height: 50px" wx:key="*this">{{item}}</view>
</picker-view-column>
<picker-view-column>
<view wx:for="{{dataList[5]}}" style="line-height: 50px" wx:key="*this">{{item}}</view>
</picker-view-column>
</picker-view>
</block>
<block wx:else>
<picker
wx:if="{{native}}"
mode="multiSelector"
range="{{ dataList }}"
value="{{ selected }}"
bindchange="change"
bindcolumnchange="columnchange"
bindcancel="cancel" >
<view class="placeholder-class">
{{text || placeholder}}
<view wx:if="{{ pickerView }}" class="picker-view">
<template is="date-picker-view" data="{{ dataList, selected, use }}" />
</view>
<view wx:else>
<pop-manager show="{{ show }}" type="bottom" >
<view class="picker">
<view class="picker-action">
<view data-action="cancel" bindtap="hidePicker">取消</view>
<view data-action="change" bindtap="hidePicker">确认</view>
</view>
<template is="date-picker-view" data="{{ dataList, selected, use }}" />
</view>
</picker>
</block>
<view data-action="cancel" bindtap="hidePicker" class="picker-mask {{show && !pickerView ? 'show' : ''}}"></view>
<view wx:if="{{ !native }}" class="data-time-picker {{ pickerView && 'picker-view' }}">
<view bindtap="showPicker" wx:if="{{ !pickerView }}" class="placeholder-class">{{text || placeholder}}</view>
<view class="picker {{show ? 'picker-visible' : ''}}">
<view wx:if="{{!pickerView}}" class="picker-action">
<view data-action="cancel" bindtap="hidePicker">取消</view>
<view data-action="change" bindtap="hidePicker">确认</view>
</view>
<view
catchtouchstart="touchStart"
catchtouchend="touchEnd"
catchtouchmove="touchmove" class="picker-cols">
<view wx:if="{{use['years']}}" data-col="0" class="col">
<view data-col="0" style="transform: translateY({{ transPos[0] }}px)">
<view data-col="0" class="{{ index === selected[0] ? 'select-item' : '' }} cell"
wx:for-index="index"
wx:for="{{dataList[0]}}" wx:key="*this">{{item}}</view>
</view>
</view>
<view wx:if="{{use['years']}}" data-col="0" class="fixed-col">年</view>
<view wx:if="{{use['months']}}" data-col="1" class="col">
<view data-col="1" style="transform: translateY({{ transPos[1] }}px)">
<view data-col="1" class="{{ index === selected[1] ? 'select-item' : '' }} cell"
wx:for-index="index"
wx:for="{{dataList[1]}}" wx:key="*this">{{item}}</view>
</view>
</view>
<view wx:if="{{use['months']}}" data-col="1" class="fixed-col">月</view>
<view wx:if="{{use['days']}}" data-col="2" class="col">
<view data-col="2" style="transform: translateY({{ transPos[2] }}px)">
<view data-col="2" class="{{ index === selected[2] ? 'select-item' : '' }} cell"
wx:for-index="index" wx:for="{{dataList[2]}}" wx:key="*this">{{item}}</view>
</view>
</view>
<view wx:if="{{use['days']}}" data-col="2" class="fixed-col">日</view>
<view wx:if="{{use['hours']}}" data-col="3" class="col">
<view data-col="3" style="transform: translateY({{ transPos[3] }}px)">
<view data-col="3" class="{{ index === selected[3] ? 'select-item' : '' }} cell"
wx:for-index="index" wx:for="{{dataList[3]}}" wx:key="*this">{{item}}</view>
</view>
</view>
<view wx:if="{{use['hours']}}" data-col="3" class="fixed-col">时</view>
<view wx:if="{{use['minutes']}}" data-col="4" class="col">
<view data-col="4" style="transform: translateY({{ transPos[4] }}px)">
<view data-col="4" class="{{ index === selected[4] ? 'select-item' : '' }} cell"
wx:for-index="index" wx:for="{{dataList[4]}}" wx:key="*this">{{item}}</view>
</view>
</view>
<view wx:if="{{use['minutes']}}" data-col="4" class="fixed-col">分</view>
<view wx:if="{{use['seconds']}}" data-col="5" class="col">
<view data-col="5" style="transform: translateY({{ transPos[5] }}px)">
<view data-col="5" class="{{ index === selected[5] ? 'select-item' : '' }} cell"
wx:for-index="index" wx:for="{{dataList[5]}}" wx:key="*this">{{item}}</view>
</view>
</view>
<view wx:if="{{use['seconds']}}" data-col="5" class="fixed-col">秒</view>
</view>
</view>
</pop-manager>
</view>

View File

@ -1,110 +1,53 @@
.picker-view-column {
font-size: 14px;
}
.placeholder-class {
width: 100%;
}
.picker {
position: fixed;
bottom: -100px;
width: 100vw;
left: 0;
background: #fff;
z-index: 999;
display: flex;
flex-direction: column;
height: 0;
transition: height ease-in 0.3s;
}
.picker-view .picker {
position: relative;
bottom: auto;
left: auto;
width: 100%;
z-index: auto;
}
.picker-visible {
height: 236px;
bottom: 0;
}
.picker-view .picker-visible {
height: 200px;
picker-view-column.year-view-column {
width: 50px;
flex: 2;
}
.picker-mask {
position: fixed;
picker-view-column {
width: 30px;
text-align: center;
}
.hidden {
display: none;
}
.view-column-tip {
height: 235px;
line-height: 235px;
margin: 0 5px;
}
.picker {
width: 100vw;
height: 100vh;
top: 0;
left: 0;
right: 0;
bottom: 100vh;
background: rgba(0, 0, 0, 0.5);
z-index: -1;
}
.picker-mask.show {
bottom: 0;
z-index: 998;
z-index: 12;
}
.picker .picker-action {
height: 36px;
border-bottom: 1rpx solid #e5e5e5;
bottom: 235px;
padding: 0 15px;
width: 100%;
display: flex;
align-items: center;
position: absolute;
background: #fff;
box-sizing: border-box;
border-bottom: 1rpx solid #e5e5e5;
justify-content: space-between;
padding: 0 15px;
}
.picker-action view:last-child {
color: #1aad16;
}
.picker .picker-cols {
display: flex;
flex: 1;
margin: 10px 5px;
box-sizing: border-box;
position: relative;
overflow: hidden;
height: 180px;
font-size: 16px;
}
.picker-cols .fixed-col {
color: #999;
margin-left: 5px;
height:100%;
line-height:180px;
}
.picker-cols .col {
transform-origin: center;
font-size: 14px;
}
.picker-cols .col .select-item {
color: #333;
font-size: 16px;
}
.picker-cols .col {
flex: 1;
text-align: right;
color: #aaa;
height: 180px;
position: relative;
padding-top: 79px;
}
.picker-cols .col > view {
transition: transform 0.0003s;
}
.picker-cols .col .cell {
height: 36px;
}
.picker-cols::after, .picker-cols::before {
content: '';
.picker picker-view {
position: absolute;
height: 72px;
width: calc(100% - 10px);
pointer-events: none;
}
.picker-cols::before {
top: 0;
border-bottom: 1rpx solid #e5e5e5;
}
.picker-cols::after {
bottom: 0;
border-top: 1rpx solid #e5e5e5;
}
background: #fff;
width: 100vw;
}

View File

@ -0,0 +1,6 @@
<template name="picker-view-column">
<picker-view-column class="{{ className }} {{ hidden && 'hidden'}}">
<view wx:for="{{ data }}" style="line-height: 50px" wx:key="*this">{{item}}</view>
</picker-view-column>
<view class="view-column-tip {{ hidden && 'hidden'}}">{{ tip }}</view>
</template>

View File

@ -0,0 +1,79 @@
function partStartWithZero(num, strlen) {
let zeros = '';
while (zeros.length < strlen) {
zeros += '0';
}
return (zeros + num).slice(-strlen);
}
module.exports.genNumber = function genNumber(begin, end, strlen) {
let nums = [];
while (begin <= end) {
nums.push(partStartWithZero(begin, strlen));
begin++;
}
return nums;
}
module.exports.moment = function moment(date, formatStr = 'YYYY:MM:DD') {
if (!date && date !== 0) date = new Date();
date = new Date(date);
if (date.toString() === 'Invalid Date') throw new Error('Invalid Date');
let getDateValue = (method, fn) => (fn ? fn(date[`get${method}`]()) : date[`get${method}`]());
let map = new Map();
map.set(/(Y+)/i, () => getDateValue('FullYear', year => (year + '').substr(4 - RegExp.$1.length)));
map.set(/(M+)/, () => getDateValue('Month', month => partStartWithZero(month + 1, RegExp.$1.length)));
map.set(/(D+)/i, () => getDateValue('Date', date => partStartWithZero(date, RegExp.$1.length)));
map.set(/(H+)/i, () => getDateValue('Hours', hour => partStartWithZero(hour, RegExp.$1.length)));
map.set(/(m+)/, () => getDateValue('Minutes', minute => partStartWithZero(minute, RegExp.$1.length)));
map.set(/(s+)/, () => getDateValue('Seconds', second => partStartWithZero(second, RegExp.$1.length)));
for (const [reg, fn] of map) {
if (reg.test(formatStr)) {
formatStr = formatStr.replace(RegExp.$1, fn.call(null));
}
}
return formatStr;
}
module.exports.iso2utc = function (string) {
let regexp = "([0-9]{4})(-([0-9]{2})(-([0-9]{2})(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?";
if (string) {
let d = string.match(new RegExp(regexp));
let offset = 0;
let date = new Date(d[1], 0, 1);
if (d[3]) {
date.setMonth(d[3] - 1);
}
if (d[5]) {
date.setDate(+d[5]);
}
if (d[7]) {
date.setHours(d[7]);
}
if (d[8]) {
date.setMinutes(d[8]);
}
if (d[10]) {
date.setSeconds(d[10]);
}
if (d[12]) {
date.setMilliseconds(Number("0." + d[12]) * 1000);
}
if (d[14]) {
offset = (Number(d[16]) * 60) + Number(d[17]);
offset *= ((d[15] == '-') ? 1 : -1);
}
offset -= date.getTimezoneOffset();
return (Number(date) + (offset * 60 * 1000));
} else {
return string;
}
}