zyejMAll-mobile/sheep/ui/su-data-checkbox/su-data-checkbox.vue
2024-08-07 10:31:42 +08:00

895 lines
24 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="uni-data-checklist" :style="{ 'margin-top': isTop + 'px' }">
<template v-if="!isLocal">
<view class="uni-data-loading">
<uni-load-more
v-if="!mixinDatacomErrorMessage"
status="loading"
iconType="snow"
:iconSize="18"
:content-text="contentText"
></uni-load-more>
<text v-else>{{ mixinDatacomErrorMessage }}</text>
</view>
</template>
<template v-else>
<checkbox-group
v-if="multiple"
class="checklist-group"
:class="{ 'is-list': mode === 'list' || wrap }"
@change="chagne"
>
<label
class="checklist-box"
:class="[
'is--' + mode,
item.selected ? 'is-checked' : '',
disabled || !!item.disabled ? 'is-disable' : '',
index !== 0 && mode === 'list' ? 'is-list-border' : '',
]"
:style="item.styleBackgroud"
v-for="(item, index) in dataList"
:key="index"
>
<checkbox
class="hidden"
hidden
:disabled="disabled || !!item.disabled"
:value="item[map.value] + ''"
:checked="item.selected"
/>
<view
v-if="(mode !== 'tag' && mode !== 'list') || (mode === 'list' && icon === 'left')"
class="checkbox__inner"
:style="item.styleIcon"
>
<view class="checkbox__inner-icon"></view>
</view>
<view
class="checklist-content"
:class="{ 'list-content': mode === 'list' && icon === 'left' }"
>
<text class="checklist-text" :style="item.styleIconText">{{ item[map.text] }}</text>
<view
v-if="mode === 'list' && icon === 'right'"
class="checkobx__list"
:style="item.styleBackgroud"
></view>
</view>
</label>
</checkbox-group>
<radio-group
v-else
class="checklist-group"
:class="{ 'is-list': mode === 'list', 'is-wrap': wrap }"
@change="chagne"
>
<!-- -->
<label
class="checklist-box"
:class="[
'is--' + mode,
item.selected ? 'is-checked' : '',
disabled || !!item.disabled ? 'is-disable' : '',
index !== 0 && mode === 'list' ? 'is-list-border' : '',
]"
:style="item.styleBackgroud"
v-for="(item, index) in dataList"
:key="index"
>
<radio
class="hidden"
hidden
:disabled="disabled || item.disabled"
:value="item[map.value] + ''"
:checked="item.selected"
/>
<view
v-if="(mode !== 'tag' && mode !== 'list') || (mode === 'list' && icon === 'left')"
class="radio__inner"
:style="item.styleBackgroud"
>
<view class="radio__inner-icon" :style="item.styleIcon"></view>
</view>
<view
class="checklist-content"
:class="{ 'list-content': mode === 'list' && icon === 'left' }"
>
<text class="checklist-text" :style="item.styleIconText">{{ item[map.text] }}</text>
<view
v-if="mode === 'list' && icon === 'right'"
:style="item.styleRightIcon"
class="checkobx__list"
></view>
</view>
</label>
</radio-group>
</template>
</view>
</template>
<script>
/**
* DataChecklist 数据选择器
* @description 通过数据渲染 checkbox 和 radio
* @tutorial https://ext.dcloud.net.cn/plugin?id=xxx
* @property {String} mode = [default| list | button | tag] 显示模式
* @value default 默认横排模式
* @value list 列表模式
* @value button 按钮模式
* @value tag 标签模式
* @property {Boolean} multiple = [true|false] 是否多选
* @property {Array|String|Number} value 默认值
* @property {Array} localdata 本地数据 ,格式 [{text:'',value:''}]
* @property {Number|String} min 最小选择个数 multiple为true时生效
* @property {Number|String} max 最大选择个数 multiple为true时生效
* @property {Boolean} wrap 是否换行显示
* @property {String} icon = [left|right] list 列表模式下icon显示位置
* @property {Boolean} selectedColor 选中颜色
* @property {Boolean} emptyText 没有数据时显示的文字 ,本地数据无效
* @property {Boolean} selectedTextColor 选中文本颜色,如不填写则自动显示
* @property {Object} map 字段映射, 默认 map={text:'text',value:'value'}
* @value left 左侧显示
* @value right 右侧显示
* @event {Function} change 选中发生变化触发
*/
export default {
name: 'uniDataChecklist',
mixins: [uniCloud.mixinDatacom || {}],
emits: ['input', 'update:modelValue', 'change'],
props: {
mode: {
type: String,
default: 'default',
},
multiple: {
type: Boolean,
default: false,
},
value: {
type: [Array, String, Number],
default() {
return '';
},
},
// TODO vue3
modelValue: {
type: [Array, String, Number],
default() {
return '';
},
},
localdata: {
type: Array,
default() {
return [];
},
},
min: {
type: [Number, String],
default: '',
},
max: {
type: [Number, String],
default: '',
},
wrap: {
type: Boolean,
default: false,
},
icon: {
type: String,
default: 'left',
},
selectedColor: {
type: String,
default: '',
},
selectedTextColor: {
type: String,
default: '',
},
emptyText: {
type: String,
default: '暂无数据',
},
disabled: {
type: Boolean,
default: false,
},
map: {
type: Object,
default() {
return {
text: 'text',
value: 'value',
};
},
},
},
watch: {
localdata: {
handler(newVal) {
this.range = newVal;
this.dataList = this.getDataList(this.getSelectedValue(newVal));
},
deep: true,
},
mixinDatacomResData(newVal) {
this.range = newVal;
this.dataList = this.getDataList(this.getSelectedValue(newVal));
},
value(newVal) {
this.dataList = this.getDataList(newVal);
// fix by mehaotian is_reset 在 uni-forms 中定义
if (!this.is_reset) {
this.is_reset = false;
this.formItem && this.formItem.setValue(newVal);
}
},
modelValue(newVal) {
this.dataList = this.getDataList(newVal);
if (!this.is_reset) {
this.is_reset = false;
this.formItem && this.formItem.setValue(newVal);
}
},
},
data() {
return {
dataList: [],
range: [],
contentText: {
contentdown: '查看更多',
contentrefresh: '加载中',
contentnomore: '没有更多',
},
isLocal: true,
styles: {
selectedColor: '#2979ff',
selectedTextColor: '#666',
},
isTop: 0,
};
},
computed: {
dataValue() {
if (this.value === '') return this.modelValue;
if (this.modelValue === '') return this.value;
return this.value;
},
},
created() {
this.form = this.getForm('uniForms');
this.formItem = this.getForm('uniFormsItem');
// this.formItem && this.formItem.setValue(this.value)
if (this.formItem) {
this.isTop = 6;
if (this.formItem.name) {
// 如果存在name添加默认值,否则formData 中不存在这个字段不校验
if (!this.is_reset) {
this.is_reset = false;
this.formItem.setValue(this.dataValue);
}
this.rename = this.formItem.name;
this.form.inputChildrens.push(this);
}
}
if (this.localdata && this.localdata.length !== 0) {
this.isLocal = true;
this.range = this.localdata;
this.dataList = this.getDataList(this.getSelectedValue(this.range));
} else {
if (this.collection) {
this.isLocal = false;
this.loadData();
}
}
},
methods: {
loadData() {
this.mixinDatacomGet()
.then((res) => {
this.mixinDatacomResData = res.result.data;
if (this.mixinDatacomResData.length === 0) {
this.isLocal = false;
this.mixinDatacomErrorMessage = this.emptyText;
} else {
this.isLocal = true;
}
})
.catch((err) => {
this.mixinDatacomErrorMessage = err.message;
});
},
/**
* 获取父元素实例
*/
getForm(name = 'uniForms') {
let parent = this.$parent;
let parentName = parent.$options.name;
while (parentName !== name) {
parent = parent.$parent;
if (!parent) return false;
parentName = parent.$options.name;
}
return parent;
},
chagne(e) {
const values = e.detail.value;
let detail = {
value: [],
data: [],
};
if (this.multiple) {
this.range.forEach((item) => {
if (values.includes(item[this.map.value] + '')) {
detail.value.push(item[this.map.value]);
detail.data.push(item);
}
});
} else {
const range = this.range.find((item) => item[this.map.value] + '' === values);
if (range) {
detail = {
value: range[this.map.value],
data: range,
};
}
}
this.formItem && this.formItem.setValue(detail.value);
// TODO 兼容 vue2
this.$emit('input', detail.value);
// // TOTO 兼容 vue3
this.$emit('update:modelValue', detail.value);
this.$emit('change', {
detail,
});
if (this.multiple) {
// 如果 v-model 没有绑定 ,则走内部逻辑
// if (this.value.length === 0) {
this.dataList = this.getDataList(detail.value, true);
// }
} else {
this.dataList = this.getDataList(detail.value);
}
},
/**
* 获取渲染的新数组
* @param {Object} value 选中内容
*/
getDataList(value) {
// 解除引用关系,破坏原引用关系,避免污染源数据
let dataList = JSON.parse(JSON.stringify(this.range));
let list = [];
if (this.multiple) {
if (!Array.isArray(value)) {
value = [];
}
}
dataList.forEach((item, index) => {
item.disabled = item.disable || item.disabled || false;
if (this.multiple) {
if (value.length > 0) {
let have = value.find((val) => val === item[this.map.value]);
item.selected = have !== undefined;
} else {
item.selected = false;
}
} else {
item.selected = value === item[this.map.value];
}
list.push(item);
});
return this.setRange(list);
},
/**
* 处理最大最小值
* @param {Object} list
*/
setRange(list) {
let selectList = list.filter((item) => item.selected);
let min = Number(this.min) || 0;
let max = Number(this.max) || '';
list.forEach((item, index) => {
if (this.multiple) {
if (selectList.length <= min) {
let have = selectList.find((val) => val[this.map.value] === item[this.map.value]);
if (have !== undefined) {
item.disabled = true;
}
}
if (selectList.length >= max && max !== '') {
let have = selectList.find((val) => val[this.map.value] === item[this.map.value]);
if (have === undefined) {
item.disabled = true;
}
}
}
this.setStyles(item, index);
list[index] = item;
});
return list;
},
/**
* 设置 class
* @param {Object} item
* @param {Object} index
*/
setStyles(item, index) {
// 设置自定义样式
item.styleBackgroud = this.setStyleBackgroud(item);
item.styleIcon = this.setStyleIcon(item);
item.styleIconText = this.setStyleIconText(item);
item.styleRightIcon = this.setStyleRightIcon(item);
},
/**
* 获取选中值
* @param {Object} range
*/
getSelectedValue(range) {
if (!this.multiple) return this.dataValue;
let selectedArr = [];
range.forEach((item) => {
if (item.selected) {
selectedArr.push(item[this.map.value]);
}
});
return this.dataValue && this.dataValue.length > 0 ? this.dataValue : selectedArr;
},
/**
* 设置背景样式
*/
setStyleBackgroud(item) {
let styles = {};
let selectedColor = this.selectedColor ? this.selectedColor : '#2979ff';
if (this.mode !== 'list') {
styles['border-color'] = item.selected ? selectedColor : '#DCDFE6';
}
if (this.mode === 'tag') {
styles['background-color'] = item.selected ? selectedColor : '#f5f5f5';
}
let classles = '';
for (let i in styles) {
classles += `${i}:${styles[i]};`;
}
return classles;
},
setStyleIcon(item) {
let styles = {};
let classles = '';
let selectedColor = this.selectedColor ? this.selectedColor : '#2979ff';
styles['background-color'] = item.selected ? selectedColor : '#fff';
styles['border-color'] = item.selected ? selectedColor : '#DCDFE6';
if (!item.selected && item.disabled) {
styles['background-color'] = '#F2F6FC';
styles['border-color'] = item.selected ? selectedColor : '#DCDFE6';
}
for (let i in styles) {
classles += `${i}:${styles[i]};`;
}
return classles;
},
setStyleIconText(item) {
let styles = {};
let classles = '';
let selectedColor = this.selectedColor ? this.selectedColor : '#2979ff';
if (this.mode === 'tag') {
styles.color = item.selected
? this.selectedTextColor
? this.selectedTextColor
: '#fff'
: '#666';
} else {
styles.color = item.selected
? this.selectedTextColor
? this.selectedTextColor
: selectedColor
: '#666';
}
if (!item.selected && item.disabled) {
styles.color = '#999';
}
for (let i in styles) {
classles += `${i}:${styles[i]};`;
}
return classles;
},
setStyleRightIcon(item) {
let styles = {};
let classles = '';
if (this.mode === 'list') {
styles['border-color'] = item.selected ? this.styles.selectedColor : '#DCDFE6';
}
for (let i in styles) {
classles += `${i}:${styles[i]};`;
}
return classles;
},
},
};
</script>
<style lang="scss">
$checked-color: var(--ui-BG-Main);
$border-color: #dcdfe6;
$disable: 0.4;
@mixin flex {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
}
.uni-data-loading {
@include flex;
flex-direction: row;
justify-content: center;
align-items: center;
height: 36px;
padding-left: 10px;
color: #999;
}
.uni-data-checklist {
position: relative;
z-index: 0;
flex: 1;
// 多选样式
.checklist-group {
@include flex;
flex-direction: row;
flex-wrap: wrap;
&.is-list {
flex-direction: column;
}
.checklist-box {
@include flex;
flex-direction: row;
align-items: center;
position: relative;
margin: 5px 0;
margin-right: 25px;
.hidden {
position: absolute;
opacity: 0;
}
// 文字样式
.checklist-content {
@include flex;
flex: 1;
flex-direction: row;
align-items: center;
justify-content: space-between;
.checklist-text {
font-size: 14px;
color: #666;
margin-left: 5px;
line-height: 14px;
}
.checkobx__list {
border-right-width: 1px;
border-right-color: #007aff;
border-right-style: solid;
border-bottom-width: 1px;
border-bottom-color: #007aff;
border-bottom-style: solid;
height: 12px;
width: 6px;
left: -5px;
transform-origin: center;
transform: rotate(45deg);
opacity: 0;
}
}
// 多选样式
.checkbox__inner {
/* #ifndef APP-NVUE */
flex-shrink: 0;
box-sizing: border-box;
/* #endif */
position: relative;
width: 16px;
height: 16px;
border: 1px solid $border-color;
border-radius: 4px;
background-color: #fff;
z-index: 1;
.checkbox__inner-icon {
position: absolute;
/* #ifdef APP-NVUE */
top: 2px;
/* #endif */
/* #ifndef APP-NVUE */
top: 1px;
/* #endif */
left: 5px;
height: 8px;
width: 4px;
border-right-width: 1px;
border-right-color: #fff;
border-right-style: solid;
border-bottom-width: 1px;
border-bottom-color: #fff;
border-bottom-style: solid;
opacity: 0;
transform-origin: center;
transform: rotate(40deg);
}
}
// 单选样式
.radio__inner {
@include flex;
/* #ifndef APP-NVUE */
flex-shrink: 0;
box-sizing: border-box;
/* #endif */
justify-content: center;
align-items: center;
position: relative;
width: 16px;
height: 16px;
border: 1px solid $border-color;
border-radius: 16px;
background-color: #fff;
z-index: 1;
.radio__inner-icon {
width: 8px;
height: 8px;
border-radius: 10px;
opacity: 0;
}
}
// 默认样式
&.is--default {
// 禁用
&.is-disable {
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
.checkbox__inner {
background-color: #f2f6fc;
border-color: $border-color;
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
}
.radio__inner {
background-color: #f2f6fc;
border-color: $border-color;
}
.checklist-text {
color: #999;
}
}
// 选中
&.is-checked {
.checkbox__inner {
border-color: $checked-color;
background-color: $checked-color;
.checkbox__inner-icon {
opacity: 1;
transform: rotate(45deg);
}
}
.radio__inner {
border-color: $checked-color;
.radio__inner-icon {
opacity: 1;
background-color: $checked-color;
}
}
.checklist-text {
color: $checked-color;
}
// 选中禁用
&.is-disable {
.checkbox__inner {
opacity: $disable;
}
.checklist-text {
opacity: $disable;
}
.radio__inner {
opacity: $disable;
}
}
}
}
// 按钮样式
&.is--button {
margin-right: 10px;
padding: 5px 10px;
border: 1px $border-color solid;
border-radius: 3px;
transition: border-color 0.2s;
// 禁用
&.is-disable {
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
border: 1px #eee solid;
opacity: $disable;
.checkbox__inner {
background-color: #f2f6fc;
border-color: $border-color;
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
}
.radio__inner {
background-color: #f2f6fc;
border-color: $border-color;
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
}
.checklist-text {
color: #999;
}
}
&.is-checked {
border-color: $checked-color;
.checkbox__inner {
border-color: $checked-color;
background-color: $checked-color;
.checkbox__inner-icon {
opacity: 1;
transform: rotate(45deg);
}
}
.radio__inner {
border-color: $checked-color;
.radio__inner-icon {
opacity: 1;
background-color: $checked-color;
}
}
.checklist-text {
color: $checked-color;
}
// 选中禁用
&.is-disable {
opacity: $disable;
}
}
}
// 标签样式
&.is--tag {
margin-right: 10px;
padding: 5px 10px;
border: 1px $border-color solid;
border-radius: 3px;
background-color: #f5f5f5;
.checklist-text {
margin: 0;
color: #666;
}
// 禁用
&.is-disable {
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
opacity: $disable;
}
&.is-checked {
background-color: $checked-color;
border-color: $checked-color;
.checklist-text {
color: #fff;
}
}
}
// 列表样式
&.is--list {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
padding: 10px 15px;
padding-left: 0;
margin: 0;
&.is-list-border {
border-top: 1px #eee solid;
}
// 禁用
&.is-disable {
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
.checkbox__inner {
background-color: #f2f6fc;
border-color: $border-color;
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
}
.checklist-text {
color: #999;
}
}
&.is-checked {
.checkbox__inner {
border-color: $checked-color;
background-color: $checked-color;
.checkbox__inner-icon {
opacity: 1;
transform: rotate(45deg);
}
}
.radio__inner {
.radio__inner-icon {
opacity: 1;
}
}
.checklist-text {
color: $checked-color;
}
.checklist-content {
.checkobx__list {
opacity: 1;
border-color: $checked-color;
}
}
// 选中禁用
&.is-disable {
.checkbox__inner {
opacity: $disable;
}
.checklist-text {
opacity: $disable;
}
}
}
}
}
}
}
</style>