首页 分类列表

This commit is contained in:
魏啾
2024-04-09 09:26:47 +08:00
parent d844832aad
commit 289b808bf6
11 changed files with 1032 additions and 14 deletions

View File

@@ -14,4 +14,7 @@ export default {
home(data) { //首页上半部分
return uni.api.post("/home", data);
},
locationdistrict(data) { //获取行政区域(区,街道)
return uni.api.get("/location/district", data);
},
}

View File

@@ -3,7 +3,9 @@ const debug = process.env.NODE_ENV == 'development' ? true : false;
const proxyApi = "/api"
// #endif
// #ifdef MP-WEIXIN || APP
const proxyApi = 'http://101.37.12.135:9889/cashierService' // 线上
// const proxyApi = 'http://192.168.2.133:9889/cashierService' // 王伟
// const proxyApi = 'http://101.37.12.135:9889/cashierService' // 帆哥
const proxyApi = 'https://wxcashiertest.sxczgkj.cn/cashierService'
// #endif
// #ifdef H5

View File

@@ -15,7 +15,7 @@
<view class="therecontent_box_itembox">
<view class="therecontent_box_itembox_item flex-between" v-for="(item,index) in todayList.todayList"
:key="item">
<image src="@/static/avatar.png" mode="aspectFill"></image>
<image :src="item.image" mode="aspectFill"></image>
<view class="therecontent_box_itembox_itemview">
<view class="therecontent_box_itembox_itemviewone">
{{item.productName}}
@@ -50,7 +50,7 @@
</view>
<view class="therecontent_box_itembox">
<view class="therecontent_box_itembox_item flex-between" v-for="(item,index) in salesList.hotList" :key="item">
<image src="@/static/avatar.png" mode="aspectFill"></image>
<image :src="item.image" mode="aspectFill"></image>
<view class="therecontent_box_itembox_itemview">
<view class="therecontent_box_itembox_itemviewone flex-start">
<view class="therecontent_box_itembox_itemviewoneafter">

View File

@@ -49,10 +49,12 @@
<!-- 今日上线 -->
<todaylist :todayList='hometoplist.todayList' :salesList='hometoplist.salesList'></todaylist>
<!-- 类目 -->
<view class="fourcontent flex-between" @click="viewHistory">
<view class="fourcontent_item flex-start" v-for="(item,index) in hometoplist.menu" :key="index">
<view class="fourcontent flex-between">
<view class="fourcontent_item flex-start" v-for="(item,index) in hometoplist.menu" :key="index"
@click="viewHistory(item)">
<text>{{item.name}}</text>
<u-icon style="margin-left: 8rpx;" name="arrow-down-fill" color="#333333" size="16"></u-icon>
<u-icon v-if="item.isChild" style="margin-left: 8rpx;" name="arrow-down-fill" color="#333333"
size="16"></u-icon>
</view>
</view>
@@ -172,10 +174,35 @@
<!-- 吸顶 -->
<view class="fourcontent flex-between" style="padding: 28rpx 0 28rpx 28rpx">
<view class="fourcontent_item flex-start" v-for="(item,index) in hometoplist.menu"
:key="index">
:key="index" @click="viewHistory(item)">
<text>{{item.name}}</text>
<u-icon style="margin-left: 8rpx;" name="arrow-down-fill" color="#333333"
size="16"></u-icon>
<u-icon v-if="item.isChild" style="margin-left: 8rpx;" name="arrow-down-fill"
color="#333333" size="16"></u-icon>
</view>
</view>
<!-- 显示下拉字段 -->
<view class="sixcontent" v-if="clickhometoplistmenulist.detail">
<view class="sixcontentitemP flex-colum-start">
<view class="sixcontentitemP_item"
:class="index == clickdetailindex ?'sixcontentitemP_itemactive':''"
v-for="(item,index) in clickhometoplistmenulist.detail" :key="index"
@click="clickdetail(item,index)">
{{item.label}}
</view>
</view>
</view>
<view class="sixcontent" v-if="clickhometoplistmenulist.districts">
<view class="sixcontentitemP flex-start">
<view class="sixcontentitemP_itembox flex-colum-start">
<view class="sixcontentitemP_itemone"
:class="index == clickdetailindex ?'sixcontentitemP_itemactive':''"
v-for="(item,index) in clickhometoplistmenulist.districts" :key="index"
@click="clickdetail(item,index)">
{{item.name}}
</view>
</view>
</view>
</view>
</view>
@@ -205,12 +232,14 @@
keyword: '',
current: 0,
opacity: false,
showproductlist: false, //弹成
showproductlist: true, //弹成
clickdetailindex: 0, //默认下拉选项第一个
hometoplist: { //上面数据
carousel: [],
district: []
},
homelist: [], //下面数据
clickhometoplistmenulist: {}, //下拉点击的数据
form: {
address: '', //地址
type: '', //品类
@@ -219,7 +248,6 @@
page: 1, //页数
size: 10, //页容量
status: 'loadmore'
}
};
},
@@ -242,10 +270,14 @@
return this.$store.getters.is_BarHeight
},
},
onLoad() {
async onLoad() {
setTimeout(() => {
this.GetTop()
}, 1000)
let res = await this.apix.locationdistrict({
keywords: '西安市'
})
this.clickhometoplistmenulist = res.data[0].districts
},
onReachBottom() {
this.onLoadhome()
@@ -315,12 +347,30 @@
//TODO handle the exception
}
},
async viewHistory() {
this.showproductlist = true
// 点击元素到指定位置
async viewHistory(item) {
if (item.dictName == 'allCity') {
let res = await this.apix.locationdistrict({
keywords: '西安市'
})
this.clickhometoplistmenulist = res.data[0]
} else {
this.clickhometoplistmenulist = item
}
uni.pageScrollTo({
scrollTop: this.Topdistance + 1,
duration: 300
});
if (item.isChild) {
this.showproductlist = true
} else {
}
},
// 选择元素的指定选项
clickdetail(item, index) {
this.clickdetailindex = index
this.showproductlist = false
},
}
@@ -466,6 +516,48 @@
}
}
.sixcontent {
.sixcontentitemP {
padding: 0 32rpx 28rpx 32rpx;
max-height: 600rpx;
overflow: auto;
.sixcontentitemP_itembox {
.sixcontentitemP_itemone {
flex: 1;
margin-top: 16rpx;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
font-size: 24rpx;
color: #666666;
}
.sixcontentitemP_itemone:nth-child(1) {
margin-top: 0rpx;
}
}
.sixcontentitemP_item:nth-child(1) {
margin-top: 0rpx;
}
.sixcontentitemP_item {
margin-top: 16rpx;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
font-size: 24rpx;
color: #666666;
}
.sixcontentitemP_itemactive {
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: bold;
font-size: 24rpx;
color: #666666;
}
}
}
.fivecontent {
padding: 0 28rpx;

View File

@@ -0,0 +1,13 @@
## 1.0.42023-06-04
1.更新层级点击不了问题;
2.增加吸顶属性配置;
3.cell类型选中建议增加对号图标
4.完善文档;
## 1.0.32023-04-15
更新选项增加箭头交互
## 1.0.22023-04-14
更新示例
## 1.0.12023-04-09
更新版本标签
## 1.0.02023-04-09
第一个版本

View File

@@ -0,0 +1,112 @@
<template>
<view class="picker-view">
<scroll-view
v-for="(num,index) in colNum"
:key="index"
:style="{
width:`${1/colNum*100}%`,
height:'100%',
backgroundColor:index===0?'#F6F6F6':'#FFFFFF'
}"
scroll-y>
<view
:class="['picker-view-item',modelValue[index]==item.value&&'picker-view-item-active']"
v-for="(item,indexs) in colList[index]"
:key="indexs"
@click="onSelect(index,item,indexs)"
>
{{item.label}}
</view>
</scroll-view>
</view>
</template>
<script setup>
import {
ref,
computed
} from "vue";
const props = defineProps({
// 双向绑定
modelValue: {
type: Array,
default: []
},
// 展示的列数
colNum: {
type: Number,
default: 1
},
// options 数据
options: {
type: Array,
default: []
},
// 自定义节点 label、value、children 的字段
fieldNames: {
type: Object,
default: ()=>{
return {
label: 'label',
value: 'value',
children: 'children',
}
}
},
})
const emits = defineEmits(["update:modelValue"])
const colList = computed(() => {
const arr = [];
let option = props.options;
for(let i=0;i<props.colNum;i++){
if ((props.modelValue[i] != undefined&&option)||(props.modelValue[i-1]&&option)||(i===0&&option)) {
arr.push(funOptions(option))
const index = option?.findIndex(item=>item[props.fieldNames.value]==props.modelValue[i])
option = (option[index]&&option[index]?.[props.fieldNames.children])||[];
}
}
return arr;
})
const funOptions = (data) => {
return data.map((item) => {
return {
label: item[props.fieldNames.label],
value: item[props.fieldNames.value],
children:item[props.fieldNames.children]||[]
}
})
}
const onSelect = (index,item,indexs) => {
const modelValue = JSON.parse(JSON.stringify(props.modelValue))
if(modelValue[index]!=item.value){
modelValue[index] = item.value;
let option = item[props.fieldNames.children]||[]
for(let i=index+1;i<props.colNum;i++){
if(option[0]){
modelValue[i] = option[0][props.fieldNames.value]
option = option[0]?.[props.fieldNames.children]||[]
}else if(modelValue[i]){
modelValue.splice(i,modelValue.length)
}
}
}
emits("update:modelValue",modelValue)
}
</script>
<style lang="scss" scoped>
.picker-view {
width: 100%;
height: 600rpx;
display: flex;
.picker-view-item{
padding: 30rpx 0;
text-align: center;
font-size:24rpx;
color:#333333;
}
.picker-view-item-active{
color:var(--dropdownThemeColor);
}
}
</style>

View File

@@ -0,0 +1,603 @@
<template>
<view
:class="['le-dropdown',isCeiling&&'le-dropdown-ceiling']"
:style="`--dropdownThemeColor:${themeColor};--dropdownThemeColorRgb:${hexToRgb(themeColor)}`">
<!-- 导航 -->
<view class="le-dropdown-menu">
<view class="le-dropdown-menu-item" v-for="(item, index) in menuList" :key="index"
@tap.stop="menuClick(index)">
<view class="le-flex">
<text class="le-dropdown-menu-item-text" :style="{
color: index === current ? themeColor : inactiveColor
}">{{item.title}}</text>
<view v-if="item.type==='sort'"
:class="['le-dropdown-menu-item-arrow',item.value==='asc'&&'le-dropdown-menu-item-arrow_top',item.value==='desc'&&'le-dropdown-menu-item-arrow_bottom']">
</view>
<view v-if="item.type!=='sort'"
:class="['le-dropdown-menu-item-basicarrow',index === current&&'le-dropdown-menu-item-basicarrow_rotate']">
</view>
</view>
</view>
</view>
<!-- 展示的内容 -->
<view class="le-dropdown-content" :style="[
{
transition: `opacity ${duration / 1000}s linear`,
top:'auto',
bottom:`calc(100vh - ${contentHeight + windowTop}px)`,
height:`calc(100vh - ${contentHeight + windowTop}px)`
},
contentStyle
]" @tap="close">
<view ref="leDropdownContentPopupRef" class="le-dropdown-content-popup" :style="[popupStyle]"
@tap.stop.prevent>
<block v-for="(item,index) in menuList" :key="index">
<!-- 单选列表 -->
<view class="le-dropdown-popup-content le-dropdown-cell"
v-if="item.type==='cell'&&index === current">
<view :class="['le-dropdown-cell-item',item.value===sItem.value&&'le-dropdown-cell-active']"
v-for="(sItem,sIndex) in item.options" :key="sIndex" @click="onSelectCell(sItem,index)">
<view class="le-dropdown-cell-active-text">{{sItem.label}}</view>
<view v-show="item.value===sItem.value" class="le-dropdown-cell-active-icon"></view>
</view>
</view>
<!-- 级联动选择 -->
<view class="le-dropdown-popup-content le-dropdown-picker"
v-if="item.type==='picker'&&index === current">
<le-dropdown-picker v-model="item.value" v-bind="item.componentProps">
</le-dropdown-picker>
<view class="le-dropdown-footer">
<view class="le-dropdown-confirm" @click="onFilterConfirm(item,index)">
{{item.confirmText||'确定'}}
</view>
</view>
</view>
<!-- 筛选 -->
<view class="le-dropdown-popup-content le-dropdown-filter"
v-if="item.type==='filter'&&index === current">
<view class="le-dropdown-filter-item" v-for="(sItem,sIndex) in item.children" :key="sIndex">
<view class="le-dropdown-filter-title">
{{sItem.title}}
<text class="le-dropdown-filter-subtitle"
v-if="sItem.type==='slider'">{{sItem.value}}{{sItem.suffix}}</text>
</view>
<view class="le-dropdown-filter-content">
<!-- 单选类型 -->
<block v-if="sItem.type==='radio'">
<view v-for="(ssItem,ssIndex) in sItem.options"
:class="['le-dropdown-filter-box',sItem.value===ssItem.value&&'le-dropdown-filter-box-active']"
:key="ssIndex" @click="onRadioFilter(sIndex,ssItem,index)">
{{ssItem.label}}
</view>
</block>
<!-- 多选类型 -->
<block v-else-if="sItem.type==='checkbox'">
<view v-for="(ssItem,ssIndex) in sItem.options"
:class="['le-dropdown-filter-box',sItem.value.includes(ssItem.value)&&'le-dropdown-filter-box-active']"
:key="ssIndex" @click="onCheckboxFilter(sIndex,ssItem,index)">
{{ssItem.label}}
</view>
</block>
<!-- 滑块类型 -->
<block v-else-if="sItem.type==='slider'">
<slider style="width: 100%;" :activeColor="themeColor" :value="sItem.value"
:min="sItem.componentProps.min||0" :max="sItem.componentProps.max||100"
:step="sItem.componentProps.step||1"
:show-value="sItem.componentProps['show-value']||true"
@change="onSliderChange($event,sIndex,ssItem,index)" />
</block>
</view>
</view>
<view class="le-dropdown-footer">
<view class="le-dropdown-reset" @click="onFilterReset(item,index)">重置</view>
<view class="le-dropdown-confirm" @click="onFilterConfirm(item,index)">
{{item.confirmText||'确定'}}
</view>
</view>
</view>
</block>
</view>
<view class="le-dropdown-content-mask" @click="close"></view>
</view>
</view>
</template>
<script setup>
/**
* menuList 导航数据
* @property {String} title 导航名称
* @property {String} type =[cell|picker|sort|click|filter] 导航点击展示的类型
* @value cell 下拉选择(单选)
* @value picker 级联动
* @value sort 排序
* @value click 点击
* @value filter 复杂筛选
* @property {String} value 导航当前选中的值
* @property {Array} options 导航展示可选的数据值
* */
import {
ref,
computed,
getCurrentInstance,
onMounted
} from "vue";
import cloneDeep from './utils/cloneDeep.js';
import hexToRgb from './utils/hexToRgb.js';
import LeDropdownPicker from "./components/le-picker.vue";
const props = defineProps({
// 导航数据
menuList: {
type: Array,
default: () => {
return [
]
}
},
// 主题的颜色
themeColor: {
type: String,
default: "#3185FF"
},
// 没选中的颜色
inactiveColor: {
type: String,
default: "#333333"
},
// 过渡时间
duration: {
type: [Number, String],
default: 300
},
// 是否吸顶
isCeiling: {
type: Boolean,
default: false
},
})
const emits = defineEmits(["open", "close", "update:menuList", "onConfirm", "onChange"])
// 初始化数据用于重置使用
const initMenuList = cloneDeep(props.menuList)
const instance = getCurrentInstance(); // 获取组件实例
// 下拉出来部分的样式
const popupStyle = computed(() => {
let style = {};
// 进行Y轴位移展开状态时恢复原位。收齐状态时往上位移100%,进行隐藏
style.transform = `translateY(${active.value ? 0 : '-100%'})`
style['transition-duration'] = props.duration / 1000 + 's';
return style;
})
// 当前是第几个菜单处于激活状态
const current = ref(99999);
// 外层内容的样式,初始时处于底层,且透明
const contentStyle = ref({
zIndex: -1,
opacity: 0
})
const active = ref(false) // 下拉菜单的状态
const contentHeight = ref(0)
const leDropdownContentPopupRef = ref(null)
const windowTop = ref(0)
uni.getSystemInfo({
success(e) {
windowTop.value = e.windowTop;
}
})
// 点击菜单
const menuClick = (index) => {
getContentHeight()
switch (props.menuList[index].type) {
case 'sort':
onSort(index);
break;
case 'click':
onClick(index);
break;
default:
// 如果点击时的索引和当前激活项索引相同,意味着点击了激活项,需要收起下拉菜单
if (index === current.value) {
close();
return;
}
open(index);
break;
}
}
// 打开当前筛选窗
const open = (index) => {
// 展开时,设置下拉内容的样式
active.value = true;
contentStyle.value.zIndex = 11;
contentStyle.value.opacity = 1;
contentStyle.value.bottom = `0px`;
contentStyle.value.top = `80rpx`;
// 标记展开状态以及当前展开项的索引
current.value = index;
emits("open", current.value)
}
// 关闭当前筛选窗
const close = () => {
active.value = false;
contentStyle.value.opacity = 0;
// 等动画结束后,再移除下拉菜单中的内容,否则直接移除,也就没有下拉菜单收起的效果了
setTimeout(() => {
contentStyle.value.zIndex = -1;
current.value = 99999;
contentStyle.value.bottom = `calc(100vh - ${contentHeight.value + windowTop.value}px)`
contentStyle.value.top = 'auto';
}, props.duration)
emits("close", current.value)
}
// 获取下拉菜单内容的高度
const getContentHeight = () => {
uni.createSelectorQuery()
.in(instance)
.selectAll('.le-dropdown-menu')
.boundingClientRect()
.exec(data => {
contentHeight.value = data[0][0].bottom
});
}
// 点击排序
const onSort = (index) => {
const type = current.value === 99999 ? current.value : props.menuList[current.value].type
switch (type) {
case 'sort':
case 'click':
case 99999:
start()
break;
default:
close();
setTimeout(() => {
start()
}, props.duration)
break;
}
function start() {
const menuList = cloneDeep(props.menuList);
current.value = index;
menuList[index].value = !menuList[index].value ? 'asc' : menuList[index].value == 'asc' ? 'desc' :
null;
emits("update:menuList", menuList)
emits("onConfirm", menuList[index])
}
}
// 点击按钮
const onClick = (index) => {
const type = current.value === 99999 ? current.value : props.menuList[current.value].type
switch (type) {
case 'sort':
case 'click':
case 99999:
start()
break;
default:
close();
setTimeout(() => {
start()
}, props.duration)
break;
}
function start() {
const menuList = cloneDeep(props.menuList);
current.value = index;
emits("onConfirm", menuList[index])
}
}
// 单选列表选中事件
const onSelectCell = (sItem, index) => {
const menuList = cloneDeep(props.menuList);
menuList[index].title = sItem.label;
menuList[index].value = sItem.value;
emits("update:menuList", menuList)
close();
emits("onConfirm", menuList[index])
}
// 筛选单选选中事件
const onRadioFilter = (sIndex, ssItem, index) => {
const menuList = cloneDeep(props.menuList);
menuList[index].children[sIndex].value = ssItem.value;
emits("update:menuList", menuList)
emits("onChange", menuList[index], sIndex)
}
// 筛选多选选中事件
const onCheckboxFilter = (sIndex, ssItem, index) => {
const menuList = cloneDeep(props.menuList);
const indexs = menuList[index].children[sIndex].value.indexOf(ssItem.value)
if (indexs != -1) {
menuList[index].children[sIndex].value.splice(indexs, 1)
} else {
menuList[index].children[sIndex].value.push(ssItem.value)
}
emits("update:menuList", menuList)
emits("onChange", menuList[index], sIndex)
}
// 滑块值的变化事件
const onSliderChange = (event, sIndex, ssItem, index) => {
const menuList = cloneDeep(props.menuList);
menuList[index].children[sIndex].value = event.detail.value;
emits("update:menuList", menuList)
emits("onChange", menuList[index], sIndex)
}
// 筛选选中事件
const onSelectFilter = (sIndex, ssItem, index) => {
const menuList = cloneDeep(props.menuList);
menuList[index].active[sIndex] = ssItem.value;
emits("update:menuList", menuList)
emits("onChange", menuList[index], sIndex)
}
// 重置筛选
const onFilterReset = (item, index) => {
const menuList = cloneDeep(props.menuList);
menuList[index].children.forEach((items, indexs) => {
items.value = initMenuList[index].children[indexs].value
})
emits("update:menuList", menuList)
// close();
// emits("onConfirm",menuList[index])
}
// 确定筛选
const onFilterConfirm = (item, index) => {
close();
const menuList = cloneDeep(props.menuList);
emits("onConfirm", menuList[index])
}
const bindFun = (data) => {
return data
}
onMounted(() => {
getContentHeight()
})
</script>
<style lang="scss" scoped>
.le-flex {
display: flex;
align-items: center;
height: 100%;
}
.le-dropdown {
width: 100%;
position: relative;
.le-dropdown-menu {
display: flex;
position: relative;
z-index: 11;
height: 80rpx;
.le-dropdown-menu-item {
height: 100%;
flex: 1;
display: flex;
justify-content: center;
align-items: center;
.le-dropdown-menu-item-text {
font-size: 24rpx;
}
.le-dropdown-menu-item-arrow {
margin-left: 6rpx;
transition: transform .3s;
align-items: center;
display: flex;
position: relative;
width: 10rpx;
height: 100%;
&::before {
content: '';
position: absolute;
top: calc(50% - 8rpx);
right: -2rpx;
transform: translateY(-50%);
border: 6rpx solid transparent;
border-bottom-color: #C1C1C1;
}
&::after {
content: '';
position: absolute;
top: calc(50% + 8rpx);
right: -2rpx;
transform: translateY(-50%);
border: 6rpx solid transparent;
border-top-color: #C1C1C1;
}
}
.le-dropdown-menu-item-arrow_top {
&::before {
border-bottom-color: var(--dropdownThemeColor);
}
}
.le-dropdown-menu-item-arrow_bottom {
&::after {
border-top-color: var(--dropdownThemeColor);
}
}
.le-dropdown-menu-item-basicarrow {
margin-left: 6rpx;
transition: transform .3s;
align-items: center;
display: flex;
position: relative;
border: 6rpx solid transparent;
border-bottom: 0rpx solid transparent;
border-top-color: #C1C1C1;
}
.le-dropdown-menu-item-basicarrow_rotate {
transform: rotate(180deg);
border-top-color: var(--dropdownThemeColor);
}
}
}
.le-dropdown-content {
position: absolute;
z-index: 8;
width: 100%;
left: 0px;
bottom: 0;
overflow: hidden;
.le-dropdown-content-mask {
position: absolute;
z-index: 9;
background: rgba(0, 0, 0, .3);
width: 100%;
left: 0;
top: 0;
bottom: 0;
}
.le-dropdown-content-popup {
position: relative;
max-height: 100%;
overflow: auto;
overscroll-behavior: contain;
z-index: 10;
transition: all 0.3s;
transform: translate3D(0, -100%, 0);
}
}
}
.le-dropdown-ceiling{
position: sticky;
top: 0;
z-index: 1;
}
.le-dropdown-popup-content {
font-size: 28rpx;
border-radius: 0 0 20rpx 20rpx;
background-color: #ffffff;
}
// 单选列表
.le-dropdown-cell {
padding: 0 30rpx;
.le-dropdown-cell-item {
padding: 20rpx 0;
color: #333333;
font-size: 28rpx;
border-bottom: 1rpx solid #D5D5D5;
display: flex;
align-items: center;
justify-content: space-between;
.le-dropdown-cell-active-text{
flex: 1;
padding-right: 20rpx;
}
}
.le-dropdown-cell-item:last-child {
border-bottom: 0rpx solid #D5D5D5;
}
.le-dropdown-cell-active {
color: var(--dropdownThemeColor);
.le-dropdown-cell-active-icon {
width: 12rpx;
height: 28rpx;
margin-right: 10rpx;
border-color: var(--dropdownThemeColor);
border-style: solid;
border-width: 0 4rpx 4rpx 0;
transform: rotate(45deg);
}
}
}
// 筛选
.le-dropdown-filter {
.le-dropdown-filter-item {
padding: 0 26rpx;
}
.le-dropdown-filter-title {
padding-top: 34rpx;
margin-bottom: 18rpx;
color: #333333;
font-size: 24rpx;
.le-dropdown-filter-subtitle {
margin-left: 10rpx;
color: var(--dropdownThemeColor);
}
}
.le-dropdown-filter-content {
display: flex;
flex-wrap: wrap;
}
.le-dropdown-filter-box {
width: 200rpx;
margin-right: 30rpx;
margin-bottom: 14rpx;
padding: 18rpx 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
color: #333333;
background-color: #F5F5F5;
border-radius: 999rpx;
}
.le-dropdown-filter-box-active {
color: var(--dropdownThemeColor);
background-color: rgba(var(--dropdownThemeColorRgb), 0.04);
}
}
.le-dropdown-footer {
display: flex;
align-items: center;
margin-top: 14rpx;
.le-dropdown-reset {
flex: 1;
margin: 26rpx;
display: flex;
align-items: center;
justify-content: center;
height: 68rpx;
font-size: 28rpx;
background-color: #FFFFFF;
color: var(--dropdownThemeColor);
border: 2rpx solid var(--dropdownThemeColor);
border-radius: 999rpx;
}
.le-dropdown-confirm {
flex: 1;
margin: 26rpx;
display: flex;
align-items: center;
justify-content: center;
height: 68rpx;
font-size: 28rpx;
background-color: var(--dropdownThemeColor);
border: 2rpx solid var(--dropdownThemeColor);
color: #ffffff;
border-radius: 999rpx;
}
}
</style>

View File

@@ -0,0 +1,17 @@
function deepClone(obj) {
let result = typeof obj.splice === "function" ? [] : {};
if (obj && typeof obj === 'object') {
for (let key in obj) {
if (obj[key] && typeof obj[key] === 'object') {
result[key] = deepClone(obj[key]);//如果对象的属性值为object的时候递归调用deepClone,即在吧某个值对象复制一份到新的对象的对应值中。
} else {
result[key] = obj[key];//如果对象的属性值不为object的时候直接复制参数对象的每一个键值到新的对象对应的键值对中。
}
}
return result;
}
return obj;
}
export default deepClone

View File

@@ -0,0 +1,23 @@
export default function(hexStr){
//十六进制颜色值的正则表达式
let reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
let sColor = hexStr.toLowerCase();
if (sColor && reg.test(sColor)) {
if (sColor.length === 4) {
let sColorNew = "#";
for (let i = 1; i < 4; i += 1) {
sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1));
}
sColor = sColorNew;
}
//处理六位的颜色值f
let sColorChange = [];
for (let i = 1; i < 7; i += 2) {
sColorChange.push(parseInt(`0x${sColor.slice(i, i + 2)}`));
}
let rgbText = sColorChange.join(",")
return rgbText;
} else {
return sColor;
}
}

View File

@@ -0,0 +1,81 @@
{
"id": "le-dropdown",
"displayName": "le-dropdown多功能下拉筛选菜单+主题色)",
"version": "1.0.4",
"description": "下拉筛选、多级级联、排序、点击菜单组件兼容小程序、H5、app可以更换主题色。",
"keywords": [
"le-dropdown",
"dropdown",
"筛选",
"菜单",
"下拉"
],
"repository": "https://gitee.com/le-ui/le-dropdown",
"engines": {
"HBuilderX": "^3.6.18"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": "",
"type": "component-vue"
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"Vue": {
"vue2": "n",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "u"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "n",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "u",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "n",
"联盟": "n"
}
}
}
}
}

View File

@@ -0,0 +1,72 @@
# le-dropdown
### 使用方式
```javascript
<le-dropdown
v-model:menuList="menuList"
themeColor="#3185FF"
:duration="300"
:isCeiling="false"
@onConfirm="onConfirm"
@onChange="onChange"
></le-dropdown>
```
### 组件的属性说明如下:
| 属性 | 类型 | 默认值 | 必填 | 说明 |
| ---------------- | ------- | ------- | ---- | ------------------------------ |
| v-model:menuList | Array | [] | 是 | 设置整个菜单筛选的配置数居 |
| themeColor | String | #3185FF | 否 | 整个组件的主题颜色 |
| duration | Number | 300 | 否 | 动画的执行时间 |
| isCeiling | Boolean | false | 否 | 是否自动吸顶采用sticky规则 |
#### menuList参数说明
| 属性 | 类型 | 必填 | 说明 |
| -------------- | --------------------- | ---- | ---------------------------------------------------------- |
| title | String | 是 | 菜单名称 |
| type | String | 是 | 菜单筛选展示的类型cell\|picker\|sort\|click\|filter |
| value | Number\|String\|Array | 是 | 菜单对应的值 |
| options | Array | 否 | 菜单配置的组件参数数据,**见下** |
| children | Array | 否 | 菜单配置的组件参数数据当前只用在filter类型**见下** |
| componentProps | Object | 否 | 菜单对应type的组件参数当前只用在picker类型**见下** |
##### options参数说明
| 属性 | 类型 | 必填 | 说明 |
| -------- | -------------- | ---- | ------------------------------- |
| label | String | 是 | 属性名,显示在页面上 |
| value | Number\|String | 是 | 属性值保存在父级的value属性上 |
| children | options[] | 否 | 子层级当前只在picker上使用 |
##### **children参数filter说明**
| 属性 | 类型 | 必填 | 说明 |
| ------- | -------------- | ---- | ------------------------------------------- |
| title | String | 是 | 菜单名称 |
| type | String | 是 | 菜单筛选展示的类型radio\|slider\|checkbox |
| value | Number\|String | 是 | 菜单对应的值 |
| options | Array | 是 | 菜单配置的组件参数数据,**见上** |
###### children下type参数说明
- radio单选
- checkbox多选
- slider进度存在componentProps与type同级参数对象里的值为uniapp原生的slider里的参数
##### componentProps参数picker说明
| 属性 | 类型 | 默认值 | 必填 | 说明 |
| ---------- | ------ | ------------------------------------------------------ | ---- | --------------------------------------- |
| colNum | Number | 1 | 否 | 级联的列数 |
| options | Array | - | 是 | 菜单配置的组件参数数据,**见上** |
| fieldNames | Object | { label: `label`, value: `value`, options: `options` } | 否 | 自定义节点 label、value、options 的字段 |
### 事件
| 事件名称 | 回调参数 | 说明 |
| --------- | -------------------- | ------------------------------------------------------------ |
| onConfirm | (data) => void | 确定事件回调data为当前确认选中的菜单数据 |
| onChange | (data,index) => void | 改变事件data为当前操作菜单数据index为当前操作菜单第index个索引发生变化 |