mirror of
https://gitee.com/wot-design-uni/wot-design-uni.git
synced 2025-12-07 01:28:30 +08:00
refactor: ♻️ 优化Radio和Checkbox组件
This commit is contained in:
parent
b7dad0fb01
commit
f90fd3dcdc
@ -370,6 +370,26 @@
|
|||||||
},
|
},
|
||||||
"navigationBarTitleText": "Tab 标签页"
|
"navigationBarTitleText": "Tab 标签页"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/radio/Index",
|
||||||
|
"name": "radio",
|
||||||
|
"style": {
|
||||||
|
"mp-alipay": {
|
||||||
|
"allowsBounceVertical": "NO"
|
||||||
|
},
|
||||||
|
"navigationBarTitleText": "Radio 单选框"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/checkbox/Index",
|
||||||
|
"name": "checkbox",
|
||||||
|
"style": {
|
||||||
|
"mp-alipay": {
|
||||||
|
"allowsBounceVertical": "NO"
|
||||||
|
},
|
||||||
|
"navigationBarTitleText": "Checkbox 复选框"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
// "tabBar": {
|
// "tabBar": {
|
||||||
|
|||||||
@ -1,7 +1,124 @@
|
|||||||
|
<template>
|
||||||
|
<demo-block title="基本用法">
|
||||||
|
<wd-checkbox v-model:value="check1">京麦</wd-checkbox>
|
||||||
|
</demo-block>
|
||||||
|
|
||||||
<template>
|
<demo-block title="修改形状: square">
|
||||||
</template>
|
<wd-checkbox v-model:value="check2" shape="square">京麦</wd-checkbox>
|
||||||
<script lang="ts" setup>
|
</demo-block>
|
||||||
</script>
|
|
||||||
<style lang="scss" scoped>
|
<demo-block title="修改形状: button">
|
||||||
</style>
|
<wd-checkbox v-model:value="check3" shape="button">京麦</wd-checkbox>
|
||||||
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block title="修改选中颜色">
|
||||||
|
<wd-checkbox v-model:value="check4" checked-color="rgb(52, 209, 157)">京麦</wd-checkbox>
|
||||||
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block title="禁用状态">
|
||||||
|
<view style="margin-bottom: 10px">
|
||||||
|
<wd-checkbox-group v-model="value1" disabled>
|
||||||
|
<wd-checkbox :value="1">京麦</wd-checkbox>
|
||||||
|
<wd-checkbox :value="2" disabled="{{false}}">商家后台</wd-checkbox>
|
||||||
|
<wd-checkbox :value="3" shape="square">京麦</wd-checkbox>
|
||||||
|
<wd-checkbox :value="4" shape="square">商家后台</wd-checkbox>
|
||||||
|
</wd-checkbox-group>
|
||||||
|
</view>
|
||||||
|
<wd-checkbox-group v-model="value2" disabled>
|
||||||
|
<wd-checkbox :value="1" shape="button">京麦</wd-checkbox>
|
||||||
|
<wd-checkbox :value="2" shape="button">商家后台</wd-checkbox>
|
||||||
|
</wd-checkbox-group>
|
||||||
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block :title="`修改 true-value 和 false-value ${value3}`">
|
||||||
|
<wd-checkbox v-model:value="value3" true-value="京麦" false-value="商家后台" @change="handleChange1">复选框</wd-checkbox>
|
||||||
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block title="同行展示">
|
||||||
|
<wd-checkbox-group v-model="value4" inline>
|
||||||
|
<wd-checkbox :value="1">京麦</wd-checkbox>
|
||||||
|
<wd-checkbox :value="2">商家后台</wd-checkbox>
|
||||||
|
</wd-checkbox-group>
|
||||||
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block title="复选框组">
|
||||||
|
<wd-checkbox-group v-model="value5">
|
||||||
|
<wd-checkbox :value="1">京麦</wd-checkbox>
|
||||||
|
<wd-checkbox :value="2">商家后台</wd-checkbox>
|
||||||
|
</wd-checkbox-group>
|
||||||
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block title="表单模式---复选框组" transparent>
|
||||||
|
<wd-checkbox-group v-model="value6" cell>
|
||||||
|
<wd-checkbox :value="1">京麦</wd-checkbox>
|
||||||
|
<wd-checkbox :value="2">商家后台</wd-checkbox>
|
||||||
|
</wd-checkbox-group>
|
||||||
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block title="表单模式---复选框按钮组" transparent>
|
||||||
|
<wd-checkbox-group v-model="value7" cell shape="button">
|
||||||
|
<wd-checkbox :value="1" disabled>选项一</wd-checkbox>
|
||||||
|
<wd-checkbox :value="2">选项二</wd-checkbox>
|
||||||
|
<wd-checkbox :value="3">选项三</wd-checkbox>
|
||||||
|
<wd-checkbox :value="4">选项四</wd-checkbox>
|
||||||
|
<wd-checkbox :value="5">选项五</wd-checkbox>
|
||||||
|
<wd-checkbox :value="6">选项六</wd-checkbox>
|
||||||
|
<wd-checkbox :value="7">选项七</wd-checkbox>
|
||||||
|
</wd-checkbox-group>
|
||||||
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block title="设置最小选中数量和最大选中数量" transparent>
|
||||||
|
<wd-checkbox-group v-model="value8" :min="1" :max="3" cell>
|
||||||
|
<wd-checkbox :value="1">京东</wd-checkbox>
|
||||||
|
<wd-checkbox :value="2">京麦</wd-checkbox>
|
||||||
|
<wd-checkbox :value="3">商家后台</wd-checkbox>
|
||||||
|
<wd-checkbox :value="4">营销中心</wd-checkbox>
|
||||||
|
</wd-checkbox-group>
|
||||||
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block title="大尺寸">
|
||||||
|
<wd-checkbox-group v-model="value9" inline size="large">
|
||||||
|
<wd-checkbox value="jingmai">京麦</wd-checkbox>
|
||||||
|
<wd-checkbox value="shop">商家后台</wd-checkbox>
|
||||||
|
</wd-checkbox-group>
|
||||||
|
<wd-checkbox-group v-model="value10" size="large" class="group">
|
||||||
|
<wd-checkbox value="jingmai">京麦</wd-checkbox>
|
||||||
|
<wd-checkbox value="shop">商家后台</wd-checkbox>
|
||||||
|
</wd-checkbox-group>
|
||||||
|
</demo-block>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
const check1 = ref<boolean>(true)
|
||||||
|
const check2 = ref<boolean>(true)
|
||||||
|
const check3 = ref<boolean>(true)
|
||||||
|
const check4 = ref<boolean>(true)
|
||||||
|
const check5 = ref<boolean>(true)
|
||||||
|
const check6 = ref<boolean>(true)
|
||||||
|
|
||||||
|
const value1 = ref<number[]>([1, 3])
|
||||||
|
const value2 = ref<number[]>([1])
|
||||||
|
const value3 = ref<string>('京麦')
|
||||||
|
|
||||||
|
const value4 = ref<number[]>([1])
|
||||||
|
const value5 = ref<number[]>([])
|
||||||
|
|
||||||
|
const value6 = ref<number[]>([1])
|
||||||
|
const value7 = ref<number[]>([1])
|
||||||
|
const value8 = ref<number[]>([1])
|
||||||
|
const value9 = ref<string[]>([])
|
||||||
|
const value10 = ref<string[]>([])
|
||||||
|
|
||||||
|
function handleChange1(e) {
|
||||||
|
console.log(e)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.group {
|
||||||
|
display: block;
|
||||||
|
margin-top: 10px;
|
||||||
|
padding: 10px 0;
|
||||||
|
border-top: 1px solid rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@ -1,7 +1,138 @@
|
|||||||
|
<template>
|
||||||
|
<demo-block title="基本用法">
|
||||||
|
<view>
|
||||||
|
1、内容项在3项以内,且有比较重要的信息备选(如付款类型选择等)可考虑采用圆形组件。因为会跟圆形复选框容易混淆,且会造成当前表单页页面结构不统一,
|
||||||
|
<text style="color: #f0883a">一般情况不建议使用点状单选。</text>
|
||||||
|
</view>
|
||||||
|
<view style="margin-bottom: 10px">
|
||||||
|
2、单选框基本使用未对高度进行扩充,
|
||||||
|
<text style="color: #f0883a">一般情况建议使用表单--单选组。</text>
|
||||||
|
</view>
|
||||||
|
<wd-radio-group v-model="value0" @change="change">
|
||||||
|
<wd-radio :value="1">单选框1</wd-radio>
|
||||||
|
<wd-radio :value="2">单选框2</wd-radio>
|
||||||
|
</wd-radio-group>
|
||||||
|
</demo-block>
|
||||||
|
|
||||||
<template>
|
<demo-block title="修改形状--button">
|
||||||
</template>
|
<wd-radio-group shape="button" v-model="value1" @change="change">
|
||||||
<script lang="ts" setup>
|
<wd-radio :value="1">京麦</wd-radio>
|
||||||
</script>
|
<wd-radio :value="2">商家后台</wd-radio>
|
||||||
<style lang="scss" scoped>
|
</wd-radio-group>
|
||||||
</style>
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block title="修改形状--dot">
|
||||||
|
<wd-radio-group shape="dot" v-model="value2" @change="change">
|
||||||
|
<wd-radio :value="1">京麦</wd-radio>
|
||||||
|
<wd-radio :value="2">商家后台</wd-radio>
|
||||||
|
</wd-radio-group>
|
||||||
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block title="表单---单选组" transparent>
|
||||||
|
<wd-radio-group cell v-model="value3" @change="change">
|
||||||
|
<wd-radio :value="1">京麦</wd-radio>
|
||||||
|
<wd-radio :value="2">商家后台</wd-radio>
|
||||||
|
</wd-radio-group>
|
||||||
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block title="表单--单选按钮组" transparent>
|
||||||
|
<wd-radio-group v-model="value4" cell shape="button">
|
||||||
|
<wd-radio :value="1">选项一</wd-radio>
|
||||||
|
<wd-radio :value="2">选项二</wd-radio>
|
||||||
|
<wd-radio :value="3">选项三</wd-radio>
|
||||||
|
<wd-radio :value="4">选项四</wd-radio>
|
||||||
|
<wd-radio :value="5">选项五</wd-radio>
|
||||||
|
<wd-radio :value="6">选项六</wd-radio>
|
||||||
|
<wd-radio :value="7">选项七</wd-radio>
|
||||||
|
</wd-radio-group>
|
||||||
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block title="同行展示">
|
||||||
|
<wd-radio-group v-model="value5" inline>
|
||||||
|
<wd-radio :value="1">单选框1</wd-radio>
|
||||||
|
<wd-radio :value="2">单选框2</wd-radio>
|
||||||
|
</wd-radio-group>
|
||||||
|
<view class="divider"></view>
|
||||||
|
<wd-radio-group v-model="value6" inline shape="dot">
|
||||||
|
<wd-radio :value="1">单选框1</wd-radio>
|
||||||
|
<wd-radio :value="2">单选框2</wd-radio>
|
||||||
|
</wd-radio-group>
|
||||||
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block title="修改选中颜色">
|
||||||
|
<wd-radio-group v-model="value7" @change="change">
|
||||||
|
<wd-radio :value="1" checked-color="#fa4350">京麦</wd-radio>
|
||||||
|
<wd-radio :value="2" checked-color="#fa4350">商家后台</wd-radio>
|
||||||
|
</wd-radio-group>
|
||||||
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block title="禁用">
|
||||||
|
<wd-radio-group v-model="value1" disabled shape="dot">
|
||||||
|
<wd-radio :value="1">京麦</wd-radio>
|
||||||
|
<wd-radio :value="2">商家后台</wd-radio>
|
||||||
|
</wd-radio-group>
|
||||||
|
<view class="divider"></view>
|
||||||
|
<wd-radio-group v-model="value1" disabled>
|
||||||
|
<wd-radio :value="1">京麦</wd-radio>
|
||||||
|
<wd-radio :value="2">商家后台</wd-radio>
|
||||||
|
</wd-radio-group>
|
||||||
|
<view class="divider"></view>
|
||||||
|
<wd-radio-group v-model="value1" disabled shape="button">
|
||||||
|
<wd-radio :value="1">京麦</wd-radio>
|
||||||
|
<wd-radio :value="2">商家后台</wd-radio>
|
||||||
|
</wd-radio-group>
|
||||||
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block title="大尺寸">
|
||||||
|
<wd-radio-group v-model="value8" size="large">
|
||||||
|
<wd-radio :value="1">单选框1</wd-radio>
|
||||||
|
<wd-radio :value="2">单选框2</wd-radio>
|
||||||
|
</wd-radio-group>
|
||||||
|
<view class="divider"></view>
|
||||||
|
<wd-radio-group v-model="value9" size="large" shape="dot">
|
||||||
|
<wd-radio :value="1">单选框1</wd-radio>
|
||||||
|
<wd-radio :value="2">单选框2</wd-radio>
|
||||||
|
</wd-radio-group>
|
||||||
|
<view class="divider"></view>
|
||||||
|
<wd-radio-group v-model="value10" size="large" inline custom-class="group">
|
||||||
|
<wd-radio :value="1">单选框1</wd-radio>
|
||||||
|
<wd-radio :value="2">单选框2</wd-radio>
|
||||||
|
</wd-radio-group>
|
||||||
|
</demo-block>
|
||||||
|
|
||||||
|
<demo-block title="radio的props比radioGroup的优先级高">
|
||||||
|
<wd-radio-group hape="button" disabled checked-color="#fa4350" v-model="value11" @change="change">
|
||||||
|
<wd-radio :value="1" checked-color="#000" :disabled="false">商家前端</wd-radio>
|
||||||
|
<wd-radio :value="2" :disabled="false">京麦</wd-radio>
|
||||||
|
<wd-radio :value="3">商家智能</wd-radio>
|
||||||
|
</wd-radio-group>
|
||||||
|
</demo-block>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
const value = ref<number>(1)
|
||||||
|
const value0 = ref<number>(1)
|
||||||
|
const value1 = ref<number>(1)
|
||||||
|
const value2 = ref<number>(1)
|
||||||
|
const value3 = ref<number>(1)
|
||||||
|
const value4 = ref<number>(1)
|
||||||
|
const value5 = ref<number>(1)
|
||||||
|
const value6 = ref<number>(1)
|
||||||
|
const value7 = ref<number>(1)
|
||||||
|
const value8 = ref<number>(1)
|
||||||
|
const value9 = ref<number>(1)
|
||||||
|
const value10 = ref<number>(1)
|
||||||
|
const value11 = ref<number>(1)
|
||||||
|
|
||||||
|
function change(e) {
|
||||||
|
console.log(e)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.divider {
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-top: 1px solid rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@ -6,7 +6,7 @@ VueComponent({
|
|||||||
relations: {
|
relations: {
|
||||||
'../checkbox/index': {
|
'../checkbox/index': {
|
||||||
type: 'descendant',
|
type: 'descendant',
|
||||||
linked (target) {
|
linked(target) {
|
||||||
this.children = this.children || []
|
this.children = this.children || []
|
||||||
this.children.push(target)
|
this.children.push(target)
|
||||||
const index = this.children.indexOf(target)
|
const index = this.children.indexOf(target)
|
||||||
@ -25,8 +25,8 @@ VueComponent({
|
|||||||
prevChild && renderData(prevChild, { isLast: false })
|
prevChild && renderData(prevChild, { isLast: false })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
unlinked (target) {
|
unlinked(target) {
|
||||||
this.children = this.children.filter(child => child !== target)
|
this.children = this.children.filter((child) => child !== target)
|
||||||
const index = this.children.indexOf(target)
|
const index = this.children.indexOf(target)
|
||||||
|
|
||||||
if (this.children.length === 0) return
|
if (this.children.length === 0) return
|
||||||
@ -49,16 +49,16 @@ VueComponent({
|
|||||||
value: {
|
value: {
|
||||||
type: Array,
|
type: Array,
|
||||||
value: [],
|
value: [],
|
||||||
observer (value, oldVal) {
|
observer(value, oldVal) {
|
||||||
// 传入的value数组中包括重复的元素,这种情况非法。
|
// 传入的value数组中包括重复的元素,这种情况非法。
|
||||||
if (new Set(value).size !== value.length) {
|
if (new Set(value).size !== value.length) {
|
||||||
throw Error('checkboxGroup\'s bound value includes same value')
|
throw Error("checkboxGroup's bound value includes same value")
|
||||||
}
|
}
|
||||||
if (value.length < this.data.min) {
|
if (value.length < this.data.min) {
|
||||||
throw Error('checkboxGroup\'s bound value\'s length can\'t be less than min')
|
throw Error("checkboxGroup's bound value's length can't be less than min")
|
||||||
}
|
}
|
||||||
if (this.data.max !== 0 && value.length > this.data.max) {
|
if (this.data.max !== 0 && value.length > this.data.max) {
|
||||||
throw Error('checkboxGroup\'s bound value\'s length can\'t be large than max')
|
throw Error("checkboxGroup's bound value's length can't be large than max")
|
||||||
}
|
}
|
||||||
// 每次value变化都会触发重新匹配选中项
|
// 每次value变化都会触发重新匹配选中项
|
||||||
this.children && this.children.length > 0 && this.resetChildren()
|
this.children && this.children.length > 0 && this.resetChildren()
|
||||||
@ -68,37 +68,39 @@ VueComponent({
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: false,
|
value: false,
|
||||||
// 以下内容用于解决父子组件样式隔离的问题 —— START
|
// 以下内容用于解决父子组件样式隔离的问题 —— START
|
||||||
observer (value) {
|
observer(value) {
|
||||||
this.children && this.children.forEach(child => {
|
this.children &&
|
||||||
child.setData({ cellBox: value })
|
this.children.forEach((child) => {
|
||||||
})
|
child.setData({ cellBox: value })
|
||||||
|
})
|
||||||
}
|
}
|
||||||
// 以下内容用于解决父子组件样式隔离的问题 —— END
|
// 以下内容用于解决父子组件样式隔离的问题 —— END
|
||||||
},
|
},
|
||||||
shape: {
|
shape: {
|
||||||
type: String,
|
type: String,
|
||||||
value: 'circle',
|
value: 'circle',
|
||||||
observer (value) {
|
observer(value) {
|
||||||
const type = ['circle', 'square', 'button']
|
const type = ['circle', 'square', 'button']
|
||||||
if (type.indexOf(value) === -1) throw Error(`shape must be one of ${type.toString()}`)
|
if (type.indexOf(value) === -1) throw Error(`shape must be one of ${type.toString()}`)
|
||||||
this.updateAllChild({ shape: value })
|
this.updateAllChild({ shape: value })
|
||||||
// 以下内容用于解决父子组件样式隔离的问题 —— START
|
// 以下内容用于解决父子组件样式隔离的问题 —— START
|
||||||
this.children && this.children.forEach(child => {
|
this.children &&
|
||||||
child.setData({ buttonBox: value === 'button' })
|
this.children.forEach((child) => {
|
||||||
})
|
child.setData({ buttonBox: value === 'button' })
|
||||||
|
})
|
||||||
// 以下内容用于解决父子组件样式隔离的问题 —— END
|
// 以下内容用于解决父子组件样式隔离的问题 —— END
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
checkedColor: {
|
checkedColor: {
|
||||||
type: String,
|
type: String,
|
||||||
observer (value) {
|
observer(value) {
|
||||||
this.updateAllChild({ checkedColor: value })
|
this.updateAllChild({ checkedColor: value })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
disabled: {
|
disabled: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: null,
|
value: null,
|
||||||
observer () {
|
observer() {
|
||||||
// 当值修改时需要重新检测
|
// 当值修改时需要重新检测
|
||||||
this.resetChildren()
|
this.resetChildren()
|
||||||
}
|
}
|
||||||
@ -106,7 +108,7 @@ VueComponent({
|
|||||||
min: {
|
min: {
|
||||||
type: Number,
|
type: Number,
|
||||||
value: 0,
|
value: 0,
|
||||||
observer (value) {
|
observer(value) {
|
||||||
checkNumRange(value, 'min')
|
checkNumRange(value, 'min')
|
||||||
// 当值修改时需要重新检测
|
// 当值修改时需要重新检测
|
||||||
this.resetChildren()
|
this.resetChildren()
|
||||||
@ -115,7 +117,7 @@ VueComponent({
|
|||||||
max: {
|
max: {
|
||||||
type: Number,
|
type: Number,
|
||||||
value: 0,
|
value: 0,
|
||||||
observer (value) {
|
observer(value) {
|
||||||
checkNumRange(value, 'max')
|
checkNumRange(value, 'max')
|
||||||
// 当值修改时需要重新检测
|
// 当值修改时需要重新检测
|
||||||
this.resetChildren()
|
this.resetChildren()
|
||||||
@ -124,13 +126,13 @@ VueComponent({
|
|||||||
inline: {
|
inline: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: false,
|
value: false,
|
||||||
observer (value) {
|
observer(value) {
|
||||||
this.updateAllChild({ inline: value })
|
this.updateAllChild({ inline: value })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
type: String,
|
type: String,
|
||||||
observer (value) {
|
observer(value) {
|
||||||
this.updateAllChild({ size: value })
|
this.updateAllChild({ size: value })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -140,27 +142,24 @@ VueComponent({
|
|||||||
* @description 当和child建立relation后,用checkboxGroup的props覆盖checkbox中props值为null的属性。
|
* @description 当和child建立relation后,用checkboxGroup的props覆盖checkbox中props值为null的属性。
|
||||||
* @param {Object} data 属性键值对
|
* @param {Object} data 属性键值对
|
||||||
*/
|
*/
|
||||||
updateAllChild (data) {
|
updateAllChild(data) {
|
||||||
const keys = Object.keys(data)
|
const keys = Object.keys(data)
|
||||||
this.children && this.children.forEach(child => {
|
this.children &&
|
||||||
const will = {}
|
this.children.forEach((child) => {
|
||||||
keys.forEach(key => {
|
const will = {}
|
||||||
if (
|
keys.forEach((key) => {
|
||||||
data[key] !== null &&
|
if (data[key] !== null && data[key] !== undefined && child.data[key] === null) {
|
||||||
data[key] !== undefined &&
|
will[key] = data[key]
|
||||||
child.data[key] === null
|
}
|
||||||
) {
|
})
|
||||||
will[key] = data[key]
|
renderData(child, will)
|
||||||
}
|
|
||||||
})
|
})
|
||||||
renderData(child, will)
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @description 子节点通知父节点修改子节点选中状态
|
* @description 子节点通知父节点修改子节点选中状态
|
||||||
* @param {any} value 子组件的标识符
|
* @param {any} value 子组件的标识符
|
||||||
*/
|
*/
|
||||||
changeSelectState (value) {
|
changeSelectState(value) {
|
||||||
const temp = this.data.value
|
const temp = this.data.value
|
||||||
const index = temp.indexOf(value)
|
const index = temp.indexOf(value)
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
@ -183,17 +182,18 @@ VueComponent({
|
|||||||
* @description 修正子组件的 isChecked 和 finalDisabled
|
* @description 修正子组件的 isChecked 和 finalDisabled
|
||||||
* @param {array} values
|
* @param {array} values
|
||||||
*/
|
*/
|
||||||
resetChildren (values) {
|
resetChildren(values) {
|
||||||
values = values || this.data.value
|
values = values || this.data.value
|
||||||
this.children && this.children.forEach(child => {
|
this.children &&
|
||||||
// value 对应的节点直接选中
|
this.children.forEach((child) => {
|
||||||
const isChecked = values.indexOf(child.data.value) > -1
|
// value 对应的节点直接选中
|
||||||
renderData(child, { isChecked })
|
const isChecked = values.indexOf(child.data.value) > -1
|
||||||
child.checkDisabled()
|
renderData(child, { isChecked })
|
||||||
})
|
child.checkDisabled()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeCreate () {
|
beforeCreate() {
|
||||||
// 设置防抖,避免修改 props(min, max, disabled) 触发多次
|
// 设置防抖,避免修改 props(min, max, disabled) 触发多次
|
||||||
this.resetChildren = debounce(this.resetChildren, 50)
|
this.resetChildren = debounce(this.resetChildren, 50)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,176 @@
|
|||||||
<template></template>
|
<template>
|
||||||
|
<view :class="`wd-checkbox-group ${shape === 'button' && cell ? 'is-button' : ''} ${customClass}`">
|
||||||
|
<slot />
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
<script></script>
|
<script lang="ts" setup>
|
||||||
|
import { getCurrentInstance, provide, watch } from 'vue'
|
||||||
|
import { checkNumRange, debounce, deepClone } from '../common/util'
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
type checkShape = 'circle' | 'square' | 'button'
|
||||||
|
interface Props {
|
||||||
|
customClass?: string
|
||||||
|
modelValue: Array<string | number | boolean>
|
||||||
|
cell: boolean | null
|
||||||
|
shape: checkShape
|
||||||
|
checkedColor: string
|
||||||
|
disabled: boolean | null
|
||||||
|
min: number
|
||||||
|
max: number
|
||||||
|
inline: boolean | null
|
||||||
|
size: string
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
customClass: '',
|
||||||
|
value: () => [],
|
||||||
|
shape: 'circle',
|
||||||
|
checkedColor: '#4D80F0',
|
||||||
|
disabled: false,
|
||||||
|
min: 0,
|
||||||
|
max: 0,
|
||||||
|
inline: false
|
||||||
|
})
|
||||||
|
|
||||||
|
const children: any[] = [] // 子组件
|
||||||
|
const { proxy } = getCurrentInstance() as any
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 修正子组件的 isChecked 和 finalDisabled
|
||||||
|
* @param {array} values
|
||||||
|
*/
|
||||||
|
const resetChildren = debounce(function (values) {
|
||||||
|
values = values || props.modelValue
|
||||||
|
children &&
|
||||||
|
children.forEach((child) => {
|
||||||
|
// value 对应的节点直接选中
|
||||||
|
const isChecked = values.indexOf(child.value) > -1
|
||||||
|
child.$.exposed.setChecked(isChecked)
|
||||||
|
})
|
||||||
|
}, 50)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(newValue) => {
|
||||||
|
// 传入的value数组中包括重复的元素,这种情况非法。
|
||||||
|
if (new Set(newValue).size !== newValue.length) {
|
||||||
|
// eslint-disable-next-line quotes
|
||||||
|
throw Error("checkboxGroup's bound value includes same value")
|
||||||
|
}
|
||||||
|
if (newValue.length < props.min) {
|
||||||
|
// eslint-disable-next-line quotes
|
||||||
|
throw Error("checkboxGroup's bound value's length can't be less than min")
|
||||||
|
}
|
||||||
|
if (props.max !== 0 && newValue.length > props.max) {
|
||||||
|
// eslint-disable-next-line quotes
|
||||||
|
throw Error("checkboxGroup's bound value's length can't be large than max")
|
||||||
|
}
|
||||||
|
// 每次value变化都会触发重新匹配选中项
|
||||||
|
children && children.length > 0 && resetChildren()
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.shape,
|
||||||
|
(newValue) => {
|
||||||
|
const type = ['circle', 'square', 'button']
|
||||||
|
if (type.indexOf(newValue) === -1) throw Error(`shape must be one of ${type.toString()}`)
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
[() => props.disabled, () => props.inline, () => props.size],
|
||||||
|
() => {
|
||||||
|
resetChildren()
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.min,
|
||||||
|
(newValue) => {
|
||||||
|
checkNumRange(newValue, 'min')
|
||||||
|
resetChildren()
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.max,
|
||||||
|
(newValue) => {
|
||||||
|
checkNumRange(newValue, 'max')
|
||||||
|
resetChildren()
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
const emit = defineEmits(['change', 'update:modelValue'])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置子项
|
||||||
|
* @param child
|
||||||
|
*/
|
||||||
|
function setChild(child) {
|
||||||
|
const hasChild = children.findIndex((check) => {
|
||||||
|
return check.$.uid === child.$.uid
|
||||||
|
})
|
||||||
|
if (hasChild <= -1) {
|
||||||
|
children.push(child)
|
||||||
|
} else {
|
||||||
|
children[hasChild] = child
|
||||||
|
}
|
||||||
|
|
||||||
|
const index = children.findIndex((check) => {
|
||||||
|
return check.$.uid === child.$.uid
|
||||||
|
})
|
||||||
|
// 如果当前子节点为第一个组件,将其isFirst设置为true
|
||||||
|
if (index === 0) {
|
||||||
|
child.$.exposed.setFirst(true)
|
||||||
|
}
|
||||||
|
// 如果当前子节点为最后一个组件,将其isLast设置为true,删掉倒数第二个子节点的isLast
|
||||||
|
if (index === children.length - 1) {
|
||||||
|
child.$.exposed.setLast(true)
|
||||||
|
const [prevChild] = children.slice(-2, -1)
|
||||||
|
prevChild && prevChild.$.exposed.setLast(false)
|
||||||
|
}
|
||||||
|
resetChildren()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 子节点通知父节点修改子节点选中状态
|
||||||
|
* @param {any} value 子组件的标识符
|
||||||
|
*/
|
||||||
|
function changeSelectState(value) {
|
||||||
|
const temp: (string | number | boolean)[] = deepClone(props.modelValue)
|
||||||
|
const index = temp.indexOf(value)
|
||||||
|
if (index > -1) {
|
||||||
|
// 已经选中,则从 value 列表中删除子节点的标识符。
|
||||||
|
temp.splice(index, 1)
|
||||||
|
} else {
|
||||||
|
// 之前未选中,则现在把加子节点的标识符加到 value 列表中。
|
||||||
|
temp.push(value)
|
||||||
|
}
|
||||||
|
emit('update:modelValue', temp)
|
||||||
|
// 操作完之后更新一下 所有节点的 disabled 状态
|
||||||
|
resetChildren(temp)
|
||||||
|
emit('change', {
|
||||||
|
value: temp
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
provide('checkGroup', proxy)
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
setChild,
|
||||||
|
changeSelectState,
|
||||||
|
children
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import './index.scss';
|
||||||
|
</style>
|
||||||
|
|||||||
@ -36,7 +36,7 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
@include e(btn-check) {
|
@include edeep(btn-check) {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-size: $-checkbox-icon-size;
|
font-size: $-checkbox-icon-size;
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
@ -58,7 +58,7 @@
|
|||||||
font-size: $-checkbox-label-fs;
|
font-size: $-checkbox-label-fs;
|
||||||
color: $-checkbox-label-color;
|
color: $-checkbox-label-color;
|
||||||
}
|
}
|
||||||
@include e(check) {
|
@include edeep(check) {
|
||||||
color: $-checkbox-check-color;
|
color: $-checkbox-check-color;
|
||||||
font-size: $-checkbox-icon-size;
|
font-size: $-checkbox-icon-size;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
@ -70,7 +70,7 @@
|
|||||||
background: currentColor;
|
background: currentColor;
|
||||||
border-color: currentColor;
|
border-color: currentColor;
|
||||||
}
|
}
|
||||||
.wd-checkbox__check {
|
:deep(.wd-checkbox__check) {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
@ -195,7 +195,7 @@
|
|||||||
.wd-checkbox__label {
|
.wd-checkbox__label {
|
||||||
font-size: $-checkbox-large-label-fs;
|
font-size: $-checkbox-large-label-fs;
|
||||||
}
|
}
|
||||||
.wd-checkbox__check {
|
:deep(.wd-checkbox__check) {
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 1px;
|
left: 1px;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,251 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<view
|
||||||
|
:class="`wd-checkbox ${innerCell ? 'is-cell-box' : ''} ${innerShape === 'button' ? 'is-button-box' : ''} ${isChecked ? 'is-checked' : ''} ${
|
||||||
|
isFirst ? 'is-first-child' : ''
|
||||||
|
} ${isLast ? 'is-last-child' : ''} ${innerInline ? 'is-inline' : ''} ${innerShape === 'button' ? 'is-button' : ''} ${
|
||||||
|
innerDisabled ? 'is-disabled' : ''
|
||||||
|
} ${innerSize ? 'is-' + innerSize : ''} ${customClass}`"
|
||||||
|
@click="toggle"
|
||||||
|
>
|
||||||
|
<!--shape为button时,移除wd-checkbox__shape,只保留wd-checkbox__label-->
|
||||||
|
<view
|
||||||
|
v-if="innerShape !== 'button'"
|
||||||
|
:class="`wd-checkbox__shape ${innerShape === 'square' ? 'is-square' : ''} ${customShapeClass}`"
|
||||||
|
:style="isChecked && !innerDisabled && innerCheckedColor ? 'color :' + innerCheckedColor : ''"
|
||||||
|
>
|
||||||
|
<wd-icon custom-class="wd-checkbox__check" name="check-bold" size="14px" color="#ffffff" />
|
||||||
|
</view>
|
||||||
|
<!--shape为button时只保留wd-checkbox__label-->
|
||||||
|
<view
|
||||||
|
:class="`wd-checkbox__label ${customLabelClass}`"
|
||||||
|
:style="isChecked && innerShape === 'button' && !innerDisabled && innerCheckedColor ? 'color:' + innerCheckedColor : ''"
|
||||||
|
>
|
||||||
|
<!--button选中时展示的icon-->
|
||||||
|
<wd-icon v-if="innerShape === 'button' && isChecked" custom-class="wd-checkbox__btn-check" name="check-bold" size="14px" />
|
||||||
|
<!--文案-->
|
||||||
|
<view class="wd-checkbox__txt" :style="maxWidth ? 'max-width:' + maxWidth : ''">
|
||||||
|
<slot></slot>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
options: {
|
||||||
|
virtualHost: true,
|
||||||
|
styleIsolation: 'shared'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, getCurrentInstance, inject, onBeforeMount, onMounted, ref, watch } from 'vue'
|
||||||
|
|
||||||
|
type checkShape = 'circle' | 'square' | 'button'
|
||||||
|
interface Props {
|
||||||
|
customLabelClass?: string
|
||||||
|
customShapeClass?: string
|
||||||
|
customClass?: string
|
||||||
|
value: string | number | boolean
|
||||||
|
shape: checkShape
|
||||||
|
checkedColor: string
|
||||||
|
disabled: boolean | null
|
||||||
|
trueValue: string | number | boolean
|
||||||
|
falseValue: string | number | boolean
|
||||||
|
size: string
|
||||||
|
maxWidth: string
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
customLabelClass: '',
|
||||||
|
customShapeClass: '',
|
||||||
|
customClass: '',
|
||||||
|
trueValue: true,
|
||||||
|
falseValue: false,
|
||||||
|
disabled: null
|
||||||
|
})
|
||||||
|
|
||||||
|
const isChecked = ref<boolean>(false) // 是否被选中
|
||||||
|
|
||||||
|
const inited = ref<boolean>(false)
|
||||||
|
// 相同组件的伪类选择器无效,这里配合类名手动模拟 last-child、first-child
|
||||||
|
const isFirst = ref<boolean>(false)
|
||||||
|
const isLast = ref<boolean>(false)
|
||||||
|
const parent = inject<any>('checkGroup')
|
||||||
|
const { proxy } = getCurrentInstance() as any
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.value,
|
||||||
|
(newValue) => {
|
||||||
|
if (newValue === null || newValue === undefined) {
|
||||||
|
// eslint-disable-next-line prettier/prettier
|
||||||
|
throw Error('checkbox\'s value can\'t be null or undefined')
|
||||||
|
}
|
||||||
|
if (!inited.value) return
|
||||||
|
// 组合使用走这个逻辑
|
||||||
|
if (parent && parent.$.exposed.resetChildren) {
|
||||||
|
checkName()
|
||||||
|
return parent.$.exposed.resetChildren()
|
||||||
|
}
|
||||||
|
isChecked.value = newValue === props.trueValue
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.shape,
|
||||||
|
(newValue) => {
|
||||||
|
const type = ['circle', 'square', 'button']
|
||||||
|
if (type.indexOf(newValue) === -1) throw Error(`shape must be one of ${type.toString()}`)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const innerShape = computed(() => {
|
||||||
|
if (!props.shape && parent && parent.shape) {
|
||||||
|
return parent.shape
|
||||||
|
} else {
|
||||||
|
return props.shape
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const innerCheckedColor = computed(() => {
|
||||||
|
if (!props.checkedColor && parent && parent.checkedColor) {
|
||||||
|
return parent.checkedColor
|
||||||
|
} else {
|
||||||
|
return props.checkedColor
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const innerDisabled = computed(() => {
|
||||||
|
let innerDisabled = props.disabled
|
||||||
|
if (parent) {
|
||||||
|
if (
|
||||||
|
// max 生效时,group 已经选满,禁止其它节点再选中。
|
||||||
|
(parent.max && parent.modelValue.length >= parent.max && !isChecked.value) ||
|
||||||
|
// min 生效时,group 选中的节点数量仅满足最小值,禁止取消已选中的节点。
|
||||||
|
(parent.min && parent.modelValue.length <= parent.min && isChecked.value) ||
|
||||||
|
// 只要子节点自己要求 disabled,那就 disabled。
|
||||||
|
props.disabled === true ||
|
||||||
|
// 父节点要求全局 disabled,子节点没吱声,那就 disabled。
|
||||||
|
(parent.disabled && props.disabled === null)
|
||||||
|
) {
|
||||||
|
innerDisabled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return innerDisabled
|
||||||
|
})
|
||||||
|
|
||||||
|
const innerInline = computed(() => {
|
||||||
|
if (parent && parent.inline) {
|
||||||
|
return parent.inline
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const innerCell = computed(() => {
|
||||||
|
if (parent && parent.cell) {
|
||||||
|
return parent.cell
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const innerSize = computed(() => {
|
||||||
|
if (!props.size && parent && parent.size) {
|
||||||
|
return parent.size
|
||||||
|
} else {
|
||||||
|
return props.size
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
// eslint-disable-next-line quotes
|
||||||
|
if (props.value === null) throw Error("checkbox's value must be set")
|
||||||
|
inited.value = true
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 如果没有父组件,设置 isChecked
|
||||||
|
if (!parent) {
|
||||||
|
isChecked.value = props.value === props.trueValue
|
||||||
|
isFirst.value = true
|
||||||
|
isLast.value = true
|
||||||
|
}
|
||||||
|
if (parent) {
|
||||||
|
parent.$.exposed.setChild && parent.$.exposed.setChild(proxy)
|
||||||
|
isChecked.value = props.value === parent.modelValue
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['change', 'update:value'])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 检测checkbox绑定的value是否和其它checkbox的value冲突
|
||||||
|
* @param {Object} self 自身
|
||||||
|
* @param myName 自己的标识符
|
||||||
|
*/
|
||||||
|
function checkName() {
|
||||||
|
parent &&
|
||||||
|
parent.$.exposed.children &&
|
||||||
|
parent.$.exposed.children.forEach((child) => {
|
||||||
|
if (child.$.uid !== proxy.$.uid && child.value === props.value) {
|
||||||
|
throw Error(`The checkbox's bound value: ${props.value} has been used`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @description 点击checkbox的Event handle
|
||||||
|
*/
|
||||||
|
function toggle() {
|
||||||
|
if (innerDisabled.value) return
|
||||||
|
// 复选框单独使用时点击反选,并且在checkbox上触发change事件
|
||||||
|
if (parent) {
|
||||||
|
emit('change', {
|
||||||
|
value: !isChecked.value
|
||||||
|
})
|
||||||
|
parent.$.exposed.changeSelectState(props.value)
|
||||||
|
} else {
|
||||||
|
const newVal = props.value === props.trueValue ? props.falseValue : props.trueValue
|
||||||
|
emit('update:value', newVal)
|
||||||
|
emit('change', {
|
||||||
|
value: newVal
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置是否为第一个
|
||||||
|
* @param first
|
||||||
|
*/
|
||||||
|
function setFirst(first: boolean) {
|
||||||
|
isFirst.value = first
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置是否为最后一个
|
||||||
|
* @param first
|
||||||
|
*/
|
||||||
|
function setLast(last: boolean) {
|
||||||
|
isLast.value = last
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置是否选中
|
||||||
|
* @param checked 是否选中
|
||||||
|
*/
|
||||||
|
function setChecked(checked: boolean) {
|
||||||
|
isChecked.value = checked
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
setFirst,
|
||||||
|
setLast,
|
||||||
|
setChecked
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@import './index.scss';
|
||||||
</style>
|
</style>
|
||||||
@ -1,6 +1,126 @@
|
|||||||
<template>
|
<template>
|
||||||
</template>
|
<view :class="`wd-radio-group ${customClass} ${cell && shape === 'button' ? 'is-button' : ''}`">
|
||||||
<script>
|
<slot />
|
||||||
</script>
|
</view>
|
||||||
<style lang="scss" scoped>
|
</template>
|
||||||
</style>
|
<script lang="ts" setup>
|
||||||
|
import { getCurrentInstance, provide, watch } from 'vue'
|
||||||
|
|
||||||
|
type RadioShape = 'dot' | 'button' | 'check'
|
||||||
|
interface Props {
|
||||||
|
customClass?: string
|
||||||
|
modelValue: string | number | boolean
|
||||||
|
shape: RadioShape
|
||||||
|
checkedColor: string
|
||||||
|
disabled: boolean
|
||||||
|
cell: boolean
|
||||||
|
size: string
|
||||||
|
inline: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
customClass: '',
|
||||||
|
shape: 'check',
|
||||||
|
size: '',
|
||||||
|
checkedColor: '#4D80F0',
|
||||||
|
disabled: false,
|
||||||
|
inline: false,
|
||||||
|
cell: false
|
||||||
|
})
|
||||||
|
|
||||||
|
const children: any[] = [] // 子组件
|
||||||
|
const { proxy } = getCurrentInstance() as any
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(newValue, oldValue) => {
|
||||||
|
// 类型校验,支持所有值(除null、undefined。undefined建议统一写成void (0)防止全局undefined被覆盖)
|
||||||
|
if (newValue === null || newValue === undefined) {
|
||||||
|
// eslint-disable-next-line quotes
|
||||||
|
throw Error("value can't be null or undefined")
|
||||||
|
}
|
||||||
|
// prop初始化watch执行时,relations关系还没有建立,所以ready之后手动执行一下
|
||||||
|
if (oldValue !== null) {
|
||||||
|
// radioGroup绑定的value变化,,立即切换到此value对应的radio
|
||||||
|
changeSelect(newValue)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.shape,
|
||||||
|
(newValue) => {
|
||||||
|
// type: 'dot', 'button', 'check'
|
||||||
|
const type = ['check', 'dot', 'button']
|
||||||
|
if (type.indexOf(newValue) === -1) throw Error(`shape must be one of ${type.toString()}`)
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
const emit = defineEmits(['change', 'update:modelValue'])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置子项
|
||||||
|
* @param child
|
||||||
|
*/
|
||||||
|
function setChild(child) {
|
||||||
|
checkValue(child)
|
||||||
|
const hasChild = children.findIndex((radio) => {
|
||||||
|
return radio.$.uid === child.$.uid
|
||||||
|
})
|
||||||
|
if (hasChild <= -1) {
|
||||||
|
children.push(child)
|
||||||
|
} else {
|
||||||
|
children[hasChild] = child
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 检测radio绑定的value是否已经被其他节点绑定
|
||||||
|
*/
|
||||||
|
function checkValue(child) {
|
||||||
|
children.forEach((radio) => {
|
||||||
|
const value = child.value
|
||||||
|
if (radio.$.uid !== child.$.uid && radio.value === value) {
|
||||||
|
throw Error(`The radio's bound value: ${value} has been used `)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 修改选中的radio
|
||||||
|
* @param value - radio绑定的value
|
||||||
|
* @param old - 老节点,默认为已经被选中的节点
|
||||||
|
*/
|
||||||
|
function changeSelect(value) {
|
||||||
|
// 没有radio子元素,不执行任何操作
|
||||||
|
if (!children || children.length === 0 || value === null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
children.forEach((child) => {
|
||||||
|
child.$.exposed.setChecked(child.value === value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @description 处理radio子节点通知
|
||||||
|
*/
|
||||||
|
function handleClick(value) {
|
||||||
|
emit('update:modelValue', value)
|
||||||
|
emit('change', {
|
||||||
|
value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
setChild,
|
||||||
|
handleClick,
|
||||||
|
changeSelect,
|
||||||
|
checkValue,
|
||||||
|
children
|
||||||
|
})
|
||||||
|
|
||||||
|
provide('radioGroup', proxy)
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import './index.scss';
|
||||||
|
</style>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<!--
|
<!--
|
||||||
* @Author: weisheng
|
* @Author: weisheng
|
||||||
* @Date: 2023-06-12 18:40:58
|
* @Date: 2023-06-12 18:40:58
|
||||||
* @LastEditTime: 2023-07-01 18:13:00
|
* @LastEditTime: 2023-07-10 10:55:11
|
||||||
* @LastEditors: weisheng
|
* @LastEditors: weisheng
|
||||||
* @Description:
|
* @Description:
|
||||||
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\wd-radio\wd-radio.vue
|
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\wd-radio\wd-radio.vue
|
||||||
@ -9,17 +9,23 @@
|
|||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<view
|
<view
|
||||||
class="wd-radio {{cell ? 'is-cell-radio':''}} {{cell && shape == 'button' ? 'is-button-radio':''}} {{ size && ('is-' + size) }} {{ inline ? 'is-inline' : '' }} {{ isChecked ? 'is-checked' : '' }} {{shape !== 'check' ? 'is-'+shape : '' }} {{ disabled ? 'is-disabled' : ''}} custom-class"
|
:class="`wd-radio ${innerCell ? 'is-cell-radio' : ''} ${innerCell && innerShape == 'button' ? 'is-button-radio' : ''} ${
|
||||||
bindtap="handleClick"
|
innerSize ? 'is-' + innerSize : ''
|
||||||
|
} ${innerInline ? 'is-inline' : ''} ${isChecked ? 'is-checked' : ''} ${innerShape !== 'check' ? 'is-' + innerShape : ''} ${
|
||||||
|
innerDisabled ? 'is-disabled' : ''
|
||||||
|
} ${customClass}`"
|
||||||
|
@click="handleClick"
|
||||||
>
|
>
|
||||||
<view
|
<view
|
||||||
class="wd-radio__label"
|
class="wd-radio__label"
|
||||||
style="{{ maxWidth ? 'max-width:'+ maxWidth : '' }}; {{(isChecked && shape === 'button' && !disabled ) ? 'color :' + checkedColor : '' }} "
|
:style="`${maxWidth ? 'max-width:' + maxWidth : ''}; ${
|
||||||
|
isChecked && innerShape === 'button' && !innerDisabled ? 'color :' + innerCheckedColor : ''
|
||||||
|
}`"
|
||||||
>
|
>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</view>
|
</view>
|
||||||
<view class="wd-radio__shape" style="{{ (isChecked && !disabled) ? 'color: ' + checkedColor : '' }}">
|
<view class="wd-radio__shape" :style="isChecked && !disabled ? 'color: ' + innerCheckedColor : ''">
|
||||||
<wd-icon v-if="{{shape === 'check'}}" style="{{ (isChecked && !disabled) ? 'color: ' + checkedColor : '' }}" name="check"></wd-icon>
|
<wd-icon v-if="innerShape === 'check'" :style="isChecked && !disabled ? 'color: ' + innerCheckedColor : ''" name="check"></wd-icon>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
@ -31,7 +37,142 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<script lang="ts" setup></script>
|
<script lang="ts" setup>
|
||||||
|
import { computed, getCurrentInstance, inject, onBeforeMount, ref, watch } from 'vue'
|
||||||
|
|
||||||
|
type RadioShape = 'dot' | 'button' | 'check'
|
||||||
|
interface Props {
|
||||||
|
customClass?: string
|
||||||
|
value: string | number | boolean
|
||||||
|
shape: RadioShape
|
||||||
|
checkedColor: string
|
||||||
|
disabled: boolean | null
|
||||||
|
cell: boolean | null
|
||||||
|
size: string
|
||||||
|
inline: boolean | null
|
||||||
|
maxWidth: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
customClass: '',
|
||||||
|
disabled: null,
|
||||||
|
cell: null,
|
||||||
|
inline: null
|
||||||
|
})
|
||||||
|
|
||||||
|
const isChecked = ref<boolean>(false) // 是否被选中
|
||||||
|
const parent = inject<any>('radioGroup') || {}
|
||||||
|
const { proxy } = getCurrentInstance() as any
|
||||||
|
|
||||||
|
const innerShape = computed(() => {
|
||||||
|
if (!props.shape && parent && parent.shape) {
|
||||||
|
return parent.shape
|
||||||
|
} else {
|
||||||
|
return props.shape
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const innerCheckedColor = computed(() => {
|
||||||
|
if (!props.checkedColor && parent && parent.checkedColor) {
|
||||||
|
return parent.checkedColor
|
||||||
|
} else {
|
||||||
|
return props.checkedColor
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const innerDisabled = computed(() => {
|
||||||
|
if ((props.disabled === null || props.disabled === undefined) && parent && parent.disabled) {
|
||||||
|
return parent.disabled
|
||||||
|
} else {
|
||||||
|
return props.disabled
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const innerInline = computed(() => {
|
||||||
|
if ((props.inline === null || props.inline === undefined) && parent && parent.inline) {
|
||||||
|
return parent.inline
|
||||||
|
} else {
|
||||||
|
return props.inline
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const innerSize = computed(() => {
|
||||||
|
if (!props.size && parent && parent.size) {
|
||||||
|
return parent.size
|
||||||
|
} else {
|
||||||
|
return props.size
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const innerCell = computed(() => {
|
||||||
|
if ((props.cell === null || props.cell === undefined) && parent && parent.cell) {
|
||||||
|
return parent.cell
|
||||||
|
} else {
|
||||||
|
return props.cell
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.value,
|
||||||
|
(newValue) => {
|
||||||
|
// 类型校验,支持所有值(除null、undefined。undefined建议统一写成void (0)防止全局undefined被覆盖)
|
||||||
|
if (newValue === null || newValue === undefined) {
|
||||||
|
// eslint-disable-next-line quotes
|
||||||
|
throw Error("value can't be null or undefined")
|
||||||
|
}
|
||||||
|
// 当建立relations关系之后,radio的value改变,以下内容才能执行
|
||||||
|
if (!parent || newValue === null) return
|
||||||
|
// 检查自己绑定的值是否和其它radio冲突
|
||||||
|
parent.$.exposed.checkValue(proxy)
|
||||||
|
// 会判断新value是否和radioGroup的value一致,一致就会调用radio的方法选中此节点。
|
||||||
|
// 如果之前本节点被选中,匹配不上还要主动关闭自己
|
||||||
|
if (newValue === parent.modelValue) {
|
||||||
|
parent.$.exposed.changeSelect(newValue)
|
||||||
|
} else {
|
||||||
|
isChecked.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.shape,
|
||||||
|
(newValue) => {
|
||||||
|
// type: 'dot', 'button', 'check'
|
||||||
|
const type = ['check', 'dot', 'button']
|
||||||
|
if (type.indexOf(newValue) === -1) throw Error(`shape must be one of ${type.toString()}`)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
if (parent) {
|
||||||
|
parent.$.exposed.setChild && parent.$.exposed.setChild(proxy)
|
||||||
|
isChecked.value = props.value === parent.modelValue
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 点击子元素,通知父元素触发change事件
|
||||||
|
*/
|
||||||
|
function handleClick() {
|
||||||
|
const { value } = props
|
||||||
|
if (!innerDisabled.value && parent && value !== null && value !== undefined) {
|
||||||
|
parent.$.exposed.handleClick(value)
|
||||||
|
// this.parent.setData({ value })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置选中状态
|
||||||
|
* @param checked 选中状态
|
||||||
|
*/
|
||||||
|
function setChecked(checked: boolean) {
|
||||||
|
isChecked.value = checked
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
setChecked
|
||||||
|
})
|
||||||
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import './index.scss';
|
@import './index.scss';
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user