Files
cashier_admin_app/uni_modules/xp-picker/components/xp-picker/xp-picker.vue
2024-09-10 10:49:47 +08:00

412 lines
8.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="xp-h-full">
<view @tap="show" class="xp-h-full">
<slot>
<view class="picker-label xp-h-full" :class="{'is-placeholder':label===placeholder}">{{label}}
</view>
</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>请检查你的配置 查看控制台错误信息</text>
</view>
<!-- #ifdef VUE3 -->
<picker-view v-else style="margin-top: 40rpx;" :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>
<!-- #endif -->
<!-- #ifdef VUE2 -->
<picker-view v-else style="margin-top: 40rpx;" :style="{'height':height+'vh'}"
indicator-style="height:40px;" :value="selected" @change="_change">
<picker-view-column v-for="(k,i) in modeArr" :key="k" 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>
<!-- #endif -->
<view v-if="actionPosition==='bottom'" class="xp-picker-btns">
<button class="xp-button xp-button--cancel" @tap="_cancel">取消</button>
<button class="xp-button xp-button--confirm" @tap="_confirm">确定</button>
</view>
</view>
</view>
</view>
</template>
<script>
import {
templateFactory,
getLocalTime,
fmtNumber,
time2Timestamp,
getDate,
getForm
} from "./util.js"
import assert from './assert.js'
export default {
name: 'XpPicker',
data() {
return {
isError: false,
pickerVisible: false,
cols: [],
selected: []
}
},
props: {
mode: {
type: String,
default: "ymd"
},
animation: {
type: Boolean,
default: true
},
height: {
type: [Number, String],
default: 35
},
actionPosition: {
type: String,
default: "bottom"
},
yearRange: {
type: Array,
default: () => [2010, null]
},
value: String,
// #ifdef VUE3
modelValue: String,
// #endif
history: Boolean,
placeholder: {
type: String,
default: ''
}
},
watch: {
mode() {
this.render()
}
},
// #ifdef VUE3
emits: ['confirm', 'update:modelValue'],
// #endif
computed: {
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
},
label() {
const val = this.value || this.modelValue
if (val) return val
return this.placeholder
}
},
created() {
this.bindForm()
this.isConfirm = false
this.template = {}
this.render()
// #ifdef VUE2
if (this.value) {
this.setSelected(this.value)
}
// #endif
// #ifdef VUE3
if (this.modelValue) {
this.setSelected(this.modelValue)
}
// #endif
},
methods: {
bindForm() {
this.form = getForm.call(this, 'uniForms')
this.formItem = getForm.call(this, 'uniFormsItem')
if (this.form && this.formItem) {
if (this.formItem.name) {
if (!this.is_reset) {
this.is_reset = false
// #ifdef VUE2
this.formItem.setValue(this.value)
// #endif
// #ifdef VUE3
this.formItem.setValue(this.modelValue)
// #endif
}
this.form.inputChildrens.push(this)
}
}
},
render() {
try {
assert(this)
this.template = templateFactory(this) //生成所需列 默认模板
this.initCols() //根据模板 初始化列
this.initSelected() //设置默认值
} catch (e) {
console.error(e)
this.isError = true
}
},
initCols() {
for (const k of this.mode) {
const range = this.template[k].range
this.fillCol(k, ...range)
}
},
initSelected() {
let dt
// #ifdef VUE2
dt = this.value
// #endif
// #ifdef VUE3
dt = this.modelValue
// #endif
if (!dt) dt = getLocalTime(this.mode)
if (!dt) return
this.setSelected(dt)
},
fillCol(k, s, e) {
const index = this.mode.indexOf(k)
let arr = []
for (let i = s; i <= e; i++)
arr.push(fmtNumber(i))
// #ifdef VUE2
this.$set(this.cols, index, arr)
// #endif
// #ifdef VUE3
this.cols[index] = arr
// #endif
},
//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]))
},
show() {
if ((!this.value && !this.modelValue) || (!this.history) || (this.history && !this.isConfirm)) {
this.initSelected()
}
this.pickerVisible = true
},
_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)
return dt
},
_confirm() {
const result = this._getResult()
const val = result.value
if (!this.isError) {
// #ifdef VUE2
this.$emit('input', val)
// #endif
// #ifdef VUE3
this.$emit('update:modelValue', val)
// #endif
this.$emit('confirm', result)
this.isConfirm = true
if (this.formItem) this.formItem.setValue(val)
}
this.pickerVisible = false
},
_getResult() {
const value = this._resolveCurrentDt()
const detail = {
value
}
const tp = time2Timestamp(value)
if (!isNaN(tp)) detail.timestamp = tp
return detail
},
_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-h-full {
height: 100%;
}
.picker-label {
height: 100%;
display: flex;
align-items: center;
padding-left: 10px;
font-size: 13px;
}
.is-placeholder {
color: #999;
}
.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;
border-radius: 16px 16px 0 0;
}
.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;
padding: 40rpx 30rpx;
box-sizing: border-box;
position: relative;
}
.xp-button {
line-height: 2.3;
font-size: 32rpx;
margin: 0;
padding: 0 80rpx;
transform: translate(0upx, 0upx);
}
.xp-button:active:not([disabled]) {
transform: translate(1upx, 1upx);
}
.xp-button:after {
border: none;
}
.xp-button--cancel {
background-color: #f5f5f5;
color: #47a16e;
}
.xp-button--confirm {
background-color: #47a16e;
color: #fff;
}
.xp-picker-action--cancel {
opacity: .7;
}
.xp-picker-action--confirm {
color: #007aff;
}
.xp-picker-column {
text-align: center;
border: none;
font-size: 32rpx;
}
.xp-picker-list-item {
display: flex;
justify-content: center;
align-items: center;
height: 40px;
}
</style>