mirror of
https://gitee.com/vant-contrib/vant-weapp.git
synced 2025-04-06 03:58:05 +08:00
[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:
parent
82d1a8ae98
commit
d3eee374dd
@ -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('-')})
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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 | 设置选中的时间显示的格式,支持 _YYYY,yyyy,YY,yy,MM,M,DD,dd,D,d,HH, hh,H,h,mm,m,ss,s_ |
|
||||
| bindchange | `String` | `是` | 无 | 用户点击`确认`触发该事件,返回值为按“年,月,日,时,分,秒”顺序的数组,可以通过`detail.value`获取 |
|
||||
@ -38,11 +37,10 @@
|
||||
|
||||
### 方法
|
||||
|
||||
| 名称 | 参数 | 描述 |
|
||||
| ------------ | ---- | ---- |
|
||||
| getFormatStr | 无 | 返回 `format` 格式的字符串,在 `picker-view` 为 true 时比较实用 |
|
||||
| 名称 | 参数 | 描述 |
|
||||
| ------------ | ---- | --------------------------------------------------------------- |
|
||||
| getFormatStr | 无 | 返回 `format` 格式的字符串,在 `picker-view` 为 true 时比较实用 |
|
||||
|
||||
### 增强优化
|
||||
|
||||
* 支持可选择时间区域限制
|
||||
* 滚动优化
|
||||
|
35
packages/datetime-picker/date-picker-view.wxml
Normal file
35
packages/datetime-picker/date-picker-view.wxml
Normal 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>
|
98
packages/datetime-picker/date-picker.js
Normal file
98
packages/datetime-picker/date-picker.js
Normal 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]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -1,3 +1,6 @@
|
||||
{
|
||||
"component": true
|
||||
"component": true,
|
||||
"usingComponents": {
|
||||
"pop-manager": "../common/pop-manager/index"
|
||||
}
|
||||
}
|
@ -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>
|
@ -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;
|
||||
}
|
6
packages/datetime-picker/picker-view-column.wxml
Normal file
6
packages/datetime-picker/picker-view-column.wxml
Normal 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>
|
79
packages/datetime-picker/utils.js
Normal file
79
packages/datetime-picker/utils.js
Normal 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;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user