u
This commit is contained in:
1 parent
97b88528a5
commit
5834738700
10 files changed
+864
-119
No files matched your search
@@ -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 使用的组件包括:表格 /
|
||||
|
||||
@@ -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 使用的组件包括:表格 /
|
||||
|
||||
@@ -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 使用的组件包括:表格 /
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
[class*='#{variables.$prefix}-'] {
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
|
||||
font-family:
|
||||
Inter,
|
||||
-apple-system,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const selectedValue = ref()
|
||||
|
||||
// 自定义字段名
|
||||
const options = [
|
||||
{ name: '选项一', id: 1, note: '这是额外信息 1' },
|
||||
{ name: '选项二', id: 2, note: '这是额外信息 2' },
|
||||
{ name: '选项三', id: 3 },
|
||||
]
|
||||
|
||||
function handleChange(val: string | number | boolean) {
|
||||
console.log('当前选中值:', val)
|
||||
}
|
||||
|
||||
function toggleThemeLight() {
|
||||
document.documentElement.setAttribute('theme-mode', 'light')
|
||||
}
|
||||
function toggleThemeDark() {
|
||||
document.documentElement.setAttribute('theme-mode', 'dark')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AButton status="primary" @click="toggleThemeLight">
|
||||
白天
|
||||
</AButton>
|
||||
<AButton status="primary" @click="toggleThemeDark">
|
||||
黑天
|
||||
</AButton>
|
||||
|
||||
<div class="demo-wrapper">
|
||||
<div class="demo-container">
|
||||
<h3>Default</h3>
|
||||
<ARadioGroup
|
||||
v-model="selectedValue"
|
||||
:options="options"
|
||||
label-field="name"
|
||||
value-field="id"
|
||||
extra-field="note"
|
||||
type="default"
|
||||
@change="handleChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="demo-container">
|
||||
<h3>filled</h3>
|
||||
<ARadioGroup
|
||||
v-model="selectedValue"
|
||||
:options="options"
|
||||
label-field="name"
|
||||
value-field="id"
|
||||
extra-field="note"
|
||||
type="filled"
|
||||
size="small"
|
||||
@change="handleChange"
|
||||
/>
|
||||
</div>
|
||||
<div class="demo-container">
|
||||
<h3>filled</h3>
|
||||
<ARadioGroup
|
||||
v-model="selectedValue"
|
||||
:options="options"
|
||||
label-field="name"
|
||||
value-field="id"
|
||||
extra-field="note"
|
||||
type="filled"
|
||||
@change="handleChange"
|
||||
/>
|
||||
</div>
|
||||
<div class="demo-container">
|
||||
<h3>filled</h3>
|
||||
<ARadioGroup
|
||||
v-model="selectedValue"
|
||||
:options="options"
|
||||
label-field="name"
|
||||
value-field="id"
|
||||
extra-field="note"
|
||||
type="filled"
|
||||
size="large"
|
||||
@change="handleChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="demo-container">
|
||||
<h3>primaryFilled</h3>
|
||||
<ARadioGroup
|
||||
v-model="selectedValue"
|
||||
:options="options"
|
||||
label-field="name"
|
||||
value-field="id"
|
||||
extra-field="note"
|
||||
type="primaryFilled"
|
||||
@change="handleChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="demo-container">
|
||||
<h3>outline</h3>
|
||||
<ARadioGroup
|
||||
v-model="selectedValue"
|
||||
:options="options"
|
||||
label-field="name"
|
||||
value-field="id"
|
||||
extra-field="note"
|
||||
type="outline"
|
||||
@change="handleChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="demo-container">
|
||||
<h3>card</h3>
|
||||
<ARadioGroup
|
||||
v-model="selectedValue"
|
||||
:options="options"
|
||||
label-field="name"
|
||||
value-field="id"
|
||||
extra-field="note"
|
||||
type="card"
|
||||
@change="handleChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="demo-container">
|
||||
<h3>pureCard</h3>
|
||||
<ARadioGroup
|
||||
v-model="selectedValue"
|
||||
:options="options"
|
||||
label-field="name"
|
||||
value-field="id"
|
||||
extra-field="note"
|
||||
type="pureCard"
|
||||
@change="handleChange"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="demo-container">
|
||||
<h3>自定义插槽radio</h3>
|
||||
<ARadioGroup
|
||||
v-model="selectedValue" :options="options"
|
||||
value-field="id"
|
||||
>
|
||||
<template #radio="{ data, checked }">
|
||||
<div
|
||||
:style="{
|
||||
padding: '4px 8px',
|
||||
border: checked ? '2px solid #409eff' : '1px solid #dcdfe6',
|
||||
borderRadius: '4px',
|
||||
cursor: 'pointer',
|
||||
marginRight: '4px',
|
||||
}"
|
||||
>
|
||||
{{ checked ? '选中' : '未选中' }} - {{ data.name }}
|
||||
</div>
|
||||
</template>
|
||||
</ARadioGroup>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.demo-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
padding: 24px 16px;
|
||||
min-height: 100vh;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.demo-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 12px 16px;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 1px 6px rgba(0, 0, 0, 0.08);
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
text-align: center;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 主题背景色 */
|
||||
:root[theme-mode='light'] .demo-wrapper {
|
||||
}
|
||||
:root[theme-mode='dark'] .demo-wrapper {
|
||||
background-color: #1e1e1e;
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,10 @@ export default defineComponent({
|
||||
type: String,
|
||||
default: 'value',
|
||||
},
|
||||
extraField: {
|
||||
type: String,
|
||||
default: 'extra',
|
||||
},
|
||||
options: {
|
||||
type: Array as PropType<Array<string | number | RadioOption>>,
|
||||
default: [],
|
||||
@@ -44,6 +48,14 @@ export default defineComponent({
|
||||
type: Object as PropType<Record<string, any>>,
|
||||
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 (
|
||||
<div
|
||||
key={opt[props.valueField]}
|
||||
onClick={e => handleChange(opt[props.valueField], e)} // 内部统一处理点击
|
||||
>
|
||||
{slots.radio({ data: opt, checked: _value.value === opt[props.valueField] })}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Radio
|
||||
key={opt[props.valueField]}
|
||||
value={opt[props.valueField]}
|
||||
disabled={opt.disabled ?? props.disabled}
|
||||
modelValue={_value.value === opt[props.valueField]}
|
||||
extra={extraContent}
|
||||
type={props.type}
|
||||
>
|
||||
{labelContent}
|
||||
</Radio>
|
||||
@@ -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 () => (
|
||||
<div class={[prefixCls]}>
|
||||
<Space {...props.spaceProps}>
|
||||
{props.options.length > 0 ? renderOptions() : slots.default?.()}
|
||||
</Space>
|
||||
</div>
|
||||
<Space
|
||||
align="start"
|
||||
size={spaceSize}
|
||||
{...props.spaceProps}
|
||||
class={[
|
||||
`${prefixCls}-${groupType.value}`,
|
||||
sizeClass.value,
|
||||
]}
|
||||
>
|
||||
{props.options.length > 0 ? renderOptions() : slots.default?.()}
|
||||
</Space>
|
||||
)
|
||||
},
|
||||
})
|
||||
@@ -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 = () => (
|
||||
<label
|
||||
class={[
|
||||
prefixCls,
|
||||
`${prefixCls}-default`,
|
||||
isDisabled.value ? `${prefixCls}-disabled` : '',
|
||||
isChecked.value ? `${prefixCls}-checked` : '',
|
||||
]}
|
||||
@@ -87,10 +89,140 @@ export default defineComponent({
|
||||
/>
|
||||
</span>
|
||||
<div class={`${prefixCls}-content`}>
|
||||
{slots.default?.()}
|
||||
<div class={`${prefixCls}-label`}>{slots.default?.()}</div>
|
||||
{props.extra && (
|
||||
<div class={`${prefixCls}-extra`}>
|
||||
{typeof props.extra === 'function' ? props.extra() : props.extra}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
</label>
|
||||
)
|
||||
|
||||
const renderFilled = () => (
|
||||
<label
|
||||
class={[
|
||||
`${prefixCls}-filled`,
|
||||
isDisabled.value ? `${prefixCls}-disabled` : '',
|
||||
isChecked.value ? `${prefixCls}-checked` : '',
|
||||
]}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
onClick={e => e.stopPropagation()}
|
||||
class={`${prefixCls}-input`}
|
||||
/>
|
||||
<span class={`${prefixCls}-content`}>
|
||||
{slots.default?.()}
|
||||
</span>
|
||||
</label>
|
||||
)
|
||||
|
||||
const renderPrimaryFilled = () => (
|
||||
<label
|
||||
class={[
|
||||
`${prefixCls}-primaryFilled`,
|
||||
isDisabled.value ? `${prefixCls}-disabled` : '',
|
||||
isChecked.value ? `${prefixCls}-checked` : '',
|
||||
]}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
onClick={e => e.stopPropagation()}
|
||||
class={`${prefixCls}-input`}
|
||||
/>
|
||||
<span class={`${prefixCls}-content`}>
|
||||
{slots.default?.()}
|
||||
</span>
|
||||
</label>
|
||||
)
|
||||
|
||||
const renderCard = () => (
|
||||
<label
|
||||
class={[
|
||||
`${prefixCls}-card`,
|
||||
isDisabled.value ? `${prefixCls}-disabled` : '',
|
||||
isChecked.value ? `${prefixCls}-checked` : '',
|
||||
]}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<span class={`${prefixCls}-main`}>
|
||||
<span class={`${prefixCls}-inner`} />
|
||||
<input
|
||||
class={`${prefixCls}-input`}
|
||||
type="radio"
|
||||
disabled={isDisabled.value}
|
||||
onClick={e => e.stopPropagation()}
|
||||
/>
|
||||
</span>
|
||||
<div class={`${prefixCls}-content`}>
|
||||
<div class={`${prefixCls}-label`}>{slots.default?.()}</div>
|
||||
{props.extra && (
|
||||
<div class={`${prefixCls}-extra`}>
|
||||
{typeof props.extra === 'function' ? props.extra() : props.extra}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</label>
|
||||
)
|
||||
|
||||
const renderPureCard = () => (
|
||||
<label
|
||||
class={[
|
||||
`${prefixCls}-pureCard`,
|
||||
isDisabled.value ? `${prefixCls}-disabled` : '',
|
||||
isChecked.value ? `${prefixCls}-checked` : '',
|
||||
]}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<div class={`${prefixCls}-content`}>
|
||||
<div class={`${prefixCls}-label`}>{slots.default?.()}</div>
|
||||
{props.extra && (
|
||||
<div class={`${prefixCls}-extra`}>
|
||||
{typeof props.extra === 'function' ? props.extra() : props.extra}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</label>
|
||||
)
|
||||
|
||||
const renderOutline = () => (
|
||||
<label
|
||||
class={[
|
||||
`${prefixCls}-outline`,
|
||||
isDisabled.value ? `${prefixCls}-disabled` : '',
|
||||
isChecked.value ? `${prefixCls}-checked` : '',
|
||||
]}
|
||||
onClick={handleClick}
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
onClick={e => e.stopPropagation()}
|
||||
class={`${prefixCls}-input`}
|
||||
/>
|
||||
<span class={`${prefixCls}-content`}>
|
||||
{slots.default?.()}
|
||||
</span>
|
||||
</label>
|
||||
)
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -1,110 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
// Group 示例 1: 基本使用 + v-model
|
||||
const selectedValue = ref('B')
|
||||
|
||||
// Group 示例 2: options 数组渲染
|
||||
const options = [
|
||||
{ label: 'XXX', value: 'X' },
|
||||
{ label: '选项 Y', value: 'Y' },
|
||||
{ label: '选项 Z', value: 'Z' },
|
||||
]
|
||||
|
||||
// 单独 Radio 使用
|
||||
const singleChecked = ref(false)
|
||||
|
||||
// Group Disabled
|
||||
const groupDisabled = ref(false)
|
||||
|
||||
function toggleGroupDisabled() {
|
||||
|
||||
groupDisabled.value = !groupDisabled.value
|
||||
console.log(groupDisabled.value);
|
||||
|
||||
}
|
||||
|
||||
function handleChange(val) {
|
||||
console.log('选中值:', val)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="demo-wrapper">
|
||||
<div class="demo-container">
|
||||
<h3>ARadioGroup - 基本使用</h3>
|
||||
<ARadioGroup
|
||||
v-model="selectedValue"
|
||||
default-value="B"
|
||||
:disabled="groupDisabled"
|
||||
class="demo-radio-group"
|
||||
>
|
||||
<ARadio value="A" extra="额外信息">选项 A</ARadio>
|
||||
<ARadio value="B" >选项 B</ARadio>
|
||||
<ARadio value="C">选项 C</ARadio>
|
||||
</ARadioGroup>
|
||||
<p>当前选中: {{ selectedValue }}</p>
|
||||
<button @click="toggleGroupDisabled" class="demo-btn">
|
||||
切换 Group Disabled
|
||||
</button>
|
||||
|
||||
<h3>ARadioGroup - 使用 options 数组渲染</h3>
|
||||
<ARadioGroup
|
||||
v-model="selectedValue"
|
||||
:options="options"
|
||||
@change="handleChange"
|
||||
class="demo-radio-group"
|
||||
/>
|
||||
<p>当前选中: {{ selectedValue }}</p>
|
||||
|
||||
<h3>单独 ARadio 使用</h3>
|
||||
<ARadio v-model="singleChecked" value="X" :allow-uncheck="true">
|
||||
单选 X
|
||||
</ARadio>
|
||||
<p>是否选中: {{ singleChecked }}</p>
|
||||
</div>
|
||||
<div>
|
||||
content
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.demo-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
padding: 40px 16px;
|
||||
}
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.demo-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px; /* 各部分垂直间距 */
|
||||
width: 100%;
|
||||
max-width: 600px; /* 页面居中显示 */
|
||||
background-color: #fff;
|
||||
padding: 24px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 12px rgba(0,0,0,0.08);
|
||||
}
|
||||
|
||||
.demo-radio-group {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
|
||||
.demo-btn {
|
||||
padding: 6px 16px;
|
||||
border: 1px solid #409eff;
|
||||
background-color: #fff;
|
||||
color: #409eff;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: 0.2s;
|
||||
}
|
||||
|
||||
.demo-btn:hover {
|
||||
background-color: #409eff;
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user