Commit 11aab1b9 authored by wlxuqu's avatar wlxuqu

新增下拉菜单组件

parent 65517751
File added
......@@ -2,14 +2,14 @@
"easycom": {
"^u-(.*)": "@/uview-ui/components/u-$1/u-$1.vue"
},
// "condition": { //模式配置,仅开发期间生效
// "current": 0, //当前激活的模式(list 的索引项)
// "list": [{
// "name": "test", //模式名称
// "path": "pages/componentsC/layout/index", //启动页面,必选
// "query": "id=1&name=2" //启动参数,在页面的onLoad函数里面得到
// }]
// },
"condition": { //模式配置,仅开发期间生效
"current": 0, //当前激活的模式(list 的索引项)
"list": [{
"name": "test", //模式名称
"path": "pages/componentsB/dropdown/index", //启动页面,必选
"query": "id=1&name=2" //启动参数,在页面的onLoad函数里面得到
}]
},
"pages": [
// 演示-组件
{
......@@ -663,6 +663,13 @@
{
"root": "pages/componentsB",
"pages": [
// dropdown-下拉菜单
{
"path": "dropdown/index",
"style": {
"navigationBarTitleText": "dropdown-下拉菜单"
}
},
// tabbar-底部导航栏
{
"path": "tabbar/index",
......
<template>
<view class="">
<view class="">
<view class="u-demo-title">演示效果</view>
<view class="u-demo-area u-flex u-row-center">
<u-dropdown ref="uDropdown">
<u-dropdown-item v-model="value1" title="距离" :options="options1"></u-dropdown-item>
<u-dropdown-item v-model="value2" title="温度" :options="options2"></u-dropdown-item>
<u-dropdown-item title="属性">
<view class="slot-content">
<view class="item-box">
<view class="item" :class="[item.active ? 'active' : '']" @tap="tagClick(index)" v-for="(item, index) in list" :key="index">
{{item.text}}
</view>
</view>
<u-button type="primary" @click="closeDropdown">确定</u-button>
</view>
</u-dropdown-item>
</u-dropdown>
</view>
</view>
<view class="u-config-wrap">
<view class="u-config-title u-border-bottom">
参数配置时
</view>
<!-- <view class="u-config-item">
<view class="u-item-title">颜色</view>
<u-subsection vibrateShort :list="['primary', 'success', 'warning', 'error', 'info']" @change="colorChange"></u-subsection>
</view>
<view class="u-config-item">
<view class="u-item-title">线条类型</view>
<u-subsection vibrateShort :list="['实线', '方形虚线', '圆点虚线']" @change="borderStyleChange"></u-subsection>
</view>
<view class="u-config-item">
<view class="u-item-title">细边</view>
<u-subsection vibrateShort :list="['是', '否']" @change="hairLineChange"></u-subsection>
</view>
<view class="u-config-item">
<view class="u-item-title">方向</view>
<u-subsection vibrateShort :list="['水平', '垂直']" @change="directionChange"></u-subsection>
</view> -->
</view>
</view>
</template>
<script>
export default {
data() {
return {
value1: '',
value2: '2',
value3: '',
options1: [{
text: '默认排序',
value: 1,
icon: 'map'
},
{
text: '距离优先',
value: 2,
icon: 'map'
},
{
text: '价格优先',
value: 3,
icon: 'map'
}
],
options2: [{
text: '去冰',
value: 1,
icon: 'map'
},
{
text: '加冰',
value: 2,
icon: 'map'
},
{
text: '正常温',
value: 3,
icon: 'map'
},
{
text: '加热',
value: 4,
icon: 'map'
},
{
text: '极寒风暴',
value: 5,
icon: 'map'
}
],
list: [{
text: '琪花瑶草',
active: true,
},
{
text: '清词丽句',
active: false,
},
{
text: '宛转蛾眉',
active: false,
},
{
text: '煦色韶光',
active: false,
},
{
text: '鱼沉雁落',
active: false,
},
{
text: '章台杨柳',
active: false,
},
{
text: '霞光万道',
active: false,
}
],
direction: 'row',
hairLine: true,
length: '100%',
color: this.$u.color['primary'],
borderStyle: 'solid'
}
},
methods: {
colorChange(index) {
this.color = this.$u.color[['primary', 'success', 'warning', 'error', 'info'][index]];
},
hairLineChange(index) {
this.hairLine = !index;
},
directionChange(index) {
this.direction = index == 0 ? 'row' : 'col';
if (index == 0) this.length = '100%';
else this.length = '50rpx';
},
borderStyleChange(index) {
this.borderStyle = ['solid', 'dashed', 'dotted'][index];
},
tagClick(index) {
this.list[index].active = !this.list[index].active;
},
closeDropdown() {
this.$refs.uDropdown.close();
}
}
}
</script>
<style scoped lang="scss">
.u-config-wrap {
padding: 40rpx;
}
.slot-content {
background-color: #FFFFFF;
padding: 24rpx;
.item-box {
margin-bottom: 50rpx;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
.item {
border: 1px solid $u-type-primary;
color: $u-type-primary;
padding: 8rpx 40rpx;
border-radius: 100rpx;
margin-top: 30rpx;
}
.active {
color: #FFFFFF;
background-color: $u-type-primary;
}
}
}
</style>
<template>
<view style="margin-top: 200rpx;">
<view class="">
<u-modal v-model="show" content="点击确定进行授权">
<button open-type="getUserInfo" class="u-reset-button" slot="confirm-button" @getuserinfo="getuserinfo">确定</button>
</u-modal>
<u-button @click="show = true">打开modal</u-button>
</view>
</template>
......@@ -7,17 +11,13 @@
export default {
data() {
return {
// 错误示例,切换语言时,这个intro并不会自动更新到视图
// intro: this.$t('lang.intro')
show: true
}
},
computed: {
// 正确用法
},
onShow() {
},
methods: {
getuserinfo(res) {
console.log(res);
}
}
}
</script>
\ No newline at end of file
</script>
......@@ -266,6 +266,11 @@ export default [{
groupName: '导航组件',
groupName_en: 'Navigation components',
list: [{
path: '/pages/componentsB/dropdown/index',
icon: 'dropdown',
title: 'Dropdown 下拉菜单',
title_en: 'Dropdown',
},{
path: '/pages/componentsB/tabbar/index',
icon: 'tabbar',
title: 'Tabbar 底部导航栏',
......
......@@ -224,7 +224,7 @@ export default {
},
// 卡片外围阴影,字符串形式
boxShadow: {
type: Boolean,
type: String,
default: 'none'
}
},
......
<template>
<view class="u-dropdown-item" v-if="active" @touchmove.stop.prevent @tap.stop.prevent>
<view class="u-dropdown-item__options" v-if="!$slots.default">
<u-cell-group>
<u-cell-item @click="cellClick(item.value)" :arrow="false" :title="item.text" v-for="(item, index) in options" :key="index" :title-style="{
color: value == item.value ? activeColor : inactiveColor
}">
<u-icon v-if="value == item.value" name="checkbox-mark" :color="activeColor" size="32"></u-icon>
</u-cell-item>
</u-cell-group>
</view>
<slot v-else />
</view>
</template>
<script>
export default {
name: 'u-dropdown-item',
props: {
// 当前选中项的value值
value: {
type: [Number, String, Array],
default: ''
},
// 菜单项标题
title: {
type: String,
default: ''
},
// 选项数据,如果传入了默认slot,此参数无效
options: {
type: Array,
default () {
return []
}
},
// 是否禁用此菜单项
disabled: {
type: Boolean,
default: false
},
// 标题自定义样式,对象形式
titleStyle: {
type: Object,
default () {
return {}
}
},
},
data() {
return {
active: false, // 当前项是否处于展开状态
activeColor: '#2979ff', // 激活时左边文字和右边对勾图标的颜色
inactiveColor: '#606266', // 未激活时左边文字和右边对勾图标的颜色
}
},
created() {
// 父组件的实例
this.parent = false;
},
methods: {
// cell被点击
cellClick(value) {
// 修改通过v-model绑定的值
this.$emit('input', value);
// 通知父组件(u-dropdown)收起菜单
this.parent.close();
// 发出事件,抛出当前勾选项的value
this.$emit('change', value);
}
},
mounted() {
// 获取父组件u-dropdown
let parent = this.$u.$parent.call(this, 'u-dropdown');
if(parent) {
this.parent = parent;
// 将子组件的激活颜色配置为父组件设置的激活和未激活时的颜色
this.activeColor = parent.activeColor;
this.inactiveColor = parent.inactiveColor;
// 将本组件的this,放入到父组件的children数组中,让父组件可以操作本(子)组件的方法和属性
parent.children.push(this);
if(parent.children.length == 1) this.active = true;
// 父组件无法监听children的变化,故将子组件的title,传入父组件的menuList数组中
parent.menuList.push(this.title);
}
}
}
</script>
<style scoped lang="scss">
@import "../../libs/css/style.components.scss";
</style>
<template>
<view class="dropdown-list-wapper u-flex u-flex-1">
<view
v-for="(drop, index) in dropdownListFromFather"
:key="drop.name"
:show="drop.show"
class="u-selected-class u-dropdown-list"
:style="{ zIndex: zIndex + 1 }"
>
<slot name="selectionbox">
<view
:style="{ height: top + 'rpx' }"
class="drop-item u-flex u-justify-center"
@click="handleDropClick(drop)"
>
<text :style="{ color: drop.show ? activeColor : '#999' }">
{{ getTitle(drop.options) }}
</text>
<view
class="u-animation"
:class="[drop.show ? 'u-animation-show' : '']"
>
<u-icon
v-if="drop.show"
name="arrow-up-fill"
:size="18"
:color="activeColor"
></u-icon>
<u-icon v-else name="arrow-down-fill" :size="18"></u-icon>
</view>
</view>
</slot>
<view
class="u-dropdown-view"
:class="[drop.show ? 'u-dropdownlist-show' : '']"
:style="{
background: bgcolor,
height: drop.show ? 'auto' : 0,
top: top + 'rpx'
}"
>
<slot name="dropdownbox">
<view class="u-selected-list">
<view
class="select-item u-flex u-align-center u-border-bottom u-align-between"
:style="{ color: select.select ? activeColor : '#666666' }"
@tap="handleSelected(select, drop.options)"
v-for="(select, n) in drop.options"
:key="n"
>
<text>{{ select.text }}</text>
<u-icon
v-if="select.select"
class="select-icon"
:color="activeColor"
size="35"
name="checkmark"
></u-icon>
</view>
</view>
</slot>
</view>
</view>
<u-mask
duration="100"
:show="dropdownShow"
@click="closeMask"
:z-index="zIndex"
></u-mask>
</view>
</template>
<script>
const dropdownOption1 = [
{ id: 0, text: '类型', value: '', select: false },
{ id: 1, text: '全场券', value: 1, select: false },
{ id: 2, text: '品类券', value: 2, select: false },
{ id: 3, text: '单品券', value: 3, select: false },
{ id: 4, text: '业务券', value: 4, select: false }
]
const dropdownOption2 = [
{ id: 5, text: '状态', value: '', select: false },
{ id: 6, text: '可使用', value: 1, select: false },
{ id: 7, text: '已过期', value: 2, select: false }
]
const dropdownOption3 = [
{ id: 8, text: '优惠力度', value: '', select: false },
{ id: 9, text: '满100-20', value: 1, select: false },
{ id: 10, text: '满100-50', value: 2, select: false }
]
export default {
props: {
// 下拉框数据
dropdownList: {
type: Array,
default: () => [
{ show: false, options: dropdownOption1 },
{ show: false, options: dropdownOption2 },
{ show: false, options: dropdownOption3 }
],
required: true,
validator: value =>
value.every(item => Array.isArray(item.options) && item.options.length)
},
//背景颜色
bgcolor: {
type: String,
default: 'none'
},
//rpx 选择框高度也用这个值
top: {
type: Number,
default: 90
},
// 菜单标题和选项的选中态颜色
activeColor: {
type: String,
default: '#e7141a'
},
// mask和下拉列表的z-index
zIndex: {
type: [String, Number],
default: 21
}
},
data() {
return {
dropdownShow: false,
dropdownListFromFather: this.dropdownList
}
},
computed: {},
methods: {
getTitle(item = []) {
const obj = item.find(v => v.select) || {}
if (obj.select) {
return obj.text
} else {
if (item[0]) {
item[0].select = true
return item[0].text
}
}
return ''
},
handleDropClick(item) {
if (item.show) {
item.show = false
this.dropdownShow = false
return
}
this.dropdownListFromFather.map(item => {
item.show = false
})
const t = setTimeout(() => {
item.show = true
this.dropdownShow = true
clearTimeout(t)
}, 100)
},
closeMask() {
this.dropdownShow = false
this.dropdownListFromFather.map(item => {
item.show = false
})
},
handleSelected(select, options) {
options.map(item => {
item.select = false
})
select.select = true
this.closeMask()
// 返回选中对象和下拉列表数组
this.$emit('change', select, options)
}
},
watch: {
dropdownList: {
handler(v) {
this.dropdownListFromFather = v
},
deep: true
}
}
}
</script>
<style lang="scss" scoped>
@import "../../libs/css/style.components.scss";
.dropdown-list-wapper {
position: relative;
}
.u-dropdown-view {
width: 100%;
overflow: hidden;
position: absolute;
z-index: 9999;
left: 0;
right: 0;
/* opacity: 0; */
visibility: hidden;
transition: height 0.5s ease-in-out;
.u-selected-list {
background-color: #fff;
.select-item {
color: #666666;
font-size: 28rpx;
padding: 30rpx 54rpx 30rpx 30rpx;
margin-left: 30rpx;
}
.select-item.selectActive {
color: #e7141a;
}
}
}
.u-dropdownlist-show {
/* opacity: 1; */
visibility: visible;
}
.u-dropdown-list {
flex: 1;
// z-index: 22;
background: #fff;
position: static;
}
.drop-item {
justify-content: center;
color: #999999;
font-size: 30rpx;
> text {
margin-right: 10rpx;
}
/deep/ {
.uicon {
position: relative;
top: -2rpx;
}
}
}
</style>
<template>
<view class="u-dropdown" @touchmove.stop.prevent>
<view class="u-dropdown__menu">
<view class="u-dropdown__menu__item" v-for="(item, index) in menuList" :key="index" @tap.stop="menuClick(index)">
<view class="u-flex">
<text class="u-dropdown__menu__item__text" :style="{
color: index === current ? activeColor : '#606266'
}">{{item}}</text>
<view class="u-dropdown__menu__item__arrow">
<u-icon :name="index === current ? 'arrow-up' : 'arrow-down'" size="26" :color="index === current ? activeColor : '#c0c4cc'"></u-icon>
</view>
</view>
</view>
</view>
<view class="u-dropdown__content" :style="[contentStyle, {
transition: `all ${duration / 1000}s linear`
}]" @tap="menuClick(current)">
<view @tap.stop.prevent class="u-dropdown__content__popup" :style="[popupStyle]">
<slot></slot>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'u-dropdown',
props: {
// 菜单标题和选项的激活态颜色
activeColor: {
type: String,
default: '#2979ff'
},
// 菜单标题和选项的未激活态颜色
inactiveColor: {
type: String,
default: '#606266'
},
// 菜单展开方向,bottom-向上展开,top-向下展开
direction: {
type: String,
default: 'top'
},
// 是否展示遮罩
mask: {
type: Boolean,
default: false
},
// 点击遮罩是否关闭菜单
closeOnClickMask: {
type: Boolean,
default: true
},
// 点击当前激活项标题是否关闭菜单
closeOnClickSelf: {
type: Boolean,
default: true
},
// 过渡时间
duration: {
type: [Number, String],
default: 300
}
},
data() {
return {
showDropdown: true, // 是否打开下来菜单,
menuList: [], // 显示的菜单
active: false, // 下拉菜单的状态
current: "", // 当前是第几个菜单处于激活状态
// 外层内容的样式,初始时处于底层,且透明
contentStyle: {
zIndex: -1,
opacity: 0
}
}
},
computed: {
// 下拉出来部分的样式
popupStyle() {
let style = {};
// 进行Y轴位移,展开状态时,恢复原位。收齐状态时,往上位移100%,进行隐藏
style.transform = `translateY(${this.active ? 0 : '-100%'})`
return style;
}
},
created() {
// 引用所有子组件(u-dropdown-item)的this,不能在data中声明变量,否则在微信小程序会造成循环引用而报错
this.children = [];
},
methods: {
// 点击菜单
menuClick(index) {
// 如果点击时的索引和当前激活项索引相同,意味着点击了激活项,需要收起下拉菜单
if (index === this.current) {
this.close();
// 等动画结束后,再移除下拉菜单中的内容,否则直接移除,也就没有下拉菜单收起的效果了
setTimeout(() => {
this.children[index].active = false;
}, this.duration)
return;
}
// 展开时,设置下拉内容的样式
this.contentStyle = {
zIndex: 11,
opacity: 1
}
// 标记展开状态以及当前展开项的索引
this.active = true;
this.current = index;
// 历遍所有的子元素,将索引匹配的项标记为激活状态,因为子元素是通过v-if控制切换的
// 之所以不是因display: none,是因为nvue没有display这个属性
this.children.map((val, idx) => {
val.active = index == idx ? true : false;
})
},
// 设置下拉菜单处于收起状态
close() {
// 设置为收起状态,同时current归位,设置为空字符串
this.active = false;
this.current = "";
// 下拉内容的样式进行调整,不透明度设置为0
this.contentStyle = {
zIndex: -1,
opacity: 0
}
}
}
}
</script>
<style scoped lang="scss">
@import "../../libs/css/style.components.scss";
.u-dropdown {
flex: 1;
&__menu {
display: flex;
position: relative;
z-index: 11;
height: 80rpx;
&__item {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
&__text {
font-size: 26rpx;
color: $u-content-color;
}
&__arrow {
margin-left: 6rpx;
transition: transform .3s;
align-items: center;
}
}
}
&__content {
position: absolute;
z-index: 11;
height: 100%;
width: 100%;
left: 0px;
overflow: hidden;
background: rgba(0, 0, 0, .3);
opacity: 0;
&__popup {
z-index: 11;
transition: all 0.3s;
transform: translate3D(0, -100%, 0);
}
}
}
</style>
......@@ -84,7 +84,8 @@
else style.zIndex = -1;
style.transition = `all ${this.duration / 1000}s ease-in-out`;
// 判断用户传递的对象是否为空,不为空就进行合并
if (Object.keys(this.customStyle).length) style = { ...style,
if (Object.keys(this.customStyle).length) style = {
...style,
...this.customStyle
};
return style;
......
......@@ -164,6 +164,7 @@ uni-toast .uni-toast {
padding: 0;
font-size: inherit;
line-height: inherit;
background-color: transparent;
}
.u-reset-button::after {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment