You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

548 lines
17 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<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>