源文件
This commit is contained in:
105
jeepay-ui-uapp-agent/components/xp-picker/util.js
Normal file
105
jeepay-ui-uapp-agent/components/xp-picker/util.js
Normal file
@@ -0,0 +1,105 @@
|
||||
const isLeapYear = y => y % 4 == 0 && y % 100 != 0 || y % 100 == 0 && y % 400 == 0
|
||||
const variables = {
|
||||
y: {
|
||||
text: "年",
|
||||
range: [null, null]
|
||||
},
|
||||
m: {
|
||||
text: "月",
|
||||
range: [1, 12]
|
||||
},
|
||||
d: {
|
||||
text: "日",
|
||||
range: [1, 31]
|
||||
},
|
||||
h: {
|
||||
text: "时",
|
||||
range: [0, 23]
|
||||
},
|
||||
i: {
|
||||
text: "分",
|
||||
range: [0, 59]
|
||||
},
|
||||
s: {
|
||||
text: "秒",
|
||||
range: [0, 59]
|
||||
}
|
||||
}
|
||||
export function templateFactory({
|
||||
mode,
|
||||
value,
|
||||
yearRange
|
||||
}) {
|
||||
const [start, end] = yearRange
|
||||
let ret = {}
|
||||
for (const key of mode) {
|
||||
ret[key] = variables[key]
|
||||
}
|
||||
if (mode.indexOf("y") !== -1) ret['y'].range = [start || 2016, end || new Date().getFullYear()]
|
||||
if (mode.indexOf("d") !== -1) {
|
||||
const date = getDate(value || getLocalTime(mode))
|
||||
ret['d'].range = [1, date]
|
||||
}
|
||||
return ret
|
||||
}
|
||||
export function getDate(dt) {
|
||||
const s = dt.substring(0, dt.lastIndexOf("-"))
|
||||
let year, month
|
||||
const d = new Date()
|
||||
switch (s.length) {
|
||||
case 0:
|
||||
year = d.getFullYear()
|
||||
month = d.getMonth() + 1
|
||||
break;
|
||||
case 2:
|
||||
year = d.getFullYear()
|
||||
month = parseInt(s)
|
||||
break;
|
||||
default:
|
||||
const [y, m] = s.split("-")
|
||||
year = parseInt(y)
|
||||
month = parseInt(m)
|
||||
break;
|
||||
}
|
||||
const days = [31, isLeapYear(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
||||
return days[month - 1]
|
||||
}
|
||||
export function getLocalTime(fmt) {
|
||||
if (!fmt) return null
|
||||
const da = new Date()
|
||||
const y = fmtNumber(da.getFullYear()),
|
||||
m = fmtNumber(da.getMonth() + 1),
|
||||
d = fmtNumber(da.getDate()),
|
||||
h = fmtNumber(da.getHours()),
|
||||
i = fmtNumber(da.getMinutes()),
|
||||
s = fmtNumber(da.getSeconds())
|
||||
const types = {
|
||||
'y': `${y}`,
|
||||
'm': `${m}`,
|
||||
'd': `${d}`,
|
||||
'h': `${h}`,
|
||||
'i': `${i}`,
|
||||
's': `${s}`,
|
||||
'ym': `${y}-${m}`,
|
||||
'md': `${m}-${d}`,
|
||||
'hi': `${h}:${i}`,
|
||||
'is': `${i}:${s}`,
|
||||
'ymd': `${y}-${m}-${d}`,
|
||||
'his': `${h}:${i}:${s}`,
|
||||
'mdh': `${m}-${d} ${h}`,
|
||||
'ymdh': `${y}-${m}-${d} ${h}`,
|
||||
'mdhi': `${m}-${d} ${h}:${i}`,
|
||||
'mdhis': `${m}-${d} ${h}:${m}:${s}`,
|
||||
'yd':`${y}-${d}`,
|
||||
'ymdhi': `${y}-${m}-${d} ${h}:${i}`,
|
||||
'ymdhis': `${y}-${m}-${d} ${h}:${i}:${s}`,
|
||||
}
|
||||
return types[fmt]
|
||||
}
|
||||
export function fmtNumber(n) {
|
||||
// return n.toString().padStart(2,"0")
|
||||
return n > 9 ? n + "" : "0" + n
|
||||
}
|
||||
export function time2Timestamp(timer) {
|
||||
return new Date(timer).getTime()
|
||||
}
|
||||
345
jeepay-ui-uapp-agent/components/xp-picker/xp-picker.vue
Normal file
345
jeepay-ui-uapp-agent/components/xp-picker/xp-picker.vue
Normal file
@@ -0,0 +1,345 @@
|
||||
<template>
|
||||
<view>
|
||||
<view v-if="hasSlot" @tap="show">
|
||||
<slot />
|
||||
</view>
|
||||
<view class="xp-picker" :style="{ visibility: pickerVisible ? 'visible' : 'hidden' }">
|
||||
<view
|
||||
class="xp-picker-mask"
|
||||
:class="{ 'xp-picker-animation': animation }"
|
||||
:style="{ opacity: pickerVisible ? 0.6 : 0 }"
|
||||
@tap="_cancel"
|
||||
></view>
|
||||
<view
|
||||
class="xp-picker-container"
|
||||
:class="{ 'xp-picker-container--show': pickerVisible, 'xp-picker-animation': animation }"
|
||||
>
|
||||
<view v-if="actionPosition === 'top'" class="xp-picker-action">
|
||||
<view class="xp-picker-action--cancel" @tap="_cancel">取消</view>
|
||||
<view class="xp-picker-action--confirm" @tap="_confirm">确定</view>
|
||||
</view>
|
||||
<view v-if="isError" class="xp-picker-error" :style="{ height: height + 'vh' }">
|
||||
<text>Error!please check your configuration</text>
|
||||
<text>(请检查你的配置 或 查看控制台错误信息)</text>
|
||||
</view>
|
||||
<picker-view
|
||||
v-else
|
||||
:style="{ height: height + 'vh' }"
|
||||
indicator-style="height:40px;"
|
||||
:value="selected"
|
||||
@change="_change"
|
||||
>
|
||||
<picker-view-column v-for="(k, i) in modeArr" :key="i" class="xp-picker-column">
|
||||
<view class="xp-picker-list-item" v-for="(item, index) in cols[i]" :key="index">
|
||||
{{ item + units[i] }}
|
||||
</view>
|
||||
</picker-view-column>
|
||||
</picker-view>
|
||||
<view v-if="actionPosition === 'bottom'" class="xp-picker-btns">
|
||||
<view class="xp-button xp-button--cancel" @tap="_cancel">取消</view>
|
||||
<view class="xp-button xp-button--confirm" @tap="_confirm">确定</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import tool from "@/util/tool.js"
|
||||
import { templateFactory, getLocalTime, fmtNumber, time2Timestamp, getDate } from "./util.js"
|
||||
export default {
|
||||
name: "XpPicker",
|
||||
data() {
|
||||
return {
|
||||
isError: true,
|
||||
isConfirm: false,
|
||||
pickerVisible: false,
|
||||
template: {},
|
||||
cols: [],
|
||||
selected: [],
|
||||
}
|
||||
},
|
||||
props: {
|
||||
startOrEnd: {
|
||||
type: String,
|
||||
default: "start",
|
||||
},
|
||||
startTime: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
endTime: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
default: "ymd",
|
||||
},
|
||||
animation: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
height: {
|
||||
type: [Number, String],
|
||||
default: 35,
|
||||
},
|
||||
"action-position": {
|
||||
type: String,
|
||||
default: "bottom",
|
||||
},
|
||||
"year-range": {
|
||||
type: Array,
|
||||
default: () => [2010, 2035],
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
history: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
mode() {
|
||||
this.render()
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
hasSlot() {
|
||||
return !!this.$slots["default"]
|
||||
},
|
||||
modeArr() {
|
||||
return this.mode.split("")
|
||||
},
|
||||
units() {
|
||||
const arr = []
|
||||
for (const k in this.template) {
|
||||
if (this.mode.indexOf(k) !== -1) arr.push(this.template[k].text)
|
||||
}
|
||||
return arr
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.render()
|
||||
},
|
||||
methods: {
|
||||
render() {
|
||||
this.assert() //检查用户配置
|
||||
this.template = templateFactory(this) //生成所需列 默认模板
|
||||
this.initCols() //根据模板 初始化列
|
||||
this.initSelected() //设置默认值
|
||||
},
|
||||
assert() {
|
||||
if ("ymdhis".indexOf(this.mode) === -1) {
|
||||
throw new Error("render error,illegal 'mode'")
|
||||
}
|
||||
if (getLocalTime(this.mode) == undefined) {
|
||||
throw new Error("render error,the 'mode' is not found")
|
||||
}
|
||||
if (this.value != null) {
|
||||
const arr = this.value.split(/-|:|\s/)
|
||||
if (arr.length != this.modeArr.length) {
|
||||
throw new Error("render error,because the 'value' cannot be formatted as 'mode'")
|
||||
}
|
||||
}
|
||||
if (this.yearRange.length !== 2) {
|
||||
throw new Error("render error,because the length of array 'year-rang' must be 2")
|
||||
}
|
||||
this.isError = false
|
||||
},
|
||||
initCols() {
|
||||
for (const k of this.mode) {
|
||||
const range = this.template[k].range
|
||||
this.fillCol(k, ...range)
|
||||
}
|
||||
},
|
||||
initSelected() {
|
||||
const v = this.value || getLocalTime(this.mode)
|
||||
if (this.startOrEnd === "start") {
|
||||
this.setSelected(this.startTime)
|
||||
} else {
|
||||
this.setSelected(this.endTime)
|
||||
}
|
||||
},
|
||||
fillCol(k, s, e) {
|
||||
const index = this.mode.indexOf(k)
|
||||
let arr = []
|
||||
for (let i = s; i <= e; i++) arr.push(fmtNumber(i))
|
||||
this.$set(this.cols, index, arr)
|
||||
},
|
||||
//dt 时间字符串 如 '2020-02-16'
|
||||
setSelected(dt) {
|
||||
const arr = dt.split(/-|:|\s/)
|
||||
const a = this.cols
|
||||
for (let i = 0; i < a.length; i++) this.$set(this.selected, i, a[i].indexOf(arr[i]))
|
||||
},
|
||||
resolveCurrentDt() {
|
||||
let str = ""
|
||||
for (let i = 0; i < this.selected.length; i++) str += this.cols[i][this.selected[i]] + this.units[i]
|
||||
let dt = str
|
||||
.replace("年", "-")
|
||||
.replace("月", "-")
|
||||
.replace("日", " ")
|
||||
.replace("时", ":")
|
||||
.replace("分", ":")
|
||||
.replace("秒", "")
|
||||
if (!this.mode.endsWith("s")) dt = dt.substring(0, dt.length - 1)
|
||||
dt = dt.split("undefined").join("00")
|
||||
return dt
|
||||
},
|
||||
show() {
|
||||
if (this.history) {
|
||||
if (!this.isConfirm) this.initSelected()
|
||||
} else this.initSelected()
|
||||
this.pickerVisible = true
|
||||
},
|
||||
_confirm() {
|
||||
if (!this.isError) this.$emit("confirm", this._getResult())
|
||||
if (!this.isConfirm) this.isConfirm = true
|
||||
this.pickerVisible = false
|
||||
},
|
||||
_getResult() {
|
||||
const detail = {
|
||||
value: this.resolveCurrentDt(),
|
||||
}
|
||||
const tp = time2Timestamp(detail.value)
|
||||
if (!isNaN(tp)) detail.timestamp = tp
|
||||
return detail
|
||||
},
|
||||
_cancel() {
|
||||
this.$emit("cancel")
|
||||
this.pickerVisible = false
|
||||
},
|
||||
_change(e) {
|
||||
let col
|
||||
const newValue = e.detail.value
|
||||
for (let i = 0; i < newValue.length; i++) {
|
||||
if (newValue[i] !== this.selected[i]) {
|
||||
col = this.modeArr[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
this.selected = newValue
|
||||
const index = this.mode.indexOf("d")
|
||||
if (index !== -1 && (col === "y" || col === "m")) {
|
||||
const currentDt = this.resolveCurrentDt()
|
||||
this.fillCol("d", 1, getDate(currentDt))
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.xp-picker {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 999;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.xp-picker-container {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
transform: translateY(100%);
|
||||
z-index: 999;
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.xp-picker-container--show {
|
||||
transform: translateY(0);
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.xp-picker-mask {
|
||||
z-index: 998;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgb(0, 0, 0);
|
||||
}
|
||||
|
||||
.xp-picker-animation {
|
||||
transition: all 0.25s;
|
||||
}
|
||||
|
||||
.xp-picker-error {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #ff0000;
|
||||
}
|
||||
|
||||
.xp-picker-action {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 90rpx;
|
||||
padding: 0 28rpx;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
font-size: 34rpx;
|
||||
border-bottom: 0.5px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.xp-picker-btns {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
height: 160rpx;
|
||||
padding: 0 20rpx;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.xp-button {
|
||||
height: 45px;
|
||||
}
|
||||
|
||||
.xp-button--cancel {
|
||||
width: 115px;
|
||||
border-radius: 5px;
|
||||
background: #fff;
|
||||
border: 0.5px solid #c5c7cc;
|
||||
}
|
||||
|
||||
.xp-button--confirm {
|
||||
width: 180px;
|
||||
height: 45px;
|
||||
border-radius: 5px;
|
||||
background: #3981ff;
|
||||
border: 1.5px solid #3981ff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.xp-button--confirm,
|
||||
.xp-button--cancel {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.xp-picker-column {
|
||||
text-align: center;
|
||||
border: none;
|
||||
font-size: 34rpx;
|
||||
}
|
||||
|
||||
.xp-picker-list-item {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user