c5_labsci/fapp/ciyon_ap/components/ciy-select/ciy-select.vue
2026-01-27 00:52:00 +08:00

477 lines
10 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="_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="range.length > minselectsearch">
<view class="itm">
<input v-model="searchval" :placeholder="lang('placeholder.key')" @confirm="searchconfirm" />
</view>
</view>
<view class="_list" :class="rows==1?'_flex':'_grid'" :style="'grid-template-columns: repeat('+rows+', 1fr);text-align:' + itemalign">
<slot name="list" :data="{range:mrange,searchkey:searchval,popclass:popclass,onselect:Selitem}">
<view class="_item" :class="{_select:tvalue.id==item.id}" v-for="(item,index) in mrange" :key="index" @tap="Selitem(index)">
{{item.name}}
</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>
</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: 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: {}, //弹出的选项动画
addrange: [] //动态新增的选项,不能污染属性,保持单向性
};
},
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
});
for (var i in this.range) {
if(this.chkuse && this.range[i].isuse == 2)
continue;
if (this.range[i].name.indexOf(this.searchval) > -1)
mr.push(this.range[i]);
}
for (var i in this.addrange) {
if(this.chkuse && this.range[i].isuse == 2)
continue;
if (this.addrange[i].name.indexOf(this.searchval) > -1)
mr.push(this.addrange[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.range) {
if (this.range[i].id == val)
return {
_index: i,
...this.range[i]
};
}
if (this.range.length > 0 && this.placeholder === '')
return {
_index: 0,
...this.range[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
});
},
Addrange(newrange) {
if (!(newrange instanceof Array))
return 'ERR: Not Array.';
var addarr = [];
for (var i in newrange) {
var bfind = false;
for (var r in this.range) {
if (newrange[i].id == this.range[r].id) {
bfind = true;
break;
}
}
if (bfind)
continue;
for (var r in this.addrange) {
if (newrange[i].id == this.addrange[r].id) {
bfind = true;
break;
}
}
if (bfind)
continue;
for (var r in addarr) {
if (newrange[i].id == addarr[r].id) {
bfind = true;
break;
}
}
if (bfind)
continue;
addarr.push(newrange[i]);
}
this.addrange.push(...addarr);
return true;
},
Selitem(index) {
this.v = 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();
});
}
}
}
</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._top ._list._flex {
/* flex-direction: column-reverse; */
}
._select ._pop ._list._flex {
display: flex;
flex-direction: column;
width: 100%;
overflow: auto;
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._top ._list._grid {
transform: rotate(180deg);
}
._select ._pop._top ._list._grid ._item {
transform: rotate(180deg);
}
._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>