提交 3a7ba44b 编写于 作者: D DCloud_LXH

feat: 1. radio、radio-group 2. chore checkbox

上级 cde391e2
<template>
<view class="content">
<image class="logo" src="/static/logo.png"></image>
<!-- <image class="logo" src="/static/logo.png"></image> -->
<view class="text-area">
<text class="title">{{ title }}</text>
</view>
......
......@@ -8,6 +8,7 @@ import {
} from 'vue'
import type { Ref } from 'vue'
import { useListeners } from '../../helpers/useListeners'
import { useBooleanAttr } from '../../helpers/useBooleanAttr'
import { UniCheckGroupCtx, uniCheckGroupKey } from '../checkbox-group'
import { UniFormCtx, uniFormKey } from '../form'
import { uniLabelKey, UniLabelCtx } from '../label'
......@@ -72,24 +73,30 @@ export default /*#__PURE__*/ defineComponent({
uniCheckGroup && uniCheckGroup.checkboxChange($event)
}
uniLabel.addHandler(_onClick)
onBeforeUnmount(() => {
uniLabel.removeHandler(_onClick)
})
if (!!uniLabel) {
uniLabel.addHandler(_onClick)
onBeforeUnmount(() => {
uniLabel.removeHandler(_onClick)
})
}
useListeners(props, { 'label-click': _onClick })
return () => {
const { disabled, color } = props
const { booleanAttrs } = useBooleanAttr(props, 'disabled')
return (
<uni-checkbox disabled={disabled} onClick={_onClick}>
<uni-checkbox {...booleanAttrs} onClick={_onClick}>
<div class="uni-checkbox-wrapper">
<div
class="uni-checkbox-input"
class={{ 'uni-checkbox-input-disabled': disabled }}
class={{ 'uni-checkbox-input-disabled': props.disabled }}
>
{checkboxChecked.value
? createSvgIconVNode(ICON_PATH_SUCCESS_NO_CIRCLE, color, 16)
? createSvgIconVNode(
ICON_PATH_SUCCESS_NO_CIRCLE,
props.color,
22
)
: ''}
</div>
{slots.default && slots.default()}
......@@ -105,7 +112,7 @@ function useCheckboxInject(
checkboxValue: Ref<string>,
reset: () => void
) {
const filed = computed(() => ({
const field = computed(() => ({
checkboxChecked: Boolean(checkboxChecked.value),
value: checkboxValue.value,
}))
......@@ -116,7 +123,7 @@ function useCheckboxInject(
(false as unknown) as UniCheckGroupCtx
)
if (!!uniCheckGroup) {
uniCheckGroup.addField(filed)
uniCheckGroup.addField(field)
}
const uniForm = inject<UniFormCtx>(
......@@ -133,7 +140,7 @@ function useCheckboxInject(
)
onBeforeUnmount(() => {
uniCheckGroup && uniCheckGroup.removeField(filed)
uniCheckGroup && uniCheckGroup.removeField(field)
uniForm && uniForm.removeField(formField)
})
......
......@@ -15,8 +15,8 @@ import Navigator from './navigator/index.vue'
// import PickerView from './picker-view/index.vue'
// import PickerViewColumn from './picker-view-column/index.vue'
import Progress from './progress/index'
import Radio from './radio/index.vue'
import RadioGroup from './radio-group/index.vue'
import Radio from './radio/index'
import RadioGroup from './radio-group/index'
import ResizeSensor from './resize-sensor/index'
import RichText from './rich-text/index.vue'
import ScrollView from './scroll-view/index.vue'
......
......@@ -27,14 +27,11 @@ export default /*#__PURE__*/ defineComponent({
EventTarget.className
)
if (!stopPropagation) {
stopPropagation = /^uni-(checkbox|radio|switch|button|svg)$/i.test(
// 现在checkbox图标已经改为svg实现,svg和path都跳过
stopPropagation = /^uni-(checkbox|radio|switch|button)$|^(svg|path)$/i.test(
EventTarget.tagName
)
}
// 现在checkbox图标已经改为svg实现,svg和path都跳过
if (!stopPropagation) {
stopPropagation = /^(svg|path)$/i.test(EventTarget.tagName)
}
if (stopPropagation) {
return
}
......
import { defineComponent, inject, provide, ref, onMounted } from 'vue'
import type { Ref, ExtractPropTypes, WritableComputedRef } from 'vue'
import { PolySymbol } from '@dcloudio/uni-core'
import { UniFormCtx, uniFormKey } from '../form'
import { CustomEventTrigger, useCustomEvent } from '../../helpers/useEvent'
export const uniRadioGroupKey = PolySymbol(__DEV__ ? 'uniCheckGroup' : 'ucg')
type UniRadioGroupFieldCtx = WritableComputedRef<{
radioChecked: boolean
value: string
}>
export interface UniRadioGroupCtx {
addField: (field: UniRadioGroupFieldCtx) => void
removeField: (field: UniRadioGroupFieldCtx) => void
radioChange: ($event: Event, field: UniRadioGroupFieldCtx) => void
}
const props = {
name: {
type: String,
default: '',
},
}
type RadioGroupProps = ExtractPropTypes<typeof props>
export default /*#__PURE__*/ defineComponent({
name: 'RadioGroup',
props,
// emits: ['change'],
setup(props, { emit, slots }) {
const rootRef: Ref<HTMLElement | null> = ref(null)
const trigger = useCustomEvent(rootRef, emit)
useProvideRadioGroup(props, trigger)
return () => {
return (
<uni-radio-group ref={rootRef}>
{slots.default && slots.default()}
</uni-radio-group>
)
}
},
})
function useProvideRadioGroup(
props: RadioGroupProps,
trigger: CustomEventTrigger
) {
const fields: UniRadioGroupFieldCtx[] = []
onMounted(() => {
_resetRadioGroupValue(fields.length - 1)
})
const getFieldsValue = () =>
fields.find((field) => field.value.radioChecked)?.value.value
provide<UniRadioGroupCtx>(uniRadioGroupKey, {
addField(field: UniRadioGroupFieldCtx) {
fields.push(field)
},
removeField(field: UniRadioGroupFieldCtx) {
fields.splice(fields.indexOf(field), 1)
},
radioChange($event: Event, field: UniRadioGroupFieldCtx) {
const index = fields.indexOf(field)
_resetRadioGroupValue(index, true)
trigger('change', $event, {
value: getFieldsValue(),
})
},
})
const uniForm = inject<UniFormCtx>(
uniFormKey,
(false as unknown) as UniFormCtx
)
if (uniForm) {
uniForm.addField({
submit: () => {
let data: [string, any] = ['', null]
if (props.name !== '') {
data[0] = props.name
data[1] = getFieldsValue()
}
return data
},
})
}
function setFieldChecked(
field: UniRadioGroupFieldCtx,
radioChecked: boolean
) {
field.value = {
radioChecked,
value: field.value.value,
}
}
function _resetRadioGroupValue(key: number, change?: boolean) {
fields.forEach((value, index) => {
if (index === key) {
return
}
if (change) {
setFieldChecked(fields[index], false)
} else {
// 这里逻辑有点奇怪,但我决定保留
fields.forEach((v, i) => {
if (index >= i) {
return
}
if (fields[i].value.radioChecked) {
setFieldChecked(fields[index], false)
}
})
}
})
}
return fields
}
<template>
<uni-radio-group v-bind="$attrs">
<slot />
</uni-radio-group>
</template>
<script>
import {
emitter,
listeners
} from '../../mixins'
export default {
name: 'RadioGroup',
mixins: [emitter, listeners],
props: {
name: {
type: String,
default: ''
}
},
data () {
return {
radioList: []
}
},
listeners: {
'@radio-change': '_changeHandler',
'@radio-group-update': '_radioGroupUpdateHandler'
},
mounted () {
this._resetRadioGroupValue(this.radioList.length - 1)
},
created () {
this.$dispatch('Form', 'uni-form-group-update', {
type: 'add',
vm: this
})
},
beforeDestroy () {
this.$dispatch('Form', 'uni-form-group-update', {
type: 'remove',
vm: this
})
},
methods: {
_changeHandler ($event, vm) {
const index = this.radioList.indexOf(vm)
this._resetRadioGroupValue(index, true)
this.$trigger('change', $event, {
value: vm.radioValue
})
},
_radioGroupUpdateHandler ($event) {
if ($event.type === 'add') {
this.radioList.push($event.vm)
} else {
const index = this.radioList.indexOf($event.vm)
this.radioList.splice(index, 1)
}
},
_resetRadioGroupValue (key, change) {
this.radioList.forEach((value, index) => {
if (index === key) {
return
}
if (change) {
this.radioList[index].radioChecked = false
} else {
this.radioList.forEach((v, i) => {
if (index >= i) {
return
}
if (this.radioList[i].radioChecked) {
this.radioList[index].radioChecked = false
}
})
}
})
},
_getFormData () {
const data = {}
if (this.name !== '') {
let value = ''
this.radioList.forEach(vm => {
if (vm.radioChecked) {
value = vm.value
}
})
data.value = value
data.key = this.name
}
return data
}
}
}
</script>
\ No newline at end of file
import {
defineComponent,
onBeforeUnmount,
watch,
inject,
ref,
computed,
} from 'vue'
import type { Ref } from 'vue'
import { useListeners } from '../../helpers/useListeners'
import { useBooleanAttr } from '../../helpers/useBooleanAttr'
import { UniRadioGroupCtx, uniRadioGroupKey } from '../radio-group'
import { UniFormCtx, uniFormKey } from '../form'
import { uniLabelKey, UniLabelCtx } from '../label'
import {
createSvgIconVNode,
ICON_PATH_SUCCESS_NO_CIRCLE,
} from '@dcloudio/uni-core'
const props = {
checked: {
type: [Boolean, String],
default: false,
},
id: {
type: String,
default: '',
},
disabled: {
type: [Boolean, String],
default: false,
},
color: {
type: String,
default: '#007aff',
},
value: {
type: String,
default: '',
},
}
export default /*#__PURE__*/ defineComponent({
name: 'Radio',
props,
setup(props, { slots }) {
const radioChecked = ref(props.checked)
const radioValue = ref(props.value)
const checkedStyle = computed(
() => `background-color: ${props.color};border-color: ${props.color};`
)
watch(
[() => props.checked, () => props.value],
([newChecked, newModelValue]) => {
radioChecked.value = newChecked
radioValue.value = newModelValue
}
)
const reset = () => {
radioChecked.value = false
}
const { uniCheckGroup, uniLabel, field } = useRadioInject(
radioChecked,
radioValue,
reset
)
const _onClick = ($event: Event) => {
if (props.disabled) {
return
}
radioChecked.value = true
uniCheckGroup && uniCheckGroup.radioChange($event, field)
}
if (!!uniLabel) {
uniLabel.addHandler(_onClick)
onBeforeUnmount(() => {
uniLabel.removeHandler(_onClick)
})
}
useListeners(props, { 'label-click': _onClick })
return () => {
const { booleanAttrs } = useBooleanAttr(props, 'disabled')
return (
<uni-radio {...booleanAttrs} onClick={_onClick}>
<div class="uni-radio-wrapper">
<div
class="uni-radio-input"
class={{ 'uni-radio-input-disabled': props.disabled }}
style={radioChecked.value ? checkedStyle.value : ''}
>
{radioChecked.value
? createSvgIconVNode(ICON_PATH_SUCCESS_NO_CIRCLE, '#fff', 18)
: ''}
</div>
{slots.default && slots.default()}
</div>
</uni-radio>
)
}
},
})
function useRadioInject(
radioChecked: Ref<string | boolean>,
radioValue: Ref<string>,
reset: () => void
) {
const field = computed({
get: () => ({
radioChecked: Boolean(radioChecked.value),
value: radioValue.value,
}),
set: ({ radioChecked: checked }) => {
radioChecked.value = checked
},
})
const formField = { reset }
const uniCheckGroup = inject<UniRadioGroupCtx>(
uniRadioGroupKey,
(false as unknown) as UniRadioGroupCtx
)
if (!!uniCheckGroup) {
uniCheckGroup.addField(field)
}
const uniForm = inject<UniFormCtx>(
uniFormKey,
(false as unknown) as UniFormCtx
)
if (!!uniForm) {
uniForm.addField(formField)
}
const uniLabel = inject<UniLabelCtx>(
uniLabelKey,
(false as unknown) as UniLabelCtx
)
onBeforeUnmount(() => {
uniCheckGroup && uniCheckGroup.removeField(field)
uniForm && uniForm.removeField(formField)
})
return {
uniCheckGroup,
uniForm,
uniLabel,
field,
}
}
<template>
<uni-radio
:disabled="disabled"
v-bind="$attrs"
@click="_onClick"
>
<div class="uni-radio-wrapper">
<div
:class="radioChecked ? 'uni-radio-input-checked' : ''"
:style="radioChecked ? checkedStyle : ''"
class="uni-radio-input"
/>
<slot />
</div>
</uni-radio>
</template>
<script>
import {
emitter,
listeners
} from '../../mixins'
export default {
name: 'Radio',
mixins: [emitter, listeners],
props: {
checked: {
type: [Boolean, String],
default: false
},
id: {
type: String,
default: ''
},
disabled: {
type: [Boolean, String],
default: false
},
color: {
type: String,
default: '#007AFF'
},
value: {
type: String,
default: ''
}
},
data () {
return {
radioChecked: this.checked,
radioValue: this.value
}
},
computed: {
checkedStyle () {
return `background-color: ${this.color};border-color: ${this.color};`
}
},
watch: {
checked (val) {
this.radioChecked = val
},
value (val) {
this.radioValue = val
}
},
listeners: {
'label-click': '_onClick',
'@label-click': '_onClick'
},
created () {
this.$dispatch('RadioGroup', 'uni-radio-group-update', {
type: 'add',
vm: this
})
this.$dispatch('Form', 'uni-form-group-update', {
type: 'add',
vm: this
})
},
beforeDestroy () {
this.$dispatch('RadioGroup', 'uni-radio-group-update', {
type: 'remove',
vm: this
})
this.$dispatch('Form', 'uni-form-group-update', {
type: 'remove',
vm: this
})
},
methods: {
_onClick ($event) {
if (this.disabled || this.radioChecked) {
return
}
this.radioChecked = true
this.$dispatch('RadioGroup', 'uni-radio-change', $event, this)
},
_resetFormData () {
this.radioChecked = this.min
}
}
}
</script>
\ No newline at end of file
import { isPlainObject } from '@vue/shared'
import { watch, onUnmounted, getCurrentInstance } from 'vue'
import { watch, onUnmounted } from 'vue'
import { useCurrentPageId } from '@dcloudio/uni-core'
export function useListeners(
......
......@@ -27,12 +27,18 @@ uni-checkbox[disabled] {
border-radius: 3px;
width: 22px;
height: 22px;
line-height: 25px;
text-align: center;
position: relative;
}
uni-checkbox[disabled='false'] .uni-checkbox-input:hover,
.uni-checkbox-input svg {
color: #007aff;
font-size: 22px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -48%) scale(0.73);
}
uni-checkbox:not([disabled]) .uni-checkbox-input:hover {
border-color: #007aff;
}
......
......@@ -34,9 +34,7 @@ uni-radio:not([disabled]) .uni-radio-input:hover {
border-color: #007aff;
}
.uni-radio-input.uni-radio-input-checked:before {
font: normal normal normal 14px/1 'uni';
content: '\EA08';
.uni-radio-input svg {
color: #ffffff;
font-size: 18px;
position: absolute;
......@@ -52,4 +50,4 @@ uni-radio:not([disabled]) .uni-radio-input:hover {
.uni-radio-input.uni-radio-input-disabled:before {
color: #adadad;
}
\ No newline at end of file
}
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册