u
This commit is contained in:
1 parent
4d0d2f875e
commit
27263365ad
13 files changed
+265
-21
No files matched your search
@@ -99,6 +99,9 @@ $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 等容器的外部边框)
|
||||
|
||||
$bg-color-secondarycomponent: var(--ao-bg-color-secondarycomponent);
|
||||
$bg-color-secondarycomponent-hover: var(--ao-bg-color-secondarycomponent-hover);
|
||||
|
||||
// shadow
|
||||
// 基础/下层 投影 hover 使用的组件包括:表格 /
|
||||
$shadow-surface: var(--ao-shadow-surface);
|
||||
@@ -106,6 +109,8 @@ $shadow-surface: var(--ao-shadow-surface);
|
||||
$shadow-popup: var(--ao-shadow-popup);
|
||||
// 上层投影(警示/弹窗)使用的组件包括:全局提示 / 消息通知
|
||||
$shadow-overlay: var(--ao-shadow-overlay);
|
||||
// switch 投影
|
||||
$shadow-switch: var(--ao-shadow-switch);
|
||||
|
||||
// 内投影
|
||||
$shadow-inset-top: var(--ao-shadow-inset-top);
|
||||
|
||||
@@ -76,6 +76,8 @@
|
||||
|
||||
--ao-bg-color-component: #393939;
|
||||
--ao-border-color-component: #5e5e5e;
|
||||
--ao-bg-color-secondarycomponent: #4b4b4b;
|
||||
--ao-bg-color-secondarycomponent-hover: #5e5e5e;
|
||||
|
||||
// shadow阴影
|
||||
// 基础/下层 投影 hover 使用的组件包括:表格 /
|
||||
@@ -84,6 +86,8 @@
|
||||
--ao-shadow-popup: 0 0 4px rgba(0, 0, 0, 0.04), 0 0 8px rgba(0, 0, 0, 0.03);
|
||||
// 上层投影(警示/弹窗)使用的组件包括:全局提示 / 消息通知
|
||||
--ao-shadow-overlay: inset 0 0 0 1px hsla(0, 0%, 100%, 0.1), 0 4px 14px rgba(0, 0, 0, 0.25);
|
||||
// 开关投影
|
||||
--ao-shadow-switch: 0 4px 6px rgba(0, 0, 0, 0.06), 0 1px 10px rgba(0, 0, 0, 0.08), 0 2px 4px rgba(0, 0, 0, 0.12);
|
||||
// 内投影
|
||||
--ao-shadow-inset-top: inset 0 0.5px 0 #5e5e5e;
|
||||
--ao-shadow-inset-right: inset 0.5px 0 0 #5e5e5e;
|
||||
|
||||
@@ -77,15 +77,18 @@
|
||||
|
||||
--ao-bg-color-component: #e8e8e8;
|
||||
--ao-border-color-component: #ddd;
|
||||
--ao-bg-color-secondarycomponent: #ddd;
|
||||
--ao-bg-color-secondarycomponent-hover: #c6c6c6;
|
||||
|
||||
// shadow阴影
|
||||
// 基础/下层 投影 hover 使用的组件包括:表格 /
|
||||
--ao-shadow-surface: 0 1px 10px rgba(0, 0, 0, 5%), 0 4px 5px rgba(0, 0, 0, 8%), 0 2px 4px -1px rgba(0, 0, 0, 12%);
|
||||
// 中层投影 下拉 使用的组件包括:下拉菜单 / 气泡确认框 / 选择器 /
|
||||
--ao-shadow-popup: 0 0 4px rgba(0, 0, 0, 0.04), 0 0 8px rgba(0, 0, 0, 0.03);
|
||||
|
||||
// 上层投影(警示/弹窗)使用的组件包括:全局提示 / 消息通知
|
||||
--ao-shadow-overlay: 0 0 1px rgba(0, 0, 0, 0.3), 0 4px 14px rgba(0, 0, 0, 0.1);
|
||||
// 开关投影
|
||||
--ao-shadow-switch: 0 1px 10px rgba(0, 0, 0, 0.05), 0 4px 5px rgba(0, 0, 0, 0.08), 0 2px 4px -1px rgba(0, 0, 0, 0.12);
|
||||
--ao-shadow-inset-top: inset 0 0.5px 0 #dcdcdc;
|
||||
--ao-shadow-inset-right: inset 0.5px 0 0 #dcdcdc;
|
||||
--ao-shadow-inset-bottom: inset 0 -0.5px 0 #dcdcdc;
|
||||
|
||||
@@ -12,6 +12,11 @@ $checkbox-prefix: '#{$prefix}-checkbox';
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
|
||||
&.#{$checkbox-prefix}-disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
&.#{$checkbox-prefix}-checked {
|
||||
.#{$checkbox-prefix}-main {
|
||||
.#{$checkbox-prefix}-inner {
|
||||
|
||||
@@ -13,6 +13,7 @@ export { default as RadioGroup } from './radio/radio-group'
|
||||
export { default as Separator } from './separator'
|
||||
export { default as Space } from './space'
|
||||
export { default as Spin } from './spin'
|
||||
export { default as Switch } from './switch'
|
||||
export { default as Tag } from './tag/tag'
|
||||
export { default as TagGroup } from './tag/tag-group'
|
||||
export { default as Textarea } from './textarea'
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
@use './separator/index.scss' as *;
|
||||
@use './radio/index.scss' as *;
|
||||
@use './checkbox/index.scss' as *;
|
||||
@use './switch/index.scss' as *;
|
||||
@use './spin/index.scss' as *;
|
||||
|
||||
[class*='#{variables.$prefix}-'] {
|
||||
-webkit-box-sizing: border-box;
|
||||
|
||||
@@ -20,9 +20,8 @@ export default defineComponent({
|
||||
type: [String, Object, Function] as unknown as () => string | VNode | (() => VNode),
|
||||
required: false,
|
||||
},
|
||||
asyncConfirm: {
|
||||
type: Boolean,
|
||||
default: false, // 默认同步关闭,不显示 loading
|
||||
beforeConfirm: {
|
||||
type: Function as PropType<() => boolean | Promise<boolean>>,
|
||||
},
|
||||
confirmBtn: {
|
||||
type: null,
|
||||
@@ -68,21 +67,20 @@ export default defineComponent({
|
||||
return val
|
||||
}
|
||||
|
||||
const handleConfirm = () => {
|
||||
if (props.asyncConfirm) {
|
||||
confirmLoading.value = true
|
||||
const handleConfirm = async () => {
|
||||
confirmLoading.value = true
|
||||
let allow = true
|
||||
|
||||
emit('confirm', () => { // resolve
|
||||
popVisible.value = false
|
||||
confirmLoading.value = false
|
||||
}, () => { // reject
|
||||
confirmLoading.value = false
|
||||
})
|
||||
if (props.beforeConfirm) {
|
||||
allow = await Promise.resolve(props.beforeConfirm())
|
||||
}
|
||||
else {
|
||||
|
||||
if (allow) {
|
||||
popVisible.value = false
|
||||
emit('confirm')
|
||||
}
|
||||
|
||||
confirmLoading.value = false
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
|
||||
@@ -5,6 +5,7 @@ $spin-prefix: #{$prefix}-spin;
|
||||
.#{$spin-prefix}-wrapper {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.#{$spin-prefix} {
|
||||
@@ -23,6 +24,9 @@ $spin-prefix: #{$prefix}-spin;
|
||||
color: $text-color-base;
|
||||
}
|
||||
|
||||
&-icon {
|
||||
color: $primary-color;
|
||||
}
|
||||
&-tip {
|
||||
margin-top: 4px;
|
||||
font-size: 12px;
|
||||
|
||||
@@ -12,11 +12,22 @@ export default defineComponent({
|
||||
icon: { type: String as PropType<string>, default: 'mingcute:loading-line' },
|
||||
size: { type: Number as PropType<number>, default: 16 },
|
||||
rotate: { type: Number as PropType<number>, default: 360 },
|
||||
tip: { type: String as PropType<string>, default: '' },
|
||||
tip: {
|
||||
type: [String, Object, Function] as unknown as () => string | VNode | (() => VNode),
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
setup(props, { slots }) {
|
||||
const prefixCls = getPrefixCls('spin')
|
||||
|
||||
const renderTip = () => {
|
||||
if (slots.tip)
|
||||
return slots.tip()
|
||||
if (typeof props.tip === 'function')
|
||||
return props.tip()
|
||||
return props.tip
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (!props.loading && !slots.default)
|
||||
return null
|
||||
@@ -29,7 +40,6 @@ export default defineComponent({
|
||||
class={`${prefixCls}`}
|
||||
style={{
|
||||
background: slots.default ? `rgba(255,255,255,${props.opacity})` : 'transparent',
|
||||
borderRadius: props.rounded ? '4px' : 0,
|
||||
}}
|
||||
>
|
||||
<div class={`${prefixCls}-content`}>
|
||||
@@ -37,8 +47,9 @@ export default defineComponent({
|
||||
icon={props.icon}
|
||||
size={props.size}
|
||||
spin
|
||||
class={`${prefixCls}-icon`}
|
||||
/>
|
||||
{props.tip && <span class={`${prefixCls}-tip`}>{props.tip}</span>}
|
||||
{renderTip() && <span class={`${prefixCls}-tip`}>{renderTip()}</span>}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
@use '../_styles/_variables.scss' as *;
|
||||
@use 'sass:color';
|
||||
|
||||
$switch-prefix: '#{$prefix}-switch';
|
||||
|
||||
.#{$switch-prefix} {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
min-width: 44px;
|
||||
height: 22px;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
line-height: 22px;
|
||||
vertical-align: middle;
|
||||
background-color: $bg-color-secondarycomponent;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s cubic-bezier(0.34, 0.69, 0.1, 1);
|
||||
|
||||
// 普通 hover,只在非 disabled 下生效
|
||||
&:not(.#{$switch-prefix}-disabled):not(.#{$switch-prefix}-loading):hover {
|
||||
background-color: $bg-color-secondarycomponent-hover;
|
||||
}
|
||||
|
||||
// checked 状态 hover,只在非 disabled 下生效
|
||||
&.#{$switch-prefix}-checked:not(.#{$switch-prefix}-disabled):not(.#{$switch-prefix}-loading):hover {
|
||||
background-color: $primary-color-hover;
|
||||
}
|
||||
|
||||
&.#{$switch-prefix}-checked {
|
||||
background-color: $primary-color;
|
||||
|
||||
.#{$switch-prefix}-handle {
|
||||
left: calc(100% - 4px - 14px); // 100% - 右边距 - handle宽度
|
||||
}
|
||||
|
||||
.#{$switch-prefix}-text {
|
||||
right: auto;
|
||||
left: calc(4px + 4px); // left + left
|
||||
}
|
||||
.#{$switch-prefix}-text-holder {
|
||||
margin: 0 24px 0 8px;
|
||||
}
|
||||
}
|
||||
|
||||
&.#{$switch-prefix}-loading {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
&.#{$switch-prefix}-disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.#{$switch-prefix}-handle {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 4px;
|
||||
transform: translateY(-50%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
font-size: 12px;
|
||||
background-color: #fff;
|
||||
border-radius: 50%;
|
||||
transition: all 0.2s cubic-bezier(0.34, 0.69, 0.1, 1);
|
||||
box-shadow: $shadow-switch;
|
||||
}
|
||||
|
||||
.#{$switch-prefix}-text-holder {
|
||||
margin: 0 8px 0 24px; // 8 = 4 + 4 22 = 14 + 4 + 4 + 2
|
||||
font-size: 12px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.#{$switch-prefix}-text {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: calc(4px + 4px);
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
import type { PropType, VNode } from 'vue'
|
||||
import { computed, defineComponent, ref, watch } from 'vue'
|
||||
import { getPrefixCls } from '../_utils/getPrefixCls'
|
||||
import Icon from '../icon/index'
|
||||
import Spin from '../spin/index'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Switch',
|
||||
props: {
|
||||
modelValue: {
|
||||
type: [String, Number, Boolean] as PropType<string | number | boolean>,
|
||||
default: false,
|
||||
},
|
||||
checkedValue: {
|
||||
type: [String, Number, Boolean] as PropType<string | number | boolean>,
|
||||
default: true,
|
||||
},
|
||||
uncheckedValue: {
|
||||
type: [String, Number, Boolean] as PropType<string | number | boolean>,
|
||||
default: false,
|
||||
},
|
||||
checkedContent: {
|
||||
type: [String, Object, Function] as unknown as () => string | VNode | (() => VNode),
|
||||
default: '',
|
||||
},
|
||||
uncheckedContent: {
|
||||
type: [String, Object, Function] as unknown as () => string | VNode | (() => VNode),
|
||||
default: '',
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
class: {
|
||||
type: [String, Array, Object],
|
||||
default: '',
|
||||
},
|
||||
style: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue', 'change'],
|
||||
setup(props, { emit, slots }) {
|
||||
const prefixCls = getPrefixCls('switch')
|
||||
|
||||
const _checked = ref(props.modelValue)
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
_checked.value = val
|
||||
},
|
||||
)
|
||||
const isChecked = computed(() => _checked.value === props.checkedValue)
|
||||
|
||||
const toggle = (e: Event) => {
|
||||
const newValue = isChecked.value ? props.uncheckedValue : props.checkedValue
|
||||
_checked.value = newValue
|
||||
|
||||
emit('update:modelValue', newValue)
|
||||
emit('change', newValue, e)
|
||||
}
|
||||
|
||||
const displayContent = computed(() => {
|
||||
// 优先使用插槽
|
||||
if (isChecked.value && slots.checkedContent) {
|
||||
return slots.checkedContent()
|
||||
}
|
||||
else if (!isChecked.value && slots.uncheckedContent) {
|
||||
return slots.uncheckedContent()
|
||||
}
|
||||
|
||||
// fallback 到 props
|
||||
return isChecked.value
|
||||
? typeof props.checkedContent === 'function'
|
||||
? props.checkedContent()
|
||||
: props.checkedContent
|
||||
: typeof props.uncheckedContent === 'function'
|
||||
? props.uncheckedContent()
|
||||
: props.uncheckedContent
|
||||
})
|
||||
|
||||
return () => (
|
||||
<button
|
||||
type="button"
|
||||
role="switch"
|
||||
disabled={props.disabled || props.loading}
|
||||
class={[
|
||||
prefixCls,
|
||||
props.class,
|
||||
{
|
||||
[`${prefixCls}-checked`]: isChecked.value,
|
||||
[`${prefixCls}-disabled`]: props.disabled,
|
||||
[`${prefixCls}-loading`]: props.loading,
|
||||
},
|
||||
]}
|
||||
onClick={toggle}
|
||||
>
|
||||
<span class={`${prefixCls}-handle`}>
|
||||
{props.loading && <Spin loading size={12} />}
|
||||
</span>
|
||||
<span class={`${prefixCls}-text-holder`}>{displayContent.value}</span>
|
||||
<span class={`${prefixCls}-text`}>{displayContent.value}</span>
|
||||
</button>
|
||||
)
|
||||
},
|
||||
})
|
||||
@@ -1,13 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const value = ref('')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
content
|
||||
<div class="main">
|
||||
<ASwitch v-model="value" loading checked-value="1" unchecked-value="2" checked-content="1" unchecked-content="2" />
|
||||
{{ value }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
.main {
|
||||
width: 100%;
|
||||
height: 800px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
</style>
|
||||
@@ -28,6 +28,7 @@ declare module 'vue' {
|
||||
ASeparator: typeof import('@ao/ao-design')['Separator']
|
||||
ASpace: typeof import('@ao/ao-design')['Space']
|
||||
ASpin: typeof import('@ao/ao-design')['Spin']
|
||||
ASwitch: typeof import('@ao/ao-design')['Switch']
|
||||
ATag: typeof import('@ao/ao-design')['Tag']
|
||||
ATagGroup: typeof import('@ao/ao-design')['TagGroup']
|
||||
ATextarea: typeof import('@ao/ao-design')['Textarea']
|
||||
|
||||
Reference in new issue
Block a user