You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

250 lines
6.6 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<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" />
</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>
<script lang="ts">
export default {
name: 'wd-checkbox',
options: {
addGlobalClass: true,
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
modelValue: string | number | boolean
shape?: checkShape
checkedColor?: string
disabled?: boolean | null
trueValue?: string | number | boolean
falseValue?: string | number | boolean
size?: string
maxWidth?: string
}
const props = withDefaults(defineProps<Props>(), {
customLabelClass: '',
customShapeClass: '',
customClass: '',
shape: 'circle',
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', null)
const { proxy } = getCurrentInstance() as any
watch(
() => props.modelValue,
(newValue) => {
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) console.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.modelValue === null) console.error("checkbox's value must be set")
inited.value = true
})
onMounted(() => {
// 如果没有父组件,设置 isChecked
if (!parent) {
isChecked.value = props.modelValue === props.trueValue
isFirst.value = true
isLast.value = true
}
if (parent) {
parent.$.exposed.setChild && parent.$.exposed.setChild(proxy)
isChecked.value = props.modelValue === parent.modelValue
}
})
const emit = defineEmits(['change', 'update:modelValue'])
/**
* @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.modelValue) {
console.error(`The checkbox's bound value: ${props.modelValue} 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.modelValue)
} else {
const newVal = props.modelValue === props.trueValue ? props.falseValue : props.trueValue
emit('update:modelValue', 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>
<style lang="scss" scoped>
@import './index.scss';
</style>