459 lines
10 KiB
Vue
459 lines
10 KiB
Vue
<template>
|
||
<view class="_select">
|
||
<view class="_mask" @touchmove.stop.prevent="() => { }" v-if="popclass" @tap="hidepop"></view>
|
||
<view class="_pop" :animation="anidatapop" v-if="popclass" :class="popclass">
|
||
<view class="ciy-hr"></view>
|
||
<view class="_search" v-if="mrange.length > minselectsearch">
|
||
<view class="itm">
|
||
<input v-model="searchval" :placeholder="lang('placeholder.key')" @confirm="searchconfirm" />
|
||
</view>
|
||
</view>
|
||
<scroll-view id="select_list" class="_list" :scroll-top="itemtop" scroll-y>
|
||
<slot name="list" :data="{range:mrange,searchkey:searchval,popclass:popclass,onselect:Selitem}">
|
||
<view :class="rows==1?'_flex':'_grid'" :style="'grid-template-columns: repeat('+rows+', 1fr);text-align:' + itemalign">
|
||
<view :id="'selectitem-' + index" class="_item" :class="{_select:tvalue.id==item.id}" v-for="(item,index) in mrange" :key="index" @tap="Selitem(index)">
|
||
{{item.name}}
|
||
</view>
|
||
</view>
|
||
<template v-if="mrange.length == 0">
|
||
<view v-if="searchval" class="_nodata">
|
||
{{lang('select.nosearch')}}
|
||
<text class="_code">{{searchval}}</text>
|
||
</view>
|
||
<view v-else class="_nodata">{{lang('select.nodata')}}</view>
|
||
</template>
|
||
</slot>
|
||
</scroll-view>
|
||
<view class="ciy-hr"></view>
|
||
<view style="flex:1;pointer-events: none;width:100%;" @touchmove.stop.prevent="() => { }" @tap="hidepop"></view>
|
||
</view>
|
||
<view class="_show" :class="{_left:left}" @tap="showpop">
|
||
<view class="_txt" :style="ciystyle">
|
||
<view v-if="tvalue.name" :style="{color:disabled?'var(--bg6)':'var(--txt9)'}">{{tvalue.name}}</view>
|
||
<view v-else-if="placeholder !== ''" style="color:var(--txt1);">{{placeholder?placeholder:lang('placeholder.select')}}</view>
|
||
</view>
|
||
<view v-if="!noarrow" class="_arrow ciy-icon-arrow" :class="{_sel:popclass}"></view>
|
||
</view>
|
||
|
||
<input type="hidden" :name="name" :value="tvalue.id" style="display:none;" />
|
||
<input v-if="hasmore" type="hidden" :name="name+'_name'" :value="tvalue.name" style="display:none;" />
|
||
</view>
|
||
</template>
|
||
<script>
|
||
export default {
|
||
behaviors: ['uni://form-field-group'],
|
||
emits: ['change', 'update:modelValue', 'search'],
|
||
props: {
|
||
name: {
|
||
type: String
|
||
},
|
||
modelValue: {
|
||
type: [String, Number],
|
||
default: ''
|
||
},
|
||
value: {
|
||
type: [String, Number],
|
||
default: ''
|
||
},
|
||
disabled: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
initevent: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
ciystyle: {
|
||
type: [String, Object]
|
||
},
|
||
left: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
hasmore: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
chkuse: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
range: {
|
||
type: [String, Array],
|
||
default: []
|
||
},
|
||
placeholder: {
|
||
type: String
|
||
},
|
||
diastema: { //上下间隙
|
||
type: Number,
|
||
default: 16
|
||
},
|
||
itemalign: { //item对齐
|
||
type: String,
|
||
default: ''
|
||
},
|
||
all: { //0值选项
|
||
type: String,
|
||
default: ''
|
||
},
|
||
noarrow: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
rows: {
|
||
type: Number,
|
||
default: 1
|
||
},
|
||
minselectsearch: { //选项少于n,则不显示搜索
|
||
type: Number,
|
||
default: 5
|
||
}
|
||
},
|
||
data() {
|
||
return {
|
||
v: '',
|
||
searchval: '', //搜索键入内容
|
||
popclass: '', //值包含[btm/top],制定弹出在上方还是下方
|
||
anidatapop: {}, //弹出的选项动画
|
||
itemtop: 0,
|
||
};
|
||
},
|
||
watch: {
|
||
searchval: {
|
||
handler(newD, oldD) {
|
||
this.$emit('search', {
|
||
name: this.name,
|
||
from: 'input',
|
||
com: this,
|
||
value: newD
|
||
});
|
||
}
|
||
},
|
||
value: {
|
||
handler(newD, oldD) {
|
||
if (newD || oldD)
|
||
this.v = 'value';
|
||
},
|
||
immediate: true
|
||
},
|
||
modelValue: {
|
||
handler(newD, oldD) {
|
||
if (newD || oldD)
|
||
this.v = 'modelValue';
|
||
},
|
||
immediate: true
|
||
},
|
||
},
|
||
computed: {
|
||
mrange() {
|
||
var mr = [];
|
||
if (this.all)
|
||
mr.push({
|
||
id: 0,
|
||
name: this.all
|
||
});
|
||
var tmprange = this.range;
|
||
if (!this.isarray(this.range)) {
|
||
tmprange = [];
|
||
let oprang = (this.range + '').split('.');
|
||
for (let r in oprang) {
|
||
let idx = oprang[r].indexOf(':');
|
||
if (idx <= 0)
|
||
continue;
|
||
tmprange.push({
|
||
id: oprang[r].substring(0, idx),
|
||
name: oprang[r].substring(idx + 1)
|
||
});
|
||
}
|
||
}
|
||
for (var i in tmprange) {
|
||
if (this.chkuse && tmprange[i].isuse == 2)
|
||
continue;
|
||
if (tmprange[i].name.indexOf(this.searchval) > -1)
|
||
mr.push(tmprange[i]);
|
||
}
|
||
return mr;
|
||
},
|
||
tvalue() {
|
||
var val = '';
|
||
if (this.v == 'modelValue') {
|
||
if (typeof(this.modelValue) == 'number')
|
||
val = this.modelValue;
|
||
else if (this.modelValue)
|
||
val = this.modelValue;
|
||
} else if (this.v == 'value') {
|
||
if (typeof(this.value) == 'number')
|
||
val = this.value;
|
||
else if (this.value)
|
||
val = this.value;
|
||
} else if (this.v instanceof Object) {
|
||
return this.v;
|
||
}
|
||
for (var i in this.mrange) {
|
||
if (this.mrange[i].id == val)
|
||
return {
|
||
index: i,
|
||
...this.mrange[i]
|
||
};
|
||
}
|
||
if (this.mrange.length > 0 && this.placeholder === '')
|
||
return {
|
||
index: 0,
|
||
...this.mrange[0]
|
||
};
|
||
return {
|
||
id: 0,
|
||
index: -1,
|
||
name: ''
|
||
};
|
||
}
|
||
},
|
||
mounted() {
|
||
if (this.initevent) {
|
||
this.$emit('change', {
|
||
name: this.name,
|
||
from: 'init',
|
||
value: {
|
||
...this.tvalue
|
||
}
|
||
});
|
||
}
|
||
this.lastvalue = {
|
||
...this.tvalue
|
||
};
|
||
},
|
||
methods: {
|
||
searchconfirm(e) {
|
||
this.$emit('search', {
|
||
name: this.name,
|
||
from: 'confirm',
|
||
com: this,
|
||
value: e.detail.value
|
||
});
|
||
},
|
||
Selitem(index) {
|
||
this.v = {
|
||
index: index,
|
||
...this.mrange[index]
|
||
};
|
||
this.$emit('update:modelValue', this.v.id);
|
||
this.$emit('change', {
|
||
name: this.name,
|
||
from: 'select',
|
||
value: {
|
||
index: index,
|
||
...this.v
|
||
},
|
||
lastvalue: this.lastvalue
|
||
});
|
||
this.lastvalue = {
|
||
index: index,
|
||
...this.v
|
||
};
|
||
this.hidepop();
|
||
},
|
||
hidepop() {
|
||
var animation = uni.createAnimation({});
|
||
animation.translateX('100vw');
|
||
animation.opacity(0);
|
||
animation.step({
|
||
duration: 200
|
||
});
|
||
animation.height('auto');
|
||
this.anidatapop = animation.export();
|
||
setTimeout(() => {
|
||
this.popclass = '';
|
||
}, 300);
|
||
},
|
||
async showpop() {
|
||
if (this.disabled)
|
||
return;
|
||
var app = getApp();
|
||
app.globalData.scrollcbs['com_select'] = e => {
|
||
delete app.globalData.scrollcbs['com_select'];
|
||
this.hidepop();
|
||
};
|
||
const {
|
||
headerheight,
|
||
footerheight
|
||
} = await this.com_gethdft();
|
||
var fixtop = 0;
|
||
|
||
this.searchval = '';
|
||
this.popclass = 'temp';
|
||
var mainrect = await this.getrect('._pop');
|
||
if (!mainrect)
|
||
return;
|
||
var showrect = await this.getrect('._show');
|
||
if (!showrect)
|
||
return;
|
||
var toph = showrect.top - headerheight - this.diastema;
|
||
var btmh = app.globalData._sysinfo.windowHeight - footerheight - showrect.bottom - this.diastema;
|
||
var topcls = '_btm';
|
||
var syheight, sytop;
|
||
if (toph > btmh) {
|
||
topcls = '_top';
|
||
syheight = mainrect.height;
|
||
if (syheight > toph)
|
||
syheight = toph;
|
||
sytop = headerheight + fixtop + toph - syheight;
|
||
} else {
|
||
syheight = mainrect.height;
|
||
if (syheight > btmh)
|
||
syheight = btmh;
|
||
sytop = showrect.bottom + this.diastema - fixtop;
|
||
}
|
||
this.popclass = topcls;
|
||
this.$nextTick(() => {
|
||
var animation = uni.createAnimation({});
|
||
animation.translateX(0);
|
||
animation.top(sytop);
|
||
animation.height(syheight);
|
||
animation.opacity(1);
|
||
animation.step({
|
||
duration: 200
|
||
});
|
||
this.anidatapop = animation.export();
|
||
if (!this.tvalue.index)
|
||
return;
|
||
this.itemtop = 0;
|
||
setTimeout(async () => {
|
||
var listrect = await this.getrect('#select_list');
|
||
var rect = await this.getrect('#selectitem-' + this.tvalue.index);
|
||
if (!rect || !listrect)
|
||
return;
|
||
this.itemtop = rect.top - listrect.height / 2 - listrect.top;
|
||
}, 200);
|
||
});
|
||
}
|
||
|
||
}
|
||
}
|
||
</script>
|
||
<style scoped>
|
||
._select ._pop {
|
||
position: fixed;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
opacity: 0;
|
||
left: 0;
|
||
right: 0;
|
||
transform: translateX(100vw);
|
||
transform: translateY(38px);
|
||
z-index: 992;
|
||
}
|
||
|
||
._select ._pop ._search {
|
||
background: var(--bg2);
|
||
box-sizing: border-box;
|
||
padding: 0.5em;
|
||
width: 100%;
|
||
}
|
||
|
||
._select ._pop ._search>.itm {
|
||
background: var(--bg1);
|
||
border: 1px solid var(--bg4);
|
||
border-radius: 0.5em;
|
||
padding: 0.5em;
|
||
text-align: left;
|
||
}
|
||
|
||
._select ._pop._top {
|
||
justify-content: flex-end;
|
||
flex-direction: column-reverse;
|
||
}
|
||
|
||
._select ._pop ._list {
|
||
width: 100%;
|
||
overflow: auto;
|
||
text-align: left;
|
||
background: var(--bg2);
|
||
}
|
||
|
||
._select ._pop ._list ._flex {
|
||
display: flex;
|
||
flex-direction: column;
|
||
width: 100%;
|
||
text-align: left;
|
||
overscroll-behavior-y: contain !important;
|
||
background: var(--bg2);
|
||
}
|
||
|
||
._select ._item._select {
|
||
font-weight: bold;
|
||
color: var(--man6);
|
||
}
|
||
|
||
._select ._pop ._list ._flex ._item {
|
||
padding: 1em 1em;
|
||
width: 100%;
|
||
border-top: 1px solid var(--bg4);
|
||
}
|
||
|
||
._select ._pop ._list ._grid {
|
||
display: grid;
|
||
width: 100%;
|
||
overflow: auto;
|
||
text-align: left;
|
||
background: var(--bg2);
|
||
}
|
||
|
||
._select ._pop ._list ._grid ._item {
|
||
padding: 1em 1em;
|
||
width: 100%;
|
||
border-top: 1px solid var(--bg4);
|
||
}
|
||
|
||
._select ._pop ._list ._nodata {
|
||
background: var(--bg2);
|
||
text-align: center;
|
||
padding: 2em 0 1em 0;
|
||
color: var(--txt1);
|
||
}
|
||
|
||
._select {
|
||
position: relative;
|
||
}
|
||
|
||
._select ._show {
|
||
padding: 0;
|
||
overflow: hidden;
|
||
white-space: nowrap;
|
||
text-overflow: ellipsis;
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
align-items: center;
|
||
}
|
||
|
||
._select ._show._left {
|
||
justify-content: flex-start;
|
||
}
|
||
|
||
._select ._show._left ._txt {
|
||
text-align: left;
|
||
}
|
||
|
||
._select ._show ._arrow {
|
||
width: 1.5em;
|
||
height: 1.5em;
|
||
margin-left: 0.3em;
|
||
transition: transform 0.3s;
|
||
}
|
||
|
||
._select ._show ._arrow._sel {
|
||
transform: rotate(180deg);
|
||
}
|
||
|
||
._select ._mask {
|
||
position: fixed;
|
||
left: 0;
|
||
right: 0;
|
||
top: 0;
|
||
bottom: 0;
|
||
backgroundx: #f3abec99;
|
||
z-index: 992;
|
||
backdrop-filterx: blur(10px);
|
||
}
|
||
</style> |