548 lines
17 KiB
Vue
548 lines
17 KiB
Vue
<template>
|
||
<view class="relative">
|
||
<view class="tm-dropDownMenu absolute fulled" :style="{zIndex:101}">
|
||
<view
|
||
class="tm-dropDownMenu-bar"
|
||
:class="[
|
||
!black_tmeme && bgColor != 'white' ? bgColor : black_tmeme && bgColor == 'white' ? 'grey-darken-4' : bgColor,
|
||
black_tmeme ? '' : 'shadow-' + bgColor + '-' + shadow,
|
||
black_tmeme ? 'bk' : ''
|
||
]"
|
||
>
|
||
<tm-row align="center" justify="center">
|
||
<tm-col color="none" justify="center" align="middle" @click="changeIndex(index)" v-for="(item, index) in formartData" :key="index" :width="itemLength + '%'">
|
||
<view class="flex-center" :style="{height: height+'rpx',lineHeight:height+'rpx'}">
|
||
<text class=" pr-10" :style="{fontSize:fontSize+'rpx'}" :class="[activeIndex == index ? 'text-' + activeColor : 'text-' + unColor]">{{ item.title }}</text>
|
||
<tm-icons
|
||
style="line-height: 0;"
|
||
dense
|
||
:color="activeIndex == index ? activeColor : unColor"
|
||
size="24"
|
||
:name="activeIndex == index ? 'icon-sort-up' : 'icon-sort-down'"
|
||
></tm-icons>
|
||
</view>
|
||
</tm-col>
|
||
</tm-row>
|
||
</view>
|
||
|
||
<view v-if="formartData[activeIndex]" class="tm-dropDownMenu-body py-24 " :class="[black_tmeme ? 'grey-darken-5 bk' : 'white', 'shadow-' + shadow]">
|
||
<view v-if="formartData[activeIndex]['children']" :style="{maxHeight:maxHeight+'rpx',overflowY: 'auto'}">
|
||
<block v-for="(item, index) in formartData[activeIndex].children" :key="index">
|
||
<block v-if="item['children']&&rendIdx>=index" >
|
||
<view class="pa-24 text-size-s text-weight-b optAniopt" v-if="item['title']">{{ item.title }}</view>
|
||
<view class="optAniopt">
|
||
<block v-if="item.model == 'checkbox'">
|
||
<tm-groupcheckbox>
|
||
<block v-for="(item2, index2) in item.children" :key="index2">
|
||
<tm-checkbox :disabled="item2['disabled'] || item['disabled'] ? true : false" dense v-model="item2.checked">
|
||
<view class="px-10" :class="[item2['disabled'] || item['disabled'] ? 'opacity-6' : '']">
|
||
<tm-button
|
||
:fllowTheme="false"
|
||
:black="black_tmeme"
|
||
:theme="item2.checked ? color: (black_tmeme?'grey-darken-3':'grey-lighten-2')"
|
||
:font-color="item2.checked ? color : 'grey'"
|
||
dense
|
||
style="width: auto"
|
||
font-size="24"
|
||
height="70"
|
||
item-class="mx-14 my-10"
|
||
plan
|
||
block
|
||
icon="icon-check-circle"
|
||
:shadow="2"
|
||
:height="64"
|
||
:round="2"
|
||
>
|
||
{{ item2.title }}
|
||
</tm-button>
|
||
</view>
|
||
</tm-checkbox>
|
||
</block>
|
||
</tm-groupcheckbox>
|
||
</block>
|
||
<block v-if="item.model == 'radio'">
|
||
<tm-groupradio>
|
||
<block v-for="(item2, index2) in item.children" :key="index2">
|
||
<tm-radio :disabled="item2['disabled'] || item['disabled'] ? true : false" dense v-model="item2.checked">
|
||
<view class="px-10" :class="[item2['disabled'] || item['disabled'] ? 'opacity-6' : '']">
|
||
<tm-button
|
||
:fllowTheme="false"
|
||
:black="black_tmeme"
|
||
:theme="item2.checked ? color: (black_tmeme?'grey-darken-3':'grey-lighten-2')"
|
||
:font-color="item2.checked ? color : 'grey'"
|
||
dense
|
||
style="width: auto"
|
||
font-size="24"
|
||
height="70"
|
||
item-class="mx-14 my-10"
|
||
plan
|
||
block
|
||
icon="icon-check-circle"
|
||
:shadow="2"
|
||
:height="64"
|
||
:round="2"
|
||
>
|
||
{{ item2.title }}
|
||
</tm-button>
|
||
</view>
|
||
</tm-radio>
|
||
</block>
|
||
</tm-groupradio>
|
||
</block>
|
||
<block v-if="item.model == 'list'">
|
||
<tm-groupradio key="test">
|
||
<block v-for="(item2, index2) in item.children" :key="index2">
|
||
<tm-radio :inline="false" :disabled="item2['disabled'] || item['disabled'] ? true : false" dense v-model="item2.checked">
|
||
<view class="fulled">
|
||
|
||
<tm-listitem
|
||
:disabled="item2['disabled'] || item['disabled'] ? true : false"
|
||
:title-color="item2.checked ? color : 'grey-darken-3'"
|
||
:rightIconColor="item2.checked ? color : 'grey-lighten-3'"
|
||
:margin="[24, 12]"
|
||
:title="item2.title"
|
||
fontSize="28"
|
||
:shadow="0"
|
||
:borderBottom="true"
|
||
:rightIconSize='30'
|
||
:rightIcon="item2.checked ? 'icon-check-circle' : ''"
|
||
></tm-listitem>
|
||
</view>
|
||
</tm-radio>
|
||
</block>
|
||
</tm-groupradio>
|
||
</block>
|
||
|
||
<block v-if="item.model == 'listCheckbox'">
|
||
<tm-groupcheckbox >
|
||
<block v-for="(item2, index2) in item.children" :key="index2">
|
||
<tm-checkbox :inline="false" :disabled="item2['disabled'] || item['disabled'] ? true : false" dense v-model="item2.checked">
|
||
<view class="fulled">
|
||
|
||
<tm-listitem
|
||
:disabled="item2['disabled'] || item['disabled'] ? true : false"
|
||
:title-color="item2.checked ? color : 'grey-darken-3'"
|
||
:rightIconColor="item2.checked ? color : 'grey-lighten-3'"
|
||
:margin="[24, 12]"
|
||
:title="item2.title"
|
||
fontSize="28"
|
||
:shadow="0"
|
||
:borderBottom="true"
|
||
:rightIconSize='30'
|
||
:rightIcon="item2.checked ? 'icon-check-circle' : ''"
|
||
></tm-listitem>
|
||
</view>
|
||
</tm-checkbox>
|
||
</block>
|
||
</tm-groupcheckbox>
|
||
</block>
|
||
|
||
|
||
</view>
|
||
</block>
|
||
<block v-else>
|
||
<block v-if="item.model == 'input'&&rendIdx>=index" >
|
||
<view class="pa-24 text-size-s text-weight-b optAniopt" v-if="item['title']">{{ item.title }}</view>
|
||
<tm-input
|
||
:fllowTheme="fllowTheme"
|
||
border-color="grey-lighten-1"
|
||
:disabled="chiludis(item)"
|
||
:black="black_tmeme"
|
||
:color="color"
|
||
:border-bottom="false"
|
||
:input-type="item.type || 'text'"
|
||
:value.sync="item.value"
|
||
></tm-input>
|
||
</block>
|
||
<block v-if="item.model == 'slider'&&rendIdx>=index" >
|
||
<view class="pa-24 text-size-s text-weight-b optAniopt" v-if="item['title']">
|
||
{{ item.title }}
|
||
<text class="px-24 " :class="[`text-${color}`]">
|
||
{{ item.value ? item.value : '未设置' }}{{ item.value ? (item['suffix'] ? item.suffix : '') : '' }}
|
||
</text>
|
||
</view>
|
||
<view class="px-42 py-24 optAniopt">
|
||
<tm-slider
|
||
:fllowTheme="fllowTheme"
|
||
:disabled="chiludis(item)"
|
||
:black="black_tmeme"
|
||
:color="color"
|
||
:max="item.max ? item.max : 100"
|
||
v-model="item.value"
|
||
>
|
||
<template v-slot:tips>
|
||
{{ item.value }}
|
||
</template>
|
||
</tm-slider>
|
||
</view>
|
||
</block>
|
||
<block v-if="item.model == 'pickers'&&rendIdx>=index" >
|
||
<view class="pa-24 text-size-s text-weight-b optAniopt" v-if="item['title']">
|
||
{{ item.title }}
|
||
</view>
|
||
<view class="optAniopt">
|
||
<tm-pickers
|
||
:default-value.sync="item.value"
|
||
rang-key="title"
|
||
:btn-color="color"
|
||
:list="item.data"
|
||
>
|
||
<tm-input :value="pickTostring(item.value)" prefixp-icon="icon-calendaralt-fill" disabled :placeholder="item['placeholder']?item['placeholder']:'请选择'" suffix-icon="icon-sort-down"></tm-input>
|
||
</tm-pickers>
|
||
</view>
|
||
</block>
|
||
</block>
|
||
</block>
|
||
</view>
|
||
|
||
<view class="flex-between px-32 pt-32">
|
||
<tm-button :fllowTheme="fllowTheme" @click="getData" :theme="color" block style="width: 48%;" height="80">确认</tm-button>
|
||
<tm-button
|
||
:fllowTheme="fllowTheme"
|
||
@click="resetinit"
|
||
:black="black_tmeme"
|
||
block
|
||
:theme="color"
|
||
:font-color="color"
|
||
text
|
||
shadow="0"
|
||
style="width: 48%;"
|
||
height="80"
|
||
>
|
||
重置
|
||
</tm-button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view @click="activeIndex=-1" v-if="activeIndex>-1" class="fixed fulled" :style="{height:height_bg+'px',top:vtop+'px',width:barwidth,background:'rgba(0,0,0,0.33)',zIndex:100}">
|
||
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
/**
|
||
* 下拉选项
|
||
* @property {String} color = [] 默认:primary ,主题色下方选项子组件的主题色。
|
||
* @property {String} un-color = [] 默认:black ,默认未激活时。bar条上的文字颜色
|
||
* @property {String} active-color = [] 默认:primary ,默认激活时。bar条上的文字颜色
|
||
* @property {String} bg-color = [] 默认:white ,导航条背景主题色。
|
||
* @property {Number} shadow = [] 默认:10 ,导航条的投影。
|
||
* @property {Array} list = [] 默认:[] ,数据格式见文档
|
||
* @property {Number} maxHeight = [] 默认:650 ,弹出的标签页,最大内容高度,超过自动滚动。
|
||
* @property {Number} height = [] 默认:88 ,标签导航的高度
|
||
* @property {Number} font-size = [] 默认:28 ,标签导航的文字大小
|
||
* @property {Array} default-selected = [] 默认:[] ,默认赋值选中的选项,注意可以是id数组或者对象数组,对象数组情况下必须含id标签符,且是唯一的。
|
||
* @property {Boolean} black = [] 默认:false ,暗黑模式。
|
||
* @property {Function} change 切换选项页面时触发。
|
||
* @property {Function} confirm 点击确认按钮时触发,返回所有选中的项。
|
||
* @example <tm-dropDownMenu color="orange" :list="list"></tm-dropDownMenu>
|
||
*/
|
||
import tmRow from '@/tm-vuetify/components/tm-row/tm-row.vue';
|
||
import tmCol from '@/tm-vuetify/components/tm-col/tm-col.vue';
|
||
import tmButton from '@/tm-vuetify/components/tm-button/tm-button.vue';
|
||
import tmIcons from '@/tm-vuetify/components/tm-icons/tm-icons.vue';
|
||
import tmInput from '@/tm-vuetify/components/tm-input/tm-input.vue';
|
||
import tmGroupcheckbox from '@/tm-vuetify/components/tm-groupcheckbox/tm-groupcheckbox.vue';
|
||
import tmCheckbox from '@/tm-vuetify/components/tm-checkbox/tm-checkbox.vue';
|
||
|
||
import tmGroupradio from '@/tm-vuetify/components/tm-groupradio/tm-groupradio.vue';
|
||
import tmRadio from '@/tm-vuetify/components/tm-radio/tm-radio.vue';
|
||
import tmSlider from '@/tm-vuetify/components/tm-slider/tm-slider.vue';
|
||
import tmListitem from '@/tm-vuetify/components/tm-listitem/tm-listitem.vue';
|
||
import tmPickers from '@/tm-vuetify/components/tm-pickers/tm-pickers.vue';
|
||
|
||
export default {
|
||
components: {tmPickers, tmRow, tmCol, tmButton, tmIcons, tmInput, tmGroupcheckbox, tmCheckbox, tmGroupradio, tmRadio, tmSlider, tmListitem },
|
||
name: 'tm-dropDownMenu',
|
||
props: {
|
||
// 主题色下方选项子组件的主题色
|
||
color: {
|
||
type: String,
|
||
default: 'primary'
|
||
},
|
||
// 默认未激活时。bar条上的文字颜色
|
||
unColor: {
|
||
type: String,
|
||
default: 'black'
|
||
},
|
||
// 默认激活时。bar条上的文字颜色
|
||
activeColor: {
|
||
type: String,
|
||
default: 'primary'
|
||
},
|
||
// 背景颜色。
|
||
bgColor: {
|
||
type: String,
|
||
default: 'white'
|
||
},
|
||
list: {
|
||
type: Array,
|
||
default: () => {
|
||
return [];
|
||
}
|
||
},
|
||
maxHeight:{
|
||
type:Number|String,
|
||
default:650
|
||
},
|
||
height:{
|
||
type:Number|String,
|
||
default:88
|
||
},
|
||
fontSize:{
|
||
type:Number|String,
|
||
default:28
|
||
},
|
||
//菜单的投影。
|
||
shadow: {
|
||
type: Number | String,
|
||
default: 10
|
||
},
|
||
// 可以是id索引也可以是对象数组,可以混着来。
|
||
defaultSelected: {
|
||
type: Array,
|
||
default: () => {
|
||
return [];
|
||
}
|
||
},
|
||
black: {
|
||
type: Boolean | String,
|
||
default: null
|
||
},
|
||
// 跟随主题色的改变而改变。
|
||
fllowTheme: {
|
||
type: Boolean | String,
|
||
default: true
|
||
}
|
||
},
|
||
computed: {
|
||
itemLength: function() {
|
||
if (this.list.length == 0) return 100;
|
||
return 100 / this.list.length;
|
||
},
|
||
black_tmeme: function() {
|
||
if (this.black !== null) return this.black;
|
||
return this.$tm.vx.state().tmVuetify.black;
|
||
}
|
||
},
|
||
watch:{
|
||
list:{
|
||
deep:true,
|
||
handler(){
|
||
this.$nextTick(function() {
|
||
this.formartData = this.chulidata();
|
||
});
|
||
}
|
||
}
|
||
},
|
||
data() {
|
||
return {
|
||
activeIndex: -1,
|
||
formartData: [],
|
||
oldList: [],
|
||
test: [],
|
||
height_bg:0,
|
||
vtop:0,
|
||
maxLeng:40,//最大渲染级别
|
||
rendIdx:0,
|
||
barwidth:'100%'
|
||
};
|
||
},
|
||
created() {
|
||
this.height_bg = uni.getSystemInfoSync().screenHeight;
|
||
},
|
||
mounted() {
|
||
this.$nextTick(function() {
|
||
this.formartData = this.chulidata();
|
||
this.oldList = [...this.list]
|
||
let t = this;
|
||
uni.$tm.sleep(40).then(e=>{
|
||
uni.createSelectorQuery().in(this).select('.tm-dropDownMenu').boundingClientRect().exec(function(v){
|
||
// #ifdef H5
|
||
t.vtop = v[0].top+v[0].height+uni.getSystemInfoSync().windowTop;
|
||
// #endif
|
||
// #ifndef H5
|
||
t.vtop = v[0].top+v[0].height;
|
||
console.log(v[0]);
|
||
// #endif
|
||
t.barwidth = v[0].width+'px'
|
||
|
||
})
|
||
})
|
||
});
|
||
},
|
||
methods: {
|
||
pickTostring(item){
|
||
let p = [];
|
||
item.forEach(el=>{
|
||
if(typeof(el)=="string"){
|
||
p.push(el)
|
||
}else if(typeof el == 'object'){
|
||
p.push(el.title);
|
||
}
|
||
})
|
||
return p.join("-")
|
||
},
|
||
chiludis(item) {
|
||
return item?.disabled || false;
|
||
},
|
||
chulidata(list) {
|
||
// 处理相关数据格式以保持 一致。
|
||
let t = this;
|
||
let p = this.$tm.deepClone(list||this.list);
|
||
for (let j = 0; j < p.length; j++) {
|
||
p[j]['dot'] = 0;
|
||
if (p[j]['children']) {
|
||
let ic = p[j].children;
|
||
if (ic.length > 0) {
|
||
for (let k = 0; k < ic.length; k++) {
|
||
let children = ic[k]['children'];
|
||
if (children) {
|
||
if (ic[k]['model'] == 'checkbox'|| ic[k]['model'] == 'listCheckbox' || ic[k]['model'] == 'list' || (ic[k]['model'] == 'radio' && children.length > 0)) {
|
||
for (let z = 0; z < children.length; z++) {
|
||
let im = children[z];
|
||
if (!im.hasOwnProperty('checked')) {
|
||
im['checked'] = false;
|
||
}
|
||
for (let i = 0; i < t.defaultSelected.length; i++) {
|
||
let lsitem = t.defaultSelected[i];
|
||
if (typeof lsitem === 'object') {
|
||
if (lsitem['id'] == im['id']) {
|
||
im['checked'] = true;
|
||
}
|
||
} else {
|
||
if (lsitem == im['id']) {
|
||
im['checked'] = true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return p;
|
||
},
|
||
// 重置只重置当前打开的页面数量,并不重置其它页面数据。
|
||
resetinit(index) {
|
||
let pd = this.formartData[this.activeIndex];
|
||
if (pd['children']) {
|
||
let ic = pd.children;
|
||
if (ic.length > 0) {
|
||
for (let k = 0; k < ic.length; k++) {
|
||
let children = ic[k]['children'];
|
||
if (children) {
|
||
if (ic[k]['model'] == 'checkbox'||ic[k]['model'] == 'listCheckbox'||ic[k]['model'] == 'list' || (ic[k]['model'] == 'radio' && children.length > 0)) {
|
||
for (let z = 0; z < children.length; z++) {
|
||
let im = children[z];
|
||
im['checked'] = false;
|
||
}
|
||
}
|
||
} else {
|
||
if (ic[k]['model'] == 'slider') {
|
||
ic[k].value = 0;
|
||
} else if (ic[k]['model'] == 'input') {
|
||
ic[k].value = '';
|
||
} else if (ic[k]['model'] == 'pickers') {
|
||
ic[k].value = [];
|
||
} else if (ic[k]['model'] == 'pickersDate') {
|
||
ic[k].value = "";
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
const p = this.chulidata(this.oldList);
|
||
this.formartData.splice(this.activeIndex, 1, p[this.activeIndex]);
|
||
},
|
||
changeIndex(index) {
|
||
let t = this;
|
||
let itmod = 659;
|
||
clearInterval(itmod)
|
||
if (this.activeIndex === index) {
|
||
this.activeIndex = -1;
|
||
|
||
} else {
|
||
this.activeIndex = index;
|
||
|
||
}
|
||
this.$emit('change', this.activeIndex);
|
||
|
||
this.rendIdx = 0;
|
||
clearInterval(itmod)
|
||
itmod = setInterval(function(){
|
||
t.rendIdx+=1;
|
||
|
||
if(t.rendIdx>t.maxLeng||t.activeIndex==-1){
|
||
clearInterval(itmod)
|
||
}
|
||
},10)
|
||
},
|
||
// 获取选中以及填写的数据。
|
||
getData() {
|
||
let p = [...this.formartData];
|
||
let xz = [];
|
||
for (let i = 0; i < p.length; i++) {
|
||
if (p[i]['children']) {
|
||
for (let j = 0; j < p[i].children.length; j++) {
|
||
let ic = p[i].children[j];
|
||
let ps = [];
|
||
if (ic.model == 'checkbox' || ic.model == 'radio' || ic.model == 'listCheckbox' || ic.model == 'list') {
|
||
if (ic['children']) {
|
||
for (let k = 0; k < ic.children.length; k++) {
|
||
if (ic.children[k].checked === true) {
|
||
ps.push(ic.children[k]);
|
||
}
|
||
}
|
||
}
|
||
} else if (ic.model == 'input' || ic.model == 'slider') {
|
||
ps.push(ic);
|
||
} else if(ic.model == 'pickers'){
|
||
ps.push(ic);
|
||
}
|
||
|
||
let pyz = { ...ic };
|
||
delete pyz.children;
|
||
xz.push({
|
||
...pyz,
|
||
children: ps
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
this.$emit('confirm', xz);
|
||
this.activeIndex = -1;
|
||
}
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.tm-dropDownMenu {
|
||
position: relative;
|
||
.tm-dropDownMenu-bar {
|
||
position: relative;
|
||
z-index: 303;
|
||
}
|
||
.tm-dropDownMenu-body {
|
||
background-color: rgba(0, 0, 0, 0.35);
|
||
min-height: 150upx;
|
||
position: absolute;
|
||
z-index: 304;
|
||
width: 100%;
|
||
}
|
||
}
|
||
.optAniopt{
|
||
animation: opt 0.2s linear;
|
||
}
|
||
@keyframes opt{
|
||
0%{opacity: 0;}
|
||
100%{opacity: 1;}
|
||
}
|
||
</style>
|