feat: 首次提交
This commit is contained in:
588
components/firstui/fui-cascader/fui-cascader.vue
Normal file
588
components/firstui/fui-cascader/fui-cascader.vue
Normal file
@ -0,0 +1,588 @@
|
||||
<!--本文件由FirstUI授权予新疆天衡创新研究院有限公司(手机号: 186 14 07 2 5 49,身份证尾号:5A07X5)专用,请尊重知识产权,勿私下传播,违者追究法律责任。-->
|
||||
<template>
|
||||
<view class="fui-cascader__wrap">
|
||||
<view class="fui-cascader__header-wrap">
|
||||
<scroll-view :scroll-x="true" scroll-with-animation :show-scrollbar="false" :scroll-into-view="scrollViewId"
|
||||
:style="{ background: headBackground }" class="fui-cascader__scroll">
|
||||
<view class="fui-cascader__header" :style="{ height: headHeight+'rpx'}">
|
||||
<view class="fui-cascader__header-item" :id="`fui_cr_${idx}`" v-for="(item, idx) in selectedArr"
|
||||
:key="idx" @tap.stop="swichTabs(idx)">
|
||||
<text class="fui-cascader__header-text"
|
||||
:class="{'fui-cascader__hi-width':item.text.length>6,'fui-cascader__color':idx === current && !getActiveColor}"
|
||||
:style="{ color: idx === current ? getActiveColor : color, fontSize: size + 'rpx',fontWeight:idx === current?'bold':'normal' }">{{ item.text }}</text>
|
||||
<view class="fui-cascader__header-line" :class="{'fui-cascader__bg':!getActiveColor}"
|
||||
:style="{ background: getActiveColor }" v-if="idx === current && showLine"></view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="fui-cascader__border" :style="{background:borderColor}" v-if="showBorder"></view>
|
||||
</view>
|
||||
<!-- #ifdef MP-TOUTIAO -->
|
||||
<view :style="{ height: height+'rpx', background: background}">
|
||||
<swiper class="fui-cascader__list" :current="defCurrent" :circular="false" :duration="300"
|
||||
@change="switchTab" :style="{ height: height+'rpx', background: background}" v-if="isShow">
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef MP-TOUTIAO -->
|
||||
<swiper class="fui-cascader__list" :current="defCurrent" :circular="false" :duration="300"
|
||||
@change="switchTab" :style="{ height: height+'rpx', background: background}">
|
||||
<!-- #endif -->
|
||||
<swiper-item v-for="(item, index) in selectedArr" :key="index">
|
||||
<scroll-view :show-scrollbar="false" scroll-y :scroll-into-view="item.scrollViewId"
|
||||
class="fui-cascader__item-scroll" :style="{ height: height+'rpx', background: background }">
|
||||
<view class="fui-cascader__seat"></view>
|
||||
<view class="fui-cascader__cell" :id="`fui_c_${subi}`" v-for="(sub, subi) in item.data"
|
||||
:key="subi" @tap.stop="change(index, subi, sub)">
|
||||
<view class="fui-cascader__checkmark"
|
||||
:class="{'fui-cascader__icon-border':!getActiveColor}"
|
||||
:style="{borderBottomColor:checkMarkColor || getActiveColor,borderRightColor:checkMarkColor || getActiveColor}"
|
||||
v-if="item.index === subi"></view>
|
||||
<image :src="sub.src" v-if="sub.src" class="fui-cascader__img"
|
||||
:style="{ width: imgWidth+'rpx', height: imgHeight+'rpx', borderRadius: radius+'rpx' }">
|
||||
</image>
|
||||
<text class="fui-cascader__text"
|
||||
:style="{ color: item.index === subi ? textActiveColor : textColor, fontSize: textSize + 'rpx',fontWeight:item.index === subi?'bold':'normal' }">{{ sub.text }}</text>
|
||||
</view>
|
||||
<view class="fui-cascader__seat"></view>
|
||||
</scroll-view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
<!-- #ifdef MP-TOUTIAO -->
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "fui-cascader",
|
||||
emits: ['change', 'complete'],
|
||||
props: {
|
||||
options: {
|
||||
type: Array,
|
||||
default () {
|
||||
return []
|
||||
}
|
||||
},
|
||||
value: {
|
||||
type: Array,
|
||||
default () {
|
||||
return []
|
||||
}
|
||||
},
|
||||
defaultKey: {
|
||||
type: String,
|
||||
default: 'value'
|
||||
},
|
||||
stepLoading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
showBorder: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
borderColor: {
|
||||
type: String,
|
||||
default: '#eee'
|
||||
},
|
||||
headHeight: {
|
||||
type: [Number, String],
|
||||
default: 88
|
||||
},
|
||||
headBackground: {
|
||||
type: String,
|
||||
default: '#FFFFFF'
|
||||
},
|
||||
text: {
|
||||
type: String,
|
||||
default: '请选择'
|
||||
},
|
||||
size: {
|
||||
type: [Number, String],
|
||||
default: 28
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: '#181818'
|
||||
},
|
||||
//选中颜色
|
||||
activeColor: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
showLine: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
height: {
|
||||
type: [Number, String],
|
||||
default: 600
|
||||
},
|
||||
//item swiper 内容部分背景颜色
|
||||
background: {
|
||||
type: String,
|
||||
default: '#FFFFFF'
|
||||
},
|
||||
checkMarkColor: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
imgWidth: {
|
||||
type: [Number, String],
|
||||
default: 48
|
||||
},
|
||||
imgHeight: {
|
||||
type: [Number, String],
|
||||
default: 48
|
||||
},
|
||||
radius: {
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
},
|
||||
textSize: {
|
||||
type: [Number, String],
|
||||
default: 26
|
||||
},
|
||||
textColor: {
|
||||
type: String,
|
||||
default: '#181818'
|
||||
},
|
||||
textActiveColor: {
|
||||
type: String,
|
||||
default: '#181818'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
getActiveColor() {
|
||||
let color = this.activeColor;
|
||||
// #ifdef APP-NVUE
|
||||
if (!color || color === true) {
|
||||
const app = uni && uni.$fui && uni.$fui.color;
|
||||
color = (app && app.primary) || '#465CFF';
|
||||
}
|
||||
// #endif
|
||||
return color;
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
current: 0,
|
||||
defCurrent: 0,
|
||||
selectedArr: [],
|
||||
scrollViewId: 'fui_cr__0',
|
||||
isShow: true
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
options(vals) {
|
||||
this.setDefaultOptions(this.value);
|
||||
},
|
||||
value(vals) {
|
||||
this.setDefaultOptions(vals)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.setDefaultOptions(this.value)
|
||||
},
|
||||
methods: {
|
||||
//重置选择,暴露给用户使用
|
||||
reset() {
|
||||
this.initData(this.options, -1);
|
||||
},
|
||||
//设置请求返回数据,暴露给用户使用(分级加载数据)
|
||||
setRequestData(data, layer) {
|
||||
this.subLevelData(data, layer)
|
||||
},
|
||||
//无子级数据,选择结束
|
||||
end(layer) {
|
||||
this.subLevelData([], layer)
|
||||
},
|
||||
subLevelData(data, layer) {
|
||||
if (!data || data.length === 0) {
|
||||
if (layer == -1) return;
|
||||
let arr = this.selectedArr;
|
||||
if (layer < arr.length - 1) {
|
||||
let newArr = arr.slice(0, layer + 1);
|
||||
this.selectedArr = newArr;
|
||||
}
|
||||
// #ifdef MP-TOUTIAO
|
||||
this.isShow = false
|
||||
this.$nextTick(() => {
|
||||
setTimeout(() => {
|
||||
this.isShow = true
|
||||
}, 10)
|
||||
})
|
||||
// #endif
|
||||
let result = JSON.parse(JSON.stringify(this.selectedArr));
|
||||
let lastItem = result[result.length - 1] || {};
|
||||
let text = [];
|
||||
let value = [];
|
||||
let src = [];
|
||||
result.map(item => {
|
||||
text.push(item.text);
|
||||
value.push(item.value)
|
||||
src.push(item.src)
|
||||
delete item['data'];
|
||||
delete item['index'];
|
||||
delete item['scrollViewId'];
|
||||
return item;
|
||||
});
|
||||
this.$emit('complete', {
|
||||
result: result,
|
||||
value: value,
|
||||
text: text,
|
||||
src: src
|
||||
});
|
||||
setTimeout(() => {
|
||||
this.scrollViewId = `fui_cr_${layer}`;
|
||||
}, 50)
|
||||
} else {
|
||||
let item = [{
|
||||
text: this.text,
|
||||
value: '',
|
||||
src: '',
|
||||
index: -1,
|
||||
scrollViewId: 'fui_c__0',
|
||||
data
|
||||
}];
|
||||
if (layer == -1) {
|
||||
this.selectedArr = item;
|
||||
} else {
|
||||
let retainArr = this.selectedArr.slice(0, layer + 1) || [];
|
||||
this.selectedArr = retainArr.concat(item);
|
||||
}
|
||||
let current = this.selectedArr.length - 1;
|
||||
if (current >= this.current) {
|
||||
this.defCurrent = this.current
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
setTimeout(() => {
|
||||
this.defCurrent = current;
|
||||
this.current = current;
|
||||
this.scrollViewId = `fui_cr_${this.current > 1?this.current - 1:0}`;
|
||||
}, 50)
|
||||
});
|
||||
}
|
||||
},
|
||||
getDefaultIndex(arr, val) {
|
||||
if (!arr || arr.length === 0 || val === undefined) return -1;
|
||||
let key = this.defaultKey || 'value';
|
||||
let index = -1;
|
||||
for (let i = 0, len = arr.length; i < len; i++) {
|
||||
if (arr[i][key] == val) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return index;
|
||||
},
|
||||
removeChildren(data) {
|
||||
let list = data.map(item => {
|
||||
delete item['children'];
|
||||
return item;
|
||||
});
|
||||
return list;
|
||||
},
|
||||
getItemList(layer, index, selectedArr) {
|
||||
let list = [];
|
||||
let arr = JSON.parse(JSON.stringify(this.options));
|
||||
selectedArr = selectedArr || this.selectedArr
|
||||
if (layer == -1) {
|
||||
list = this.removeChildren(arr);
|
||||
} else {
|
||||
let subi = selectedArr[0].index;
|
||||
subi = subi === undefined || subi == -1 ? index : subi;
|
||||
if (arr[subi] && arr[subi].children) {
|
||||
list = arr[subi].children
|
||||
}
|
||||
if (layer > 0) {
|
||||
for (let i = 1; i < layer + 1; i++) {
|
||||
let val = layer === i ? index : selectedArr[i].index;
|
||||
list = val === -1 ? [] : (list[val].children || []);
|
||||
if (list.length === 0) break;
|
||||
}
|
||||
}
|
||||
list = this.removeChildren(list);
|
||||
}
|
||||
return list;
|
||||
},
|
||||
setDefaultOptions(vals) {
|
||||
let options = this.options || []
|
||||
if (!options || options.length === 0) return;
|
||||
vals = vals || [];
|
||||
let selectedArr = []
|
||||
if (vals.length > 0) {
|
||||
//分级加载
|
||||
if (this.stepLoading) {
|
||||
options.forEach((item, index) => {
|
||||
let subi = this.getDefaultIndex(item, vals[index])
|
||||
let obj = item[subi] || {}
|
||||
selectedArr.push({
|
||||
text: obj.text || this.text,
|
||||
value: obj.value || '',
|
||||
src: obj.src || '',
|
||||
index: subi,
|
||||
scrollViewId: `fui_c_${subi}`,
|
||||
data: item
|
||||
})
|
||||
})
|
||||
} else {
|
||||
let subi = -1
|
||||
for (let j = 0, len = vals.length; j < len; j++) {
|
||||
let item = vals[j]
|
||||
let list = []
|
||||
let obj = {}
|
||||
if (j === 0) {
|
||||
list = this.getItemList(-1)
|
||||
} else {
|
||||
list = this.getItemList(j - 1, subi, selectedArr)
|
||||
}
|
||||
subi = this.getDefaultIndex(list, item)
|
||||
if (subi !== -1) {
|
||||
obj = list[subi]
|
||||
}
|
||||
selectedArr.push({
|
||||
text: obj.text || this.text,
|
||||
value: obj.value || '',
|
||||
src: obj.src || '',
|
||||
index: subi,
|
||||
scrollViewId: `fui_c_${subi}`,
|
||||
data: list
|
||||
})
|
||||
if (subi === -1) break;
|
||||
}
|
||||
}
|
||||
this.selectedArr = selectedArr;
|
||||
this.defCurrent = this.current;
|
||||
let current = selectedArr.length - 1;
|
||||
this.$nextTick(() => {
|
||||
setTimeout(() => {
|
||||
this.defCurrent = current;
|
||||
this.current = current;
|
||||
this.checkTabs();
|
||||
}, 30)
|
||||
});
|
||||
} else {
|
||||
this.initData(options, -1);
|
||||
}
|
||||
},
|
||||
initData(data, layer) {
|
||||
if (!data || data.length === 0) return;
|
||||
if (this.stepLoading) {
|
||||
if (Array.isArray(data[0])) {
|
||||
data = data[0]
|
||||
}
|
||||
this.subLevelData(data, layer);
|
||||
} else {
|
||||
this.subLevelData(this.getItemList(layer, -1), layer);
|
||||
}
|
||||
},
|
||||
swichTabs(current) {
|
||||
if (this.current != current) {
|
||||
this.defCurrent = this.current;
|
||||
setTimeout(() => {
|
||||
this.defCurrent = current;
|
||||
this.current = current;
|
||||
}, 30)
|
||||
}
|
||||
},
|
||||
checkTabs() {
|
||||
let current = this.current;
|
||||
let item = this.selectedArr[current] || {};
|
||||
item.scrollViewId = 'fui_c__0';
|
||||
this.$nextTick(() => {
|
||||
setTimeout(() => {
|
||||
let index = Number(item.index)
|
||||
let val = index < 2 ? 0 : index - 2;
|
||||
item.scrollViewId = `fui_c_${val}`;
|
||||
}, 30);
|
||||
});
|
||||
this.scrollViewId = `fui_cr_${current > 1?current - 1:0}`;
|
||||
},
|
||||
switchTab(e) {
|
||||
this.current = e.detail.current;
|
||||
this.checkTabs();
|
||||
},
|
||||
change(index, subi, sub) {
|
||||
let item = this.selectedArr[index];
|
||||
if (item.index == subi) return;
|
||||
item.index = subi;
|
||||
item.text = sub.text;
|
||||
item.value = sub.value;
|
||||
item.src = sub.src || '';
|
||||
this.$emit('change', {
|
||||
layer: index,
|
||||
index: subi,
|
||||
...sub
|
||||
});
|
||||
if (!this.stepLoading) {
|
||||
let data = this.getItemList(index, subi);
|
||||
this.subLevelData(data, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.fui-cascader__wrap {
|
||||
/* #ifndef APP-NVUE */
|
||||
width: 100%;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.fui-cascader__scroll {
|
||||
/* #ifndef APP-NVUE */
|
||||
width: 100%;
|
||||
/* #endif */
|
||||
/* #ifdef APP-NVUE */
|
||||
flex: 1;
|
||||
flex-direction: row;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.fui-cascader__header-wrap {
|
||||
/* #ifndef APP-NVUE */
|
||||
width: 100%;
|
||||
/* #endif */
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.fui-cascader__header {
|
||||
/* #ifndef APP-NVUE */
|
||||
width: 100%;
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.fui-cascader__header-item {
|
||||
padding: 24rpx 32rpx;
|
||||
/* #ifndef APP-NVUE */
|
||||
flex-shrink: 0;
|
||||
overflow: hidden;
|
||||
/* #endif */
|
||||
/* #ifdef H5 */
|
||||
cursor: pointer;
|
||||
/* #endif */
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.fui-cascader__header-text {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
/* #endif */
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
/* #ifdef APP-NVUE */
|
||||
lines: 1;
|
||||
/* #endif */
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.fui-cascader__hi-width {
|
||||
width: 240rpx;
|
||||
}
|
||||
|
||||
.fui-cascader__header-line {
|
||||
height: 6rpx;
|
||||
border-radius: 4rpx;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 32rpx;
|
||||
right: 32rpx;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.fui-cascader__border {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
/* #ifdef APP-NVUE */
|
||||
height: 0.5px;
|
||||
z-index: -1;
|
||||
/* #endif */
|
||||
/* #ifndef APP-NVUE */
|
||||
height: 1px;
|
||||
-webkit-transform: scaleY(0.5);
|
||||
transform: scaleY(0.5);
|
||||
transform-origin: 0 100%;
|
||||
z-index: 1;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.fui-cascader__list {
|
||||
/* #ifndef APP-NVUE */
|
||||
width: 100%;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.fui-cascader__seat {
|
||||
/* #ifndef APP-NVUE */
|
||||
width: 100%;
|
||||
/* #endif */
|
||||
height: 24rpx;
|
||||
}
|
||||
|
||||
.fui-cascader__cell {
|
||||
/* #ifndef APP-NVUE */
|
||||
width: 100%;
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 20rpx 32rpx;
|
||||
/* #ifdef H5 */
|
||||
cursor: pointer;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.fui-cascader__checkmark {
|
||||
width: 24rpx;
|
||||
height: 48rpx;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-width: 3px;
|
||||
border-bottom-color: #FFFFFF;
|
||||
border-right-style: solid;
|
||||
border-right-width: 3px;
|
||||
border-right-color: #FFFFFF;
|
||||
/* #ifndef APP-NVUE */
|
||||
flex-shrink: 0;
|
||||
box-sizing: border-box;
|
||||
transform: rotate(45deg) scale(0.5) translateZ(0);
|
||||
/* #endif */
|
||||
/* #ifdef APP-NVUE */
|
||||
transform: rotate(45deg) scale(0.5);
|
||||
/* #endif */
|
||||
transform-origin: 54% 48%;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.fui-cascader__img {
|
||||
margin-right: 16rpx;
|
||||
/* #ifndef APP-NVUE */
|
||||
flex-shrink: 0;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
/* #ifndef APP-NVUE */
|
||||
.fui-cascader__color {
|
||||
color: var(--fui-color-primary, #465CFF) !important;
|
||||
}
|
||||
|
||||
.fui-cascader__bg {
|
||||
background: var(--fui-color-primary, #465CFF) !important;
|
||||
}
|
||||
|
||||
.fui-cascader__icon-border {
|
||||
border-bottom-color: var(--fui-color-primary, #465CFF) !important;
|
||||
border-right-color: var(--fui-color-primary, #465CFF) !important;
|
||||
}
|
||||
|
||||
/* #endif */
|
||||
</style>
|
Reference in New Issue
Block a user