首页 分类列表
This commit is contained in:
@@ -14,4 +14,7 @@ export default {
|
||||
home(data) { //首页上半部分
|
||||
return uni.api.post("/home", data);
|
||||
},
|
||||
locationdistrict(data) { //获取行政区域(区,街道)
|
||||
return uni.api.get("/location/district", data);
|
||||
},
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
13
uni_modules/le-dropdown/changelog.md
Normal file
13
uni_modules/le-dropdown/changelog.md
Normal file
@@ -0,0 +1,13 @@
|
||||
## 1.0.4(2023-06-04)
|
||||
1.更新层级点击不了问题;
|
||||
2.增加吸顶属性配置;
|
||||
3.cell类型选中建议增加对号图标;
|
||||
4.完善文档;
|
||||
## 1.0.3(2023-04-15)
|
||||
更新选项增加箭头交互
|
||||
## 1.0.2(2023-04-14)
|
||||
更新示例
|
||||
## 1.0.1(2023-04-09)
|
||||
更新版本标签
|
||||
## 1.0.0(2023-04-09)
|
||||
第一个版本
|
||||
@@ -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>
|
||||
603
uni_modules/le-dropdown/components/le-dropdown/le-dropdown.vue
Normal file
603
uni_modules/le-dropdown/components/le-dropdown/le-dropdown.vue
Normal 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>
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
81
uni_modules/le-dropdown/package.json
Normal file
81
uni_modules/le-dropdown/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
72
uni_modules/le-dropdown/readme.md
Normal file
72
uni_modules/le-dropdown/readme.md
Normal 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个索引发生变化 |
|
||||
|
||||
Reference in New Issue
Block a user