diff --git a/code/base-project/base-vue/packages/ao-design/src/_styles/_variables.scss b/code/base-project/base-vue/packages/ao-design/src/_styles/_variables.scss index 1d9f6ef8..e9fa641a 100644 --- a/code/base-project/base-vue/packages/ao-design/src/_styles/_variables.scss +++ b/code/base-project/base-vue/packages/ao-design/src/_styles/_variables.scss @@ -94,6 +94,10 @@ $scroll-track-color: var(--ao-scroll-track-color); $bg-color-container: var(--ao-bg-color-container); $bg-color-container-hover: var(--ao-bg-color-container-hover); $bg-color-container-active: var(--ao-bg-color-container-active); +$bg-color-container-select: var(--ao-bg-color-container-select); + +$bg-color-component: var(--ao-bg-color-component); +$border-color-component: var(--ao-border-color-component); // 组件边框(Input、Select、Card 等容器的外部边框) // shadow // 基础/下层 投影 hover 使用的组件包括:表格 / diff --git a/code/base-project/base-vue/packages/ao-design/src/_styles/theme/_dark.scss b/code/base-project/base-vue/packages/ao-design/src/_styles/theme/_dark.scss index d4217be5..f4c9a9ef 100644 --- a/code/base-project/base-vue/packages/ao-design/src/_styles/theme/_dark.scss +++ b/code/base-project/base-vue/packages/ao-design/src/_styles/theme/_dark.scss @@ -72,6 +72,10 @@ --ao-bg-color-container: #242424; --ao-bg-color-container-hover: #2c2c2c; --ao-bg-color-container-active: #4b4b4b; + --ao-bg-color-container-select: #5e5e5e; + + --ao-bg-color-component: #393939; + --ao-border-color-component: #5e5e5e; // shadow阴影 // 基础/下层 投影 hover 使用的组件包括:表格 / diff --git a/code/base-project/base-vue/packages/ao-design/src/_styles/theme/_light.scss b/code/base-project/base-vue/packages/ao-design/src/_styles/theme/_light.scss index dbce674b..959f0911 100644 --- a/code/base-project/base-vue/packages/ao-design/src/_styles/theme/_light.scss +++ b/code/base-project/base-vue/packages/ao-design/src/_styles/theme/_light.scss @@ -73,6 +73,10 @@ --ao-bg-color-container: #fff; --ao-bg-color-container-hover: #f3f3f3; --ao-bg-color-container-active: #e8e8e8; + --ao-bg-color-container-select: #fff; + + --ao-bg-color-component: #e8e8e8; + --ao-border-color-component: #ddd; // shadow阴影 // 基础/下层 投影 hover 使用的组件包括:表格 / diff --git a/code/base-project/base-vue/packages/ao-design/src/index.scss b/code/base-project/base-vue/packages/ao-design/src/index.scss index 54297fb9..e5218888 100644 --- a/code/base-project/base-vue/packages/ao-design/src/index.scss +++ b/code/base-project/base-vue/packages/ao-design/src/index.scss @@ -21,6 +21,7 @@ [class*='#{variables.$prefix}-'] { -webkit-box-sizing: border-box; box-sizing: border-box; + font-family: Inter, -apple-system, diff --git a/code/base-project/base-vue/packages/ao-design/src/radio/context.ts b/code/base-project/base-vue/packages/ao-design/src/radio/context.ts index b1c88f8f..28236b3c 100644 --- a/code/base-project/base-vue/packages/ao-design/src/radio/context.ts +++ b/code/base-project/base-vue/packages/ao-design/src/radio/context.ts @@ -5,6 +5,7 @@ export interface RadioGroupContext { value: string | number | boolean disabled: boolean slots: Slots + type?: 'default' | 'filled' | 'primaryFilled' | 'card' | 'pureCard' | 'outline' handleChange: (value: string | number | boolean, e: Event) => void } diff --git a/code/base-project/base-vue/packages/ao-design/src/radio/demo.vue b/code/base-project/base-vue/packages/ao-design/src/radio/demo.vue new file mode 100644 index 00000000..79314b03 --- /dev/null +++ b/code/base-project/base-vue/packages/ao-design/src/radio/demo.vue @@ -0,0 +1,199 @@ + + + + + 白天 + + + 黑天 + + + + + Default + + + + + filled + + + + filled + + + + filled + + + + + primaryFilled + + + + + outline + + + + + card + + + + + pureCard + + + + + 自定义插槽radio + + + + {{ checked ? '选中' : '未选中' }} - {{ data.name }} + + + + + + + + diff --git a/code/base-project/base-vue/packages/ao-design/src/radio/index.scss b/code/base-project/base-vue/packages/ao-design/src/radio/index.scss index ed8a6b67..743f075a 100644 --- a/code/base-project/base-vue/packages/ao-design/src/radio/index.scss +++ b/code/base-project/base-vue/packages/ao-design/src/radio/index.scss @@ -3,14 +3,13 @@ $radio-prefix: '#{$prefix}-radio'; $radio-group-prefix: '#{$prefix}-radio-group'; -.#{$radio-prefix} { +.#{$radio-prefix}-default { color: $text-color-base; display: inline-flex; - align-items: center; + align-items: flex-start; position: relative; cursor: pointer; user-select: none; - line-height: 20px; &.#{$radio-prefix}-disabled { cursor: not-allowed; opacity: 0.5; @@ -34,8 +33,6 @@ $radio-group-prefix: '#{$prefix}-radio-group'; align-items: center; white-space: nowrap; outline: 0; - margin-top: 2px; - vertical-align: sub; .#{$radio-prefix}-input { opacity: 0; outline: 0; @@ -79,9 +76,459 @@ $radio-group-prefix: '#{$prefix}-radio-group'; .#{$radio-prefix}-content { font-size: 12px; margin-left: 8px; - vertical-align: middle; + display: inline-block; + flex-direction: column; + + .#{$radio-prefix}-label { + line-height: 16px; + display: flex; + align-items: center; + color: $text-color-base; + } + + .#{$radio-prefix}-extra { + font-size: 12px; + color: $text-color-secondary; + margin-top: 4px; + } } } -.#{$radio-group-prefix} { +.#{$radio-group-prefix}-filled { + background-color: $bg-color-component; + border-radius: $border-radius; + padding: 1.5px; + line-height: 26px; + + &.#{$radio-group-prefix}-small { + line-height: 18px; + + .#{$radio-prefix}-filled { + font-size: 11px; + line-height: 18px; + } + } + + &.#{$radio-group-prefix}-medium { + line-height: 22px; + .#{$radio-prefix}-filled { + font-size: 12px; + line-height: 22px; + } + } + + &.#{$radio-group-prefix}-large { + line-height: 26px; + .#{$radio-prefix}-filled { + font-size: 13px; + line-height: 26px; + } + } +} + +.#{$radio-prefix}-filled { + margin: 1.5px; + position: relative; + display: inline-block; + color: $text-color-secondary; + font-size: 14px; + line-height: 26px; + background-color: transparent; + border-right: 0; + cursor: pointer; + width: 100%; + + &:first-child { + border-top-left-radius: $border-radius; + border-bottom-left-radius: $border-radius; + } + + &:last-child { + border-top-right-radius: $border-radius; + border-bottom-right-radius: $border-radius; + } + + &.#{$radio-prefix}-disabled { + cursor: not-allowed; + opacity: 0.5; + } + + .#{$radio-prefix}-input { + position: absolute; + top: 0; + left: 0; + width: 0; + height: 0; + opacity: 0; + } + + .#{$radio-prefix}-content { + position: relative; + display: block; + padding: 0 12px; + } + + &.#{$radio-prefix}-checked { + color: $text-color-base; + background-color: $bg-color-container-select; + z-index: 1; + } + + &:not(.#{$radio-prefix}-disabled):not(.#{$radio-prefix}-checked):hover { + color: $text-color-base; + } + + &:before { + content: ''; + position: absolute; + left: -2px; + top: 50%; + transform: translateY(-50%); + width: 1px; + height: 80%; + background-color: $border-color-component; + transition: opacity 0.2s cubic-bezier(0, 0, 0.15, 1); + } +} + +.#{$radio-group-prefix}-primaryFilled { + background-color: $bg-color-component; + border-radius: $border-radius; + padding: 1.5px; + line-height: 26px; + + &.#{$radio-group-prefix}-small { + line-height: 18px; + + .#{$radio-prefix}-primaryFilled { + font-size: 11px; + line-height: 18px; + } + } + + &.#{$radio-group-prefix}-medium { + line-height: 22px; + .#{$radio-prefix}-primaryFilled { + font-size: 12px; + line-height: 22px; + } + } + + &.#{$radio-group-prefix}-large { + line-height: 26px; + .#{$radio-prefix}-primaryFilled { + font-size: 13px; + line-height: 26px; + } + } +} + +.#{$radio-prefix}-primaryFilled { + margin: 1.5px; + position: relative; + display: inline-block; + color: $text-color-secondary; + font-size: 14px; + line-height: 26px; + background-color: transparent; + border-right: 0; + cursor: pointer; + width: 100%; + + &:first-child { + border-top-left-radius: $border-radius; + border-bottom-left-radius: $border-radius; + } + + &:last-child { + border-top-right-radius: $border-radius; + border-bottom-right-radius: $border-radius; + } + + &.#{$radio-prefix}-disabled { + cursor: not-allowed; + opacity: 0.5; + } + + .#{$radio-prefix}-input { + position: absolute; + top: 0; + left: 0; + width: 0; + height: 0; + opacity: 0; + } + + .#{$radio-prefix}-content { + position: relative; + display: block; + padding: 0 12px; + } + + &.#{$radio-prefix}-checked { + color: $text-color-anti; + background-color: $primary-color; + z-index: 1; + } + + &:not(.#{$radio-prefix}-disabled):not(.#{$radio-prefix}-checked):hover { + color: $primary-color; + } + + &:before { + content: ''; + position: absolute; + left: -2px; + top: 50%; + transform: translateY(-50%); + width: 1px; + height: 80%; + background-color: $border-color-component; + transition: opacity 0.2s cubic-bezier(0, 0, 0.15, 1); + } +} + +.#{$radio-group-prefix}-outline { + padding: 1.5px; + line-height: 26px; + background-color: transparent; + border-radius: $border-radius; + + &.#{$radio-group-prefix}-small { + line-height: 18px; + + .#{$radio-prefix}-outline { + font-size: 11px; + line-height: 18px; + } + } + + &.#{$radio-group-prefix}-medium { + line-height: 22px; + .#{$radio-prefix}-outline { + font-size: 12px; + line-height: 22px; + } + } + + &.#{$radio-group-prefix}-large { + line-height: 26px; + .#{$radio-prefix}-outline { + font-size: 13px; + line-height: 26px; + } + } + + .#{$radio-prefix}-outline:last-child { + border-right-color: $border-color; + } + .#{$radio-prefix}-outline.#{$radio-prefix}-checked.#{$radio-prefix}-outline:last-child { + border-color: $primary-color; + } + .#{$radio-prefix}-outline.#{$radio-prefix}-checked + .#{$radio-prefix}-outline { + border-left-color: transparent; + } +} + +.#{$radio-prefix}-outline { + position: relative; + display: inline-block; + color: $text-color-secondary; + font-size: 14px; + line-height: 26px; + background-color: transparent; + border: 1px solid $border-color; + border-right-color: transparent; + cursor: pointer; + width: 100%; + + &:first-child { + border-top-left-radius: $border-radius; + border-bottom-left-radius: $border-radius; + } + + &:last-child { + border-top-right-radius: $border-radius; + border-bottom-right-radius: $border-radius; + } + + &.#{$radio-prefix}-disabled { + cursor: not-allowed; + opacity: 0.5; + } + .#{$radio-prefix}-input { + position: absolute; + top: 0; + left: 0; + width: 0; + height: 0; + opacity: 0; + } + .#{$radio-prefix}-content { + position: relative; + display: block; + padding: 0 12px; + } + &:not(.#{$radio-prefix}-disabled):hover { + color: $primary-color; + } + &.#{$radio-prefix}-checked { + color: $primary-color; + border-color: $primary-color; + } +} + +.#{$radio-prefix}-card { + color: $text-color-base; + display: inline-flex; + align-items: flex-start; + position: relative; + cursor: pointer; + user-select: none; + padding: 8px 12px; + box-sizing: border-box; + border: 1px solid transparent; + border-radius: $border-radius; + + &:hover, + &:hover .#{$radio-prefix}-main { + background-color: $bg-color-component; + } + + &.#{$radio-prefix}-disabled { + cursor: not-allowed; + opacity: 0.5; + } + + &.#{$radio-prefix}-checked { + border-color: $primary-color; + background-color: $status-info-bg-color; + } + + &:hover .#{$radio-prefix}-main .#{$radio-prefix}-inner { + border-color: $primary-color; + } + + &.#{$radio-prefix}-checked .#{$radio-prefix}-main .#{$radio-prefix}-inner { + border-color: $primary-color; + &::after { + transform: translate(-50%, -50%) scale(1); + background-color: $primary-color; + } + } + + .#{$radio-prefix}-main { + cursor: pointer; + display: flex; + align-items: center; + white-space: nowrap; + outline: 0; + + .#{$radio-prefix}-input { + opacity: 0; + outline: 0; + position: absolute; + z-index: -1; + margin: 0; + width: 0; + height: 0; + box-sizing: border-box; + } + + .#{$radio-prefix}-inner { + border: 1px solid $border-color; + border-radius: 100%; + width: 16px; + height: 16px; + background-color: $bg-color-container; + position: relative; + display: inline-block; + cursor: pointer; + box-sizing: border-box; + outline: 0; + &::after { + box-sizing: border-box; + width: 8px; + height: 8px; + border-radius: 999px; + background-color: transparent; + content: ''; + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%) scale(0); + transition: + transform 0.1s ease-in, + background-color 0.1s ease-in; + } + } + } + + .#{$radio-prefix}-content { + font-size: 12px; + margin-left: 8px; + display: inline-block; + flex-direction: column; + + .#{$radio-prefix}-label { + line-height: 16px; + display: flex; + align-items: center; + color: $text-color-base; + font-weight: 600; + } + + .#{$radio-prefix}-extra { + font-size: 12px; + color: $text-color-secondary; + margin-top: 8px; + } + } +} + +.#{$radio-prefix}-pureCard { + color: $text-color-base; + display: inline-flex; + align-items: flex-start; + position: relative; + cursor: pointer; + user-select: none; + padding: 8px 12px; + box-sizing: border-box; + border: 1px solid transparent; + border-radius: $border-radius; + + &:hover { + background-color: $bg-color-component; + } + + &.#{$radio-prefix}-disabled { + cursor: not-allowed; + opacity: 0.5; + } + + &.#{$radio-prefix}-checked { + border-color: $primary-color; + background-color: $status-info-bg-color; + } + + .#{$radio-prefix}-content { + font-size: 12px; + display: inline-block; + flex-direction: column; + + .#{$radio-prefix}-label { + line-height: 16px; + display: flex; + align-items: center; + color: $text-color-base; + font-weight: 600; + } + + .#{$radio-prefix}-extra { + font-size: 12px; + color: $text-color-secondary; + margin-top: 8px; + } + } } diff --git a/code/base-project/base-vue/packages/ao-design/src/radio/radio-group.tsx b/code/base-project/base-vue/packages/ao-design/src/radio/radio-group.tsx index e96cac8c..3985f90e 100644 --- a/code/base-project/base-vue/packages/ao-design/src/radio/radio-group.tsx +++ b/code/base-project/base-vue/packages/ao-design/src/radio/radio-group.tsx @@ -36,6 +36,10 @@ export default defineComponent({ type: String, default: 'value', }, + extraField: { + type: String, + default: 'extra', + }, options: { type: Array as PropType>, default: [], @@ -44,6 +48,14 @@ export default defineComponent({ type: Object as PropType>, default: () => ({}), }, + type: { + type: String as PropType<'default' | 'filled' | 'primaryFilled' | 'card' | 'pureCard' | 'outline'>, + default: 'default', + }, + size: { + type: String as PropType<'small' | 'medium' | 'large'>, + default: 'medium', + }, }, emits: ['update:modelValue', 'change'], setup(props, { emit, slots }) { @@ -64,12 +76,13 @@ export default defineComponent({ emit('change', val, e) } const groupDisabled = computed(() => props.disabled) - + const groupType = computed(() => props.type) // 提供 context 给 Radio 使用 provide(radioGroupKey, reactive({ name: 'AoRadioGroup' as const, value: _value, disabled: groupDisabled, + type: groupType, slots, handleChange, })) @@ -88,12 +101,31 @@ export default defineComponent({ ? opt[props.labelField]() : opt[props.labelField] + const extraContent = slots.extra + ? slots.extra({ data: opt }) + : typeof opt[props.extraField] === 'function' + ? opt[props.extraField]() + : opt[props.extraField] + + if (slots.radio) { + return ( + handleChange(opt[props.valueField], e)} // 内部统一处理点击 + > + {slots.radio({ data: opt, checked: _value.value === opt[props.valueField] })} + + ) + } + return ( {labelContent} @@ -101,12 +133,30 @@ export default defineComponent({ }) } + const spaceSize + = ['default', 'card', 'pureCard'].includes(groupType.value) + ? [6, 6] + : props.spaceProps.size ?? 0 + + const sizeClass = computed(() => { + if (['filled', 'primaryFilled', 'outline'].includes(groupType.value)) { + return `${prefixCls}-${props.size}` + } + return null + }) + return () => ( - - - {props.options.length > 0 ? renderOptions() : slots.default?.()} - - + + {props.options.length > 0 ? renderOptions() : slots.default?.()} + ) }, }) diff --git a/code/base-project/base-vue/packages/ao-design/src/radio/radio.tsx b/code/base-project/base-vue/packages/ao-design/src/radio/radio.tsx index 279ae553..65c2cd89 100644 --- a/code/base-project/base-vue/packages/ao-design/src/radio/radio.tsx +++ b/code/base-project/base-vue/packages/ao-design/src/radio/radio.tsx @@ -31,6 +31,10 @@ export default defineComponent({ type: [String, Object, Function] as unknown as () => string | VNode | (() => VNode), default: '', }, + type: { + type: String as PropType<'default' | 'filled' | 'primaryFilled' | 'card' | 'pureCard' | 'outline'>, + default: 'default', + } }, emits: ['update:modelValue', 'change'], setup(props, { slots, emit }) { @@ -42,7 +46,6 @@ export default defineComponent({ const isDisabled = computed(() => props.disabled || radioGroupCtx?.disabled) const _checked = ref(props.defaultChecked) - // 判断是否选中 const isChecked = computed(() => { if (inGroup.value) return props.value === radioGroupCtx!.value @@ -61,17 +64,16 @@ export default defineComponent({ } else { const newValue = props.allowUncheck ? !isChecked.value : true - _checked.value = newValue emit('update:modelValue', newValue) emit('change', props.value, e) } } - return () => ( + const renderDefault = () => ( - {slots.default?.()} + {slots.default?.()} + {props.extra && ( + + {typeof props.extra === 'function' ? props.extra() : props.extra} + + )} - ) + + const renderFilled = () => ( + + e.stopPropagation()} + class={`${prefixCls}-input`} + /> + + {slots.default?.()} + + + ) + + const renderPrimaryFilled = () => ( + + e.stopPropagation()} + class={`${prefixCls}-input`} + /> + + {slots.default?.()} + + + ) + + const renderCard = () => ( + + + + e.stopPropagation()} + /> + + + {slots.default?.()} + {props.extra && ( + + {typeof props.extra === 'function' ? props.extra() : props.extra} + + )} + + + ) + + const renderPureCard = () => ( + + + {slots.default?.()} + {props.extra && ( + + {typeof props.extra === 'function' ? props.extra() : props.extra} + + )} + + + ) + + const renderOutline = () => ( + + e.stopPropagation()} + class={`${prefixCls}-input`} + /> + + {slots.default?.()} + + + ) + + return () => { + switch (props.type) { + case 'filled': + return renderFilled() + case 'primaryFilled': + return renderPrimaryFilled() + case 'card': + return renderCard() + case 'pureCard': + return renderPureCard() + case 'outline': + return renderOutline() + default: + return renderDefault() + } + } }, }) diff --git a/code/base-project/base-vue/src/App.vue b/code/base-project/base-vue/src/App.vue index ac2a3b22..e69319a2 100644 --- a/code/base-project/base-vue/src/App.vue +++ b/code/base-project/base-vue/src/App.vue @@ -1,110 +1,13 @@ - - - ARadioGroup - 基本使用 - - 选项 A - 选项 B - 选项 C - - 当前选中: {{ selectedValue }} - - 切换 Group Disabled - - - ARadioGroup - 使用 options 数组渲染 - - 当前选中: {{ selectedValue }} - - 单独 ARadio 使用 - - 单选 X - - 是否选中: {{ singleChecked }} - + + content -
当前选中: {{ selectedValue }}
是否选中: {{ singleChecked }}