diff --git a/code/base-project/base-vue/.vscode/vue3.code-snippets b/code/base-project/base-vue/.vscode/vue3.code-snippets index f9b1f88b..390bbe5a 100644 --- a/code/base-project/base-vue/.vscode/vue3.code-snippets +++ b/code/base-project/base-vue/.vscode/vue3.code-snippets @@ -29,12 +29,20 @@ "export default defineComponent({", " name: '${1:AoComponent}',", " props: {", + " class: {", + " type: [String, Array, Object],", + " default: '',", + " },", + " style: {", + " type: Object,", + " default: () => ({}),", + " },", " },", " emits: [],", " setup(props, { slots, emit }) {", " const prefixCls = getPrefixCls('${2:component}');", " return () => (", - "
", + "
", " {slots.default?.()}", "
", " );", 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 eec18171..13379ffb 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 @@ -13,30 +13,48 @@ $default-color-hover: var(--ao-default-color-hover); // hover态 $default-color-focus: var(--ao-default-color-focus); // focus态,包括鼠标和键盘 $default-color-active: var(--ao-default-color-active); // 点击态 $default-color-disabled: var(--ao-default-color-disabled); // 禁用态 +$default-color-light: var(--ao-default-color-light); +$default-color-light-active: var(--ao-default-color-light-active); // 基础颜色的扩展 用于 hover / 聚焦 / 禁用 / 点击 等状态 $primary-color-hover: var(--ao-primary-color-hover); // hover态 $primary-color-focus: var(--ao-primary-color-focus); // focus态,包括鼠标和键盘 $primary-color-active: var(--ao-primary-color-active); // 点击态 $primary-color-disabled: var(--ao-primary-color-disabled); // 禁用态 +$primary-color-light: var(--ao-primary-color-light); +$primary-color-light-active: var(--ao-primary-color-light-active); // 警告色扩展 $warning-color-hover: var(--ao-warning-color-hover); $warning-color-focus: var(--ao-warning-color-focus); $warning-color-active: var(--ao-warning-color-active); $warning-color-disabled: var(--ao-warning-color-disabled); +$warning-color-light: var(--ao-warning-color-light); +$warning-color-light-active: var(--ao-warning-color-light-active); // 失败/错误色扩展 $danger-color-hover: var(--ao-danger-color-hover); $danger-color-focus: var(--ao-danger-color-focus); $danger-color-active: var(--ao-danger-color-active); $danger-color-disabled: var(--ao-danger-color-disabled); +$danger-color-light: var(--ao-danger-color-light); +$danger-color-light-active: var(--ao-danger-color-light-active); // 成功色扩展 $success-color-hover: var(--ao-success-color-hover); $success-color-focus: var(--ao-success-color-focus); $success-color-active: var(--ao-success-color-active); $success-color-disabled: var(--ao-success-color-disabled); +$success-color-light: var(--ao-success-color-light); +$success-color-light-active: var(--ao-success-color-light-active); + +// 状态背景色 +$status-base-bg-color: var(--ao-status-base-bg-color); +$status-anti-bg-color: var(--ao-status-anti-bg-color); +$status-info-bg-color: var(--ao-status-info-bg-color); +$status-success-bg-color: var(--ao-status-success-bg-color); +$status-warning-bg-color: var(--ao-status-warning-bg-color); +$status-danger-bg-color: var(--ao-status-danger-bg-color); // 文本颜色 $text-color-base: var(--ao-text-color-base); // 色彩-文字-主要 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 46ebd5f4..703ca4ba 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 @@ -9,26 +9,43 @@ --ao-default-color-focus: #505050; --ao-default-color-active: #5c5c5c; --ao-default-color-disabled: #2c2c2c; + --ao-default-color-light: #f3f3f3; + --ao-default-color-light-active: #ddd; --ao-primary-color-hover: #2667d4; --ao-primary-color-focus: #173463; --ao-primary-color-active: #699ef5; --ao-primary-color-disabled: #143975; + --ao-primary-color-light: #1b2f51; + --ao-primary-color-light-active: #173463; --ao-warning-color-hover: #a75d2b; --ao-warning-color-focus: #582f21; --ao-warning-color-active: #dc7633; --ao-warning-color-disabled: #733c23; + --ao-warning-color-light: #4f2a1d; + --ao-warning-color-light-active: #582f21; --ao-danger-color-hover: #a03f46; --ao-danger-color-focus: #5e2a2d; --ao-danger-color-active: #de6670; --ao-danger-color-disabled: #703439; + --ao-danger-color-light: #472324; + --ao-danger-color-light-active: #5e2a2d; --ao-success-color-hover: #0d7a55; --ao-success-color-focus: #1a4230; --ao-success-color-active: #43af8a; --ao-success-color-disabled: #17533d; + --ao-success-color-light: #193a2a; + --ao-success-color-light-active: #1a4230; + + --ao-status-base-bg-color: #242424; + --ao-status-anti-bg-color: #c6cacd; // 反色,用于tooltip默认背景色 + --ao-status-info-bg-color: #233448; + --ao-status-success-bg-color: #253929; + --ao-status-warning-bg-color: #453522; + --ao-status-danger-bg-color: #442927; --ao-text-color-base: rgba(255, 255, 255, 90%); /* 主要文字 */ --ao-text-color-secondary: rgba(255, 255, 255, 55%); /* 次要文字 / 辅助信息 */ @@ -60,10 +77,9 @@ // 基础/下层 投影 hover 使用的组件包括:表格 / --ao-shadow-surface: 0 4px 6px rgba(0, 0, 0, 6%), 0 1px 10px rgba(0, 0, 0, 8%), 0 2px 4px rgba(0, 0, 0, 12%); // 中层投影 下拉 使用的组件包括:下拉菜单 / 气泡确认框 / 选择器 / - --ao-shadow-popup: 0 8px 10px rgba(0, 0, 0, 12%), 0 3px 14px rgba(0, 0, 0, 10%), 0 5px 5px rgba(0, 0, 0, 16%); + --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 16px 24px rgba(0, 0, 0, 14%), 0 6px 30px rgba(0, 0, 0, 12%), 0 8px 10px rgba(0, 0, 0, 20%); - + --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-inset-top: inset 0 0.5px 0 #5e5e5e; --ao-shadow-inset-right: inset 0.5px 0 0 #5e5e5e; 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 1167b2b1..da7caf2a 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 @@ -10,26 +10,43 @@ --ao-default-color-focus: #cfcfcf; --ao-default-color-active: #ababab; --ao-default-color-disabled: #eeeeee; + --ao-default-color-light: #f3f3f3; + --ao-default-color-light-active: #ddd; --ao-primary-color-hover: #366ef4; --ao-primary-color-focus: #d9e1ff; --ao-primary-color-active: #003cab; --ao-primary-color-disabled: #b5c7ff; + --ao-primary-color-light: #f2f3ff; + --ao-primary-color-light-active: #d9e1ff; --ao-warning-color-hover: #fa9550; --ao-warning-color-focus: #ffd9c2; --ao-warning-color-active: #be5a00; --ao-warning-color-disabled: #ffb98c; + --ao-warning-color-light: #fff1e9; + --ao-warning-color-light-active: #ffd9c2; --ao-danger-color-hover: #f6685d; --ao-danger-color-focus: #ffd8d2; --ao-danger-color-active: #ad352f; --ao-danger-color-disabled: #ffb9b0; + --ao-danger-color-light: #fff0ed; + --ao-danger-color-light-active: #ffd8d2; --ao-success-color-hover: #56c08d; --ao-success-color-focus: #c6f3d7; --ao-success-color-active: #008858; --ao-success-color-disabled: #92dab2; + --ao-success-color-light: #e3f9e9; + --ao-success-color-light-active: #c6f3d7; + + --ao-status-base-bg-color: #fff; + --ao-status-anti-bg-color: #41464c; // 反色,用于tooltip默认背景色 + --ao-status-info-bg-color: #f2f3ff; + --ao-status-success-bg-color: #ecf7ec; + --ao-status-warning-bg-color: #fff8ea; + --ao-status-danger-bg-color: #fef2ed; --ao-text-color-base: rgba(0, 0, 0, 90%); /* 主要文字 */ --ao-text-color-secondary: rgba(0, 0, 0, 60%); /* 次要文字 / 辅助信息 */ @@ -61,13 +78,10 @@ // 基础/下层 投影 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 3px 14px 2px rgba(0, 0, 0, 5%), 0 8px 10px 1px rgba(0, 0, 0, 6%), 0 5px 5px -3px rgba(0, 0, 0, 10%); - // 上层投影(警示/弹窗)使用的组件包括:全局提示 / 消息通知 - --ao-shadow-overlay: - 0 6px 30px 5px rgba(0, 0, 0, 5%), 0 16px 24px 2px rgba(0, 0, 0, 4%), 0 8px 10px -5px rgba(0, 0, 0, 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: 0 0 1px rgba(0, 0, 0, 0.3), 0 4px 14px rgba(0, 0, 0, 0.1); --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; diff --git a/code/base-project/base-vue/packages/ao-design/src/components.ts b/code/base-project/base-vue/packages/ao-design/src/components.ts index a136277a..92d4099b 100644 --- a/code/base-project/base-vue/packages/ao-design/src/components.ts +++ b/code/base-project/base-vue/packages/ao-design/src/components.ts @@ -7,4 +7,7 @@ export { default as Input } from './input' export { default as Popconfirm } from './popconfirm' export { default as Popup } from './popup' export { default as Spin } from './spin' +export { default as Tag } from './tag' +export { default as TagGroup } from './tag-group' export { default as Textarea } from './textarea' +export { default as Tooltip } from './tooltip' 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 a9831b14..cf5ba8e3 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 @@ -10,3 +10,6 @@ @use './message/index.scss' as *; @use './notification/index.scss' as *; @use './drawer/index.scss' as *; +@use './tooltip/index.scss' as *; +@use './tag/index.scss' as *; +@use './tag-group/index.scss' as *; diff --git a/code/base-project/base-vue/packages/ao-design/src/message/demo.vue b/code/base-project/base-vue/packages/ao-design/src/message/demo.vue index a5d1712d..66b8396e 100644 --- a/code/base-project/base-vue/packages/ao-design/src/message/demo.vue +++ b/code/base-project/base-vue/packages/ao-design/src/message/demo.vue @@ -21,6 +21,8 @@ function showMessageType(type: any) { message.success({ message: `这里是 ${type} 提示`, type, + duration: 0, + theme: 'light', }) } @@ -76,6 +78,9 @@ function showMessageType(type: any) {
+ + default + info @@ -85,7 +90,7 @@ function showMessageType(type: any) { warning - + error diff --git a/code/base-project/base-vue/packages/ao-design/src/message/index.scss b/code/base-project/base-vue/packages/ao-design/src/message/index.scss index 94fe26c6..95617d8c 100644 --- a/code/base-project/base-vue/packages/ao-design/src/message/index.scss +++ b/code/base-project/base-vue/packages/ao-design/src/message/index.scss @@ -47,22 +47,34 @@ $message-prefix: #{$prefix}-message; } &-info { - color: $primary-color; + .#{$message-prefix}-icon { + color: $primary-color; + } } &-success { - color: $success-color; + .#{$message-prefix}-icon { + color: $success-color; + } } &-warning { - color: $warning-color; + .#{$message-prefix}-icon { + color: $warning-color; + } } - &-error { - color: $danger-color; + &-danger { + .#{$message-prefix}-icon { + color: $danger-color; + } } &-question { - color: $primary-color; + .#{$message-prefix}-icon { + color: $primary-color; + } } &-loading { - color: $primary-color; + .#{$message-prefix}-icon { + color: $primary-color; + } } &-placement { &-top { @@ -128,3 +140,27 @@ $message-prefix: #{$prefix}-message; } } } +.#{$message-prefix}-theme-normal.#{$message-prefix} { + background-color: $status-base-bg-color; +} +.#{$message-prefix}-theme-light.#{$message-prefix}-default { + background-color: $status-base-bg-color; +} +.#{$message-prefix}-theme-light.#{$message-prefix}-info { + background-color: $status-info-bg-color; +} +.#{$message-prefix}-theme-light.#{$message-prefix}-success { + background-color: $status-success-bg-color; +} +.#{$message-prefix}-theme-light.#{$message-prefix}-warning { + background-color: $status-warning-bg-color; +} +.#{$message-prefix}-theme-light.#{$message-prefix}-danger { + background-color: $status-danger-bg-color; +} +.#{$message-prefix}-theme-light.#{$message-prefix}-loading { + background-color: $status-info-bg-color; +} +.#{$message-prefix}-theme-light.#{$message-prefix}-question { + background-color: $status-info-bg-color; +} diff --git a/code/base-project/base-vue/packages/ao-design/src/message/index.ts b/code/base-project/base-vue/packages/ao-design/src/message/index.ts index f13e60f5..bca1272c 100644 --- a/code/base-project/base-vue/packages/ao-design/src/message/index.ts +++ b/code/base-project/base-vue/packages/ao-design/src/message/index.ts @@ -14,11 +14,13 @@ type Placement | 'bottom-end' | 'center' -type MessageType = 'info' | 'success' | 'warning' | 'error' | 'loading' | 'question' +type MessageType = 'default' | 'info' | 'success' | 'warning' | 'danger' | 'loading' | 'question' +type MessageTheme = 'normal' | 'light' interface MessageOptions { message: string | VNode | (() => VNode) type?: MessageType + theme?: MessageTheme placement?: Placement duration?: number showClose?: boolean @@ -30,10 +32,11 @@ interface MessageHandle { interface MessageFn { (msg: string | MessageOptions): MessageHandle + open: (msg: string | MessageOptions) => MessageHandle info: (msg: string | MessageOptions) => MessageHandle success: (msg: string | MessageOptions) => MessageHandle warning: (msg: string | MessageOptions) => MessageHandle - error: (msg: string | MessageOptions) => MessageHandle + danger: (msg: string | MessageOptions) => MessageHandle closeAll: () => void } @@ -68,13 +71,13 @@ function getTransform(placement: Placement, hidden: boolean) { export function useMessage(): MessageFn { const createMessage = ( msg: string | MessageOptions, - defaultType: MessageType = 'info', + defaultType: MessageType = 'default', ): MessageHandle => { const opts: MessageOptions = typeof msg === 'string' ? { message: msg, type: defaultType } : { ...msg, type: msg.type || defaultType } - const { message, type, placement = 'top', duration = 3000, showClose } = opts + const { message, type, theme, placement = 'top', duration = 3000, showClose } = opts const container = getContainer(placement) let timer: number | undefined let handle: MessageHandle @@ -83,6 +86,7 @@ export function useMessage(): MessageFn { render() { return h(MessageItem, { type, + theme, message, placement, showClose, @@ -140,10 +144,11 @@ export function useMessage(): MessageFn { } const api = ((msg: string | MessageOptions) => createMessage(msg)) as MessageFn + api.open = msg => createMessage(msg, 'default') api.info = msg => createMessage(msg, 'info') api.success = msg => createMessage(msg, 'success') api.warning = msg => createMessage(msg, 'warning') - api.error = msg => createMessage(msg, 'error') + api.danger = msg => createMessage(msg, 'danger') api.closeAll = () => { instances.slice().forEach(instance => instance.close()) } diff --git a/code/base-project/base-vue/packages/ao-design/src/message/item.tsx b/code/base-project/base-vue/packages/ao-design/src/message/item.tsx index 497414fb..9282c911 100644 --- a/code/base-project/base-vue/packages/ao-design/src/message/item.tsx +++ b/code/base-project/base-vue/packages/ao-design/src/message/item.tsx @@ -8,7 +8,8 @@ const prefixCls = getPrefixCls('message') export default defineComponent({ name: 'MessageItem', props: { - type: { type: String, default: 'info' }, + type: { type: String, default: 'default' }, + theme: { type: String, default: 'normal' }, message: { type: [String, Object, Function] as unknown as () => string | VNode | (() => VNode), required: true, @@ -23,7 +24,7 @@ export default defineComponent({ setup(props) { const iconMap: Record = { success: 'ix:success-filled', - error: 'ix:error-filled', + danger: 'ix:error-filled', warning: 'ix:warning-filled', info: 'ix:info-filled', question: 'ix:question-filled', @@ -36,14 +37,16 @@ export default defineComponent({ } return () => (
- + {props.type !== 'default' && ( + + )} {renderMessage(props.message)} diff --git a/code/base-project/base-vue/packages/ao-design/src/notification/demo.vue b/code/base-project/base-vue/packages/ao-design/src/notification/demo.vue index d7f2dc4c..3b3a1fd0 100644 --- a/code/base-project/base-vue/packages/ao-design/src/notification/demo.vue +++ b/code/base-project/base-vue/packages/ao-design/src/notification/demo.vue @@ -1,59 +1,85 @@ @@ -65,4 +91,9 @@ function showCustomMessage() { gap: 16px; margin-top: 40px; } + +.row { + display: flex; + gap: 12px; +} diff --git a/code/base-project/base-vue/packages/ao-design/src/notification/index.scss b/code/base-project/base-vue/packages/ao-design/src/notification/index.scss index 9f3f7701..c9297603 100644 --- a/code/base-project/base-vue/packages/ao-design/src/notification/index.scss +++ b/code/base-project/base-vue/packages/ao-design/src/notification/index.scss @@ -24,7 +24,6 @@ $notification-prefix: #{$prefix}-notification; border: 0; margin: 0; display: flex; - background-color: #ecf7ec; &-icon { text-align: center; @@ -47,8 +46,6 @@ $notification-prefix: #{$prefix}-notification; &-title { font-size: 16px; font-weight: 600; - line-height: 1; - small { margin: 0px 6px; font-size: 14px; @@ -76,26 +73,35 @@ $notification-prefix: #{$prefix}-notification; word-break: break-all; } - &-footer { - } - &-info { - color: $primary-color; + .#{$notification-prefix}-icon { + color: $primary-color; + } } &-success { - color: $success-color; + .#{$notification-prefix}-icon { + color: $success-color; + } } &-warning { - color: $warning-color; + .#{$notification-prefix}-icon { + color: $warning-color; + } } - &-error { - color: $danger-color; + &-danger { + .#{$notification-prefix}-icon { + color: $danger-color; + } } &-question { - color: $primary-color; + .#{$notification-prefix}-icon { + color: $primary-color; + } } &-loading { - color: $primary-color; + .#{$notification-prefix}-icon { + color: $primary-color; + } } &-placement { @@ -125,3 +131,27 @@ $notification-prefix: #{$prefix}-notification; } } } +.#{$notification-prefix}-theme-normal.#{$notification-prefix} { + background-color: $status-base-bg-color; +} +.#{$notification-prefix}-theme-light.#{$notification-prefix}-default { + background-color: $status-base-bg-color; +} +.#{$notification-prefix}-theme-light.#{$notification-prefix}-info { + background-color: $status-info-bg-color; +} +.#{$notification-prefix}-theme-light.#{$notification-prefix}-success { + background-color: $status-success-bg-color; +} +.#{$notification-prefix}-theme-light.#{$notification-prefix}-warning { + background-color: $status-warning-bg-color; +} +.#{$notification-prefix}-theme-light.#{$notification-prefix}-danger { + background-color: $status-danger-bg-color; +} +.#{$notification-prefix}-theme-light.#{$notification-prefix}-loading { + background-color: $status-info-bg-color; +} +.#{$notification-prefix}-theme-light.#{$notification-prefix}-question { + background-color: $status-info-bg-color; +} diff --git a/code/base-project/base-vue/packages/ao-design/src/notification/index.ts b/code/base-project/base-vue/packages/ao-design/src/notification/index.ts index c2c270ed..e365636a 100644 --- a/code/base-project/base-vue/packages/ao-design/src/notification/index.ts +++ b/code/base-project/base-vue/packages/ao-design/src/notification/index.ts @@ -9,13 +9,14 @@ type Placement | 'bottom-start' | 'bottom-end' -type NotificationType = 'info' | 'success' | 'warning' | 'error' | 'loading' | 'question' +type NotificationType = 'default' | 'info' | 'success' | 'warning' | 'danger' | 'loading' | 'question' +type NotificationTheme = 'normal' | 'light' interface NotificationOptions { message: string | VNode | (() => VNode) - title: string subTitle?: string + theme?: NotificationTheme type?: NotificationType placement?: Placement duration?: number @@ -28,10 +29,11 @@ interface NotificationHandle { interface MessageFn { (msg: NotificationOptions): NotificationHandle + open: (msg: NotificationOptions) => NotificationHandle info: (msg: NotificationOptions) => NotificationHandle success: (msg: NotificationOptions) => NotificationHandle warning: (msg: NotificationOptions) => NotificationHandle - error: (msg: NotificationOptions) => NotificationHandle + danger: (msg: NotificationOptions) => NotificationHandle closeAll: () => void } @@ -65,9 +67,9 @@ function getTransform(placement: Placement, hidden: boolean) { export function useNotification(): MessageFn { const createNotification = ( opts: NotificationOptions, - defaultType: NotificationType = 'info', + defaultType: NotificationType = 'default', ): NotificationHandle => { - const { message, title, subTitle, type = defaultType, placement = 'top-end', duration = 3000, showClose } = opts + const { message, title, subTitle, type = defaultType, theme, placement = 'top-end', duration = 3000, showClose } = opts const container = getContainer(placement) let timer: number | undefined @@ -77,6 +79,7 @@ export function useNotification(): MessageFn { render() { return h(MessageItem, { type, + theme, title, subTitle, message, @@ -136,10 +139,11 @@ export function useNotification(): MessageFn { } const api = ((msg: NotificationOptions) => createNotification(msg)) as MessageFn + api.open = msg => createNotification(msg, 'default') api.info = msg => createNotification(msg, 'info') api.success = msg => createNotification(msg, 'success') api.warning = msg => createNotification(msg, 'warning') - api.error = msg => createNotification(msg, 'error') + api.danger = msg => createNotification(msg, 'danger') api.closeAll = () => { instances.slice().forEach(instance => instance.close()) } diff --git a/code/base-project/base-vue/packages/ao-design/src/notification/item.tsx b/code/base-project/base-vue/packages/ao-design/src/notification/item.tsx index 0e9e0fdf..c88e8cbe 100644 --- a/code/base-project/base-vue/packages/ao-design/src/notification/item.tsx +++ b/code/base-project/base-vue/packages/ao-design/src/notification/item.tsx @@ -8,7 +8,8 @@ const prefixCls = getPrefixCls('notification') export default defineComponent({ name: 'NotificationItem', props: { - type: { type: String, default: 'info' }, + type: { type: String, default: 'default' }, + theme: { type: String, default: 'normal' }, title: { type: String, required: true }, message: { type: [String, Object, Function] as unknown as () => string | VNode | (() => VNode), @@ -25,7 +26,7 @@ export default defineComponent({ setup(props) { const iconMap: Record = { success: 'ix:success-filled', - error: 'ix:error-filled', + danger: 'ix:error-filled', warning: 'ix:warning-filled', info: 'ix:info-filled', question: 'ix:question-filled', @@ -38,15 +39,16 @@ export default defineComponent({ } return () => (
- - + {props.type !== 'default' && ( + + )}
diff --git a/code/base-project/base-vue/packages/ao-design/src/popup/index.scss b/code/base-project/base-vue/packages/ao-design/src/popup/index.scss index c52dc0f9..08017fcb 100644 --- a/code/base-project/base-vue/packages/ao-design/src/popup/index.scss +++ b/code/base-project/base-vue/packages/ao-design/src/popup/index.scss @@ -23,14 +23,16 @@ $popup-prefix: '#{$prefix}-popup'; content: ''; position: absolute; inset: 0; - background: $bg-color-container; + background-color: $bg-color-container; transform: rotate(45deg); transform-origin: center; } } + &-content { padding: 4px 8px; } + // 上方弹出,箭头在底部 &[data-popup-placement^='top'] > &-arrow { bottom: -4px; diff --git a/code/base-project/base-vue/packages/ao-design/src/popup/index.tsx b/code/base-project/base-vue/packages/ao-design/src/popup/index.tsx index 24cea996..a97dbfba 100644 --- a/code/base-project/base-vue/packages/ao-design/src/popup/index.tsx +++ b/code/base-project/base-vue/packages/ao-design/src/popup/index.tsx @@ -53,11 +53,11 @@ export default defineComponent({ type: Boolean, default: false, }, - popupClass: { + class: { type: [String, Array, Object], default: '', }, - popupStyle: { + style: { type: Object, default: () => ({}), }, @@ -65,6 +65,8 @@ export default defineComponent({ type: [String, Object, Function] as unknown as () => string | VNode | (() => VNode), default: '', }, + showDelay: { type: Number, default: 0 }, + hideDelay: { type: Number, default: 0 }, }, emits: ['update:visible'], setup(props, { slots, emit }) { @@ -75,6 +77,10 @@ export default defineComponent({ const arrowRef = ref(null) // 箭头引用 const localVisible = ref(props.visible) const hasRendered = ref(props.visible) + // 定时器 + let showTimer: number | null = null + let hideTimer: number | null = null + let cleanup: (() => void) | null = null const { floatingStyles, middlewareData, placement, update } = useFloating( @@ -135,43 +141,65 @@ export default defineComponent({ floatEl.style.width = `${triggerEl.offsetWidth}px` } + const clearTimers = () => { + if (showTimer) { + clearTimeout(showTimer) + showTimer = null + } + if (hideTimer) { + clearTimeout(hideTimer) + hideTimer = null + } + } + const showPopup = async () => { - if (!hasRendered.value) - hasRendered.value = true - await nextTick() + clearTimers() + showTimer = window.setTimeout(async () => { + if (!hasRendered.value) + hasRendered.value = true + await nextTick() - fitFloatingWidth() - localVisible.value = true - - setupAutoUpdate() - emit('update:visible', true) + fitFloatingWidth() + localVisible.value = true + setupAutoUpdate() + emit('update:visible', true) + }, props.showDelay) } const hidePopup = async () => { - localVisible.value = false - - clearAutoUpdate() - emit('update:visible', false) + clearTimers() + hideTimer = window.setTimeout(() => { + localVisible.value = false + clearAutoUpdate() + emit('update:visible', false) + }, props.hideDelay) } const togglePopup = async () => { + clearTimers() + if (!hasRendered.value) hasRendered.value = true await nextTick() - fitFloatingWidth() - localVisible.value = !localVisible.value - await nextTick() - - if (localVisible.value) { - setupAutoUpdate() + if (!localVisible.value) { + // 需要显示 + showTimer = window.setTimeout(async () => { + localVisible.value = true + await nextTick() + setupAutoUpdate() + emit('update:visible', true) + }, props.showDelay) } else { - clearAutoUpdate() + // 需要隐藏 + hideTimer = window.setTimeout(() => { + localVisible.value = false + clearAutoUpdate() + emit('update:visible', false) + }, props.hideDelay) } - - emit('update:visible', localVisible.value) } // 事件绑定 @@ -306,11 +334,11 @@ export default defineComponent({
diff --git a/code/base-project/base-vue/packages/ao-design/src/tag-group/index.scss b/code/base-project/base-vue/packages/ao-design/src/tag-group/index.scss new file mode 100644 index 00000000..41da7890 --- /dev/null +++ b/code/base-project/base-vue/packages/ao-design/src/tag-group/index.scss @@ -0,0 +1,12 @@ +@use '../_styles/_variables.scss' as *; +@use 'sass:color'; + +$tag-prefix: '#{$prefix}-tag'; + +$tag-group-prefix: '#{$prefix}-tag-group'; + +.#{$tag-group-prefix} { + > .#{$tag-prefix}:not(:last-child) { + margin-right: 8px; + } +} diff --git a/code/base-project/base-vue/packages/ao-design/src/tag-group/index.tsx b/code/base-project/base-vue/packages/ao-design/src/tag-group/index.tsx new file mode 100644 index 00000000..2a447bc8 --- /dev/null +++ b/code/base-project/base-vue/packages/ao-design/src/tag-group/index.tsx @@ -0,0 +1,73 @@ +import type { PropType, VNode } from 'vue' +import { cloneVNode, defineComponent, ref } from 'vue' +import { getPrefixCls } from '../_utils/getPrefixCls' +import Tag from '../tag/index' + +export default defineComponent({ + name: 'TagGroup', + props: { + modelValue: { type: Array as PropType<(string | number)[]>, default: () => [] }, + multiple: { type: Boolean, default: true }, + options: { + type: Array as PropType>, + default: () => [], + }, + checkedProps: { + type: Object as PropType<{ + type?: string + status?: string + }>, + default: () => ({ + type: 'solid', + status: 'primary', + }), + }, + uncheckedProps: { + type: Object as PropType<{ + type?: string + status?: string + }>, + default: () => ({ + type: 'solid', + status: 'default', + }), + }, + class: { + type: [String, Array, Object], + default: '', + }, + style: { + type: Object, + default: () => ({}), + }, + }, + emits: [], + setup(props, { slots }) { + const prefixCls = getPrefixCls('tag-group') + const selectedValues = ref([...props.modelValue]) + + const renderSlotTags = () => { + const slotNodes = slots.default?.() || [] + return slotNodes.map((vnode: VNode) => { + // 判断是否是 Tag 组件 + if (vnode.type === Tag) { + const value = (vnode.props as any)?.value + const checked = selectedValues.value.includes(value) + return cloneVNode(vnode, { + checked, + 'type': checked ? props.checkedProps.type : props.uncheckedProps.type, + 'status': checked ? props.checkedProps.status : props.uncheckedProps.status, + 'onUpdate:checked': (val: boolean) => handleTagClick(value, val), + }) + } + return vnode + }) + } + + return () => ( +
+ {slots.default?.()} +
+ ) + }, +}) diff --git a/code/base-project/base-vue/packages/ao-design/src/tag/demo.vue b/code/base-project/base-vue/packages/ao-design/src/tag/demo.vue new file mode 100644 index 00000000..5ade7899 --- /dev/null +++ b/code/base-project/base-vue/packages/ao-design/src/tag/demo.vue @@ -0,0 +1,121 @@ + + + + + diff --git a/code/base-project/base-vue/packages/ao-design/src/tag/index.scss b/code/base-project/base-vue/packages/ao-design/src/tag/index.scss new file mode 100644 index 00000000..acbae24f --- /dev/null +++ b/code/base-project/base-vue/packages/ao-design/src/tag/index.scss @@ -0,0 +1,157 @@ +@use '../_styles/_variables.scss' as *; +@use 'sass:color'; + +$tag-prefix: '#{$prefix}-tag'; + +.#{$tag-prefix} { + box-sizing: border-box; + margin: 0; + padding: 0; + list-style: none; + display: inline-flex; + align-items: center; + flex-direction: row; + border: 1px solid transparent; + white-space: nowrap; + border-radius: $border-color; + font-family: 'PingFang SC', 'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif; + + &-size-large { + padding: 0 8px; + height: 24px; + font-size: 13px; + } + &-size-medium { + padding: 0 6px; + height: 22px; + font-size: 12px; + } + &-size-small { + padding: 0 4px; + height: 20px; + font-size: 12px; + } + &-shape-round { + border-radius: 12px; + } + &-shape-mark { + border-radius: 0px 12px 12px 0; + } + &-icon { + margin-right: 4px; + } + &-close { + margin-left: 4px; + cursor: pointer; + transition: filter 0.2s cubic-bezier(0.38, 0, 0.24, 1); + + color: currentColor; + filter: brightness(1.2); /* 比父色亮 20% */ + + &:hover { + filter: brightness(0.8); /* 比父色暗 20% */ + } + } + + &-type-solid { + &.#{$tag-prefix}-status-default { + color: $text-color-base; + background-color: $default-color; + } + &.#{$tag-prefix}-status-primary { + color: $text-color-anti; + background-color: $primary-color; + } + &.#{$tag-prefix}-status-success { + color: $text-color-anti; + background-color: $success-color; + } + &.#{$tag-prefix}-status-warning { + color: $text-color-anti; + background-color: $warning-color; + } + &.#{$tag-prefix}-status-danger { + color: $text-color-anti; + background-color: $danger-color; + } + } + + &-type-light { + &.#{$tag-prefix}-status-default { + color: $text-color-base; + background-color: $default-color-light; + } + &.#{$tag-prefix}-status-primary { + color: $primary-color; + background-color: $primary-color-light; + } + &.#{$tag-prefix}-status-success { + color: $success-color; + background-color: $success-color-light; + } + &.#{$tag-prefix}-status-warning { + color: $warning-color; + background-color: $warning-color-light; + } + &.#{$tag-prefix}-status-danger { + color: $danger-color; + background-color: $danger-color-light; + } + } + + &-type-outline { + &.#{$tag-prefix}-status-default { + color: $text-color-base; + background-color: transparent; + border-color: $default-color; + } + &.#{$tag-prefix}-status-primary { + color: $primary-color; + background-color: transparent; + border-color: $primary-color; + } + &.#{$tag-prefix}-status-success { + color: $success-color; + background-color: transparent; + border-color: $success-color; + } + &.#{$tag-prefix}-status-warning { + color: $warning-color; + background-color: transparent; + border-color: $warning-color; + } + &.#{$tag-prefix}-status-danger { + color: $danger-color; + background-color: transparent; + border-color: $danger-color; + } + } + + &-type-outline-light { + &.#{$tag-prefix}-status-default { + color: $text-color-base; + background-color: $default-color-light; + border-color: $default-color; + } + &.#{$tag-prefix}-status-primary { + color: $primary-color; + background-color: $primary-color-light; + border-color: $primary-color; + } + &.#{$tag-prefix}-status-success { + color: $success-color; + background-color: $success-color-light; + border-color: $success-color; + } + &.#{$tag-prefix}-status-warning { + color: $warning-color; + background-color: $warning-color-light; + border-color: $warning-color; + } + &.#{$tag-prefix}-status-danger { + color: $danger-color; + background-color: $danger-color-light; + border-color: $danger-color; + } + } +} diff --git a/code/base-project/base-vue/packages/ao-design/src/tag/index.tsx b/code/base-project/base-vue/packages/ao-design/src/tag/index.tsx new file mode 100644 index 00000000..46cb333b --- /dev/null +++ b/code/base-project/base-vue/packages/ao-design/src/tag/index.tsx @@ -0,0 +1,83 @@ +import type { PropType } from 'vue' +import { defineComponent, ref } from 'vue' +import { getPrefixCls } from '../_utils/getPrefixCls' +import Icon from '../icon/index' + +export default defineComponent({ + name: 'Tag', + props: { + shape: { + type: String as PropType<'square' | 'round' | 'mark'>, + default: 'square', + }, + size: { + type: String as PropType<'small' | 'medium' | 'large'>, + default: 'medium', + }, + type: { + type: String as () => 'solid' | 'light' | 'outline' | 'outline-light', + default: 'solid', + }, + status: { + type: String as () => 'primary' | 'danger' | 'warning' | 'success' | 'default', + default: 'default', + }, + showClose: { + type: Boolean, + default: false, + }, + icon: { + type: String, + default: '', + }, + disabled: { + type: Boolean, + default: false, + }, + class: { + type: [String, Array, Object], + default: '', + }, + style: { + type: Object, + default: () => ({}), + }, + }, + emits: ['click', 'close'], + setup(props, { slots, emit }) { + const prefixCls = getPrefixCls('tag') + const visible = ref(true) + + const handleClick = (event: MouseEvent) => { + emit('click', event) + } + + const handleClose = (event: MouseEvent) => { + event.stopPropagation() + visible.value = false // 点击关闭隐藏 Tag + emit('close', event) + } + + return () => + visible.value && ( +
+ {props.icon && } + {slots.default?.()} + {!props.disabled && props.showClose && ( + + )} +
+ ) + }, +}) diff --git a/code/base-project/base-vue/packages/ao-design/src/tooltip/demo.vue b/code/base-project/base-vue/packages/ao-design/src/tooltip/demo.vue new file mode 100644 index 00000000..9e6dfb4d --- /dev/null +++ b/code/base-project/base-vue/packages/ao-design/src/tooltip/demo.vue @@ -0,0 +1,55 @@ + + + + + diff --git a/code/base-project/base-vue/packages/ao-design/src/tooltip/index.scss b/code/base-project/base-vue/packages/ao-design/src/tooltip/index.scss new file mode 100644 index 00000000..dd173177 --- /dev/null +++ b/code/base-project/base-vue/packages/ao-design/src/tooltip/index.scss @@ -0,0 +1,88 @@ +@use '../_styles/_variables.scss' as *; +@use 'sass:color'; +$popup-prefix: '#{$prefix}-popup'; +$tooltip-prefix: '#{$prefix}-tooltip'; + +.#{$tooltip-prefix} { + margin: 0; + padding: 0; + color: $text-color-base; + + &-wrapper { + background-color: $status-base-bg-color; + padding: 4px 8px; + word-break: break-all; + } + + .#{$popup-prefix}-arrow::before { + background-color: $status-base-bg-color; + } + + &-default { + .#{$tooltip-prefix}-wrapper { + background-color: $status-base-bg-color; + color: $text-color-base; + } + .#{$popup-prefix}-arrow::before { + background-color: $status-base-bg-color; + } + } + + &-anti { + .#{$tooltip-prefix}-wrapper { + background-color: $status-anti-bg-color; + color: $text-color-anti; + } + .#{$popup-prefix}-arrow::before { + background-color: $status-anti-bg-color; + } + } + &-info { + .#{$tooltip-prefix}-wrapper { + background-color: $status-info-bg-color; + color: $text-color-base; + } + .#{$popup-prefix}-arrow::before { + background-color: $status-info-bg-color; + } + } + &-success { + .#{$tooltip-prefix}-wrapper { + background-color: $status-success-bg-color; + color: $text-color-base; + } + .#{$popup-prefix}-arrow::before { + background-color: $status-success-bg-color; + } + } + &-danger { + .#{$tooltip-prefix}-wrapper { + background-color: $status-danger-bg-color; + color: $text-color-base; + } + .#{$popup-prefix}-arrow::before { + background-color: $status-danger-bg-color; + } + } + &-warning { + .#{$tooltip-prefix}-wrapper { + background-color: $status-warning-bg-color; + color: $text-color-base; + } + .#{$popup-prefix}-arrow::before { + background-color: $status-warning-bg-color; + } + } + + &-anti.#{$tooltip-prefix}, + &-info.#{$tooltip-prefix}, + &-success.#{$tooltip-prefix}, + &-danger.#{$tooltip-prefix}, + &-warning.#{$tooltip-prefix} { + box-shadow: none; + + .#{$popup-prefix}-arrow::before { + box-shadow: none; + } + } +} diff --git a/code/base-project/base-vue/packages/ao-design/src/tooltip/index.tsx b/code/base-project/base-vue/packages/ao-design/src/tooltip/index.tsx new file mode 100644 index 00000000..bcc800cb --- /dev/null +++ b/code/base-project/base-vue/packages/ao-design/src/tooltip/index.tsx @@ -0,0 +1,108 @@ +import type { PropType, VNode } from 'vue' +import { defineComponent } from 'vue' +import { getPrefixCls } from '../_utils/getPrefixCls' +import Popup from '../popup/index' + +export default defineComponent({ + name: 'Tooltip', + props: { + type: { + type: String as PropType<'default' | 'anti' | 'info' | 'success' | 'danger' | 'warning'>, + default: 'default', + }, + placement: { + type: String as PropType< + 'top' | 'bottom' | 'left' | 'right' + | 'top-start' | 'top-end' | 'bottom-start' | 'bottom-end' + | 'left-start' | 'left-end' | 'right-start' | 'right-end' + >, + default: 'top', + }, + trigger: { + type: [String, Array] as PropType< + 'hover' | 'click' | 'focus' | 'contextMenu' | Array<'hover' | 'click' | 'focus' | 'contextMenu'> + >, + default: 'hover', + }, + showArrow: { + type: Boolean, + default: true, + }, + autoFitPopupMinWidth: { + type: Boolean, + default: false, + }, + autoFitPopupWidth: { + type: Boolean, + default: false, + }, + destroyOnClose: { + type: Boolean, + default: false, + }, + content: { + type: [String, Object, Function] as unknown as () => string | VNode | (() => VNode), + default: '', + }, + showDelay: { type: Number, default: 0 }, + hideDelay: { type: Number, default: 0 }, + class: { + type: [String, Array, Object], + default: '', + }, + style: { + type: Object, + default: () => ({}), + }, + maxWidth: { + type: [Number, String] as PropType, + default: '200px', + }, + }, + emits: [], + setup(props, { slots }) { + const prefixCls = getPrefixCls('tooltip') + + const renderContent = (content: string | VNode | (() => VNode)) => { + if (typeof content === 'function') + return content() + if (typeof content === 'string') { + return
{content}
+ } + return content + } + + const renderTooltip = () => { + return ( +
+ {slots.content ? slots.content() : renderContent(props.content)} +
+ ) + } + + return () => ( + + {{ + default: () => slots.default?.(), + }} + + ) + }, +}) diff --git a/code/base-project/base-vue/src/App.vue b/code/base-project/base-vue/src/App.vue index 81efaba0..a1fbdce2 100644 --- a/code/base-project/base-vue/src/App.vue +++ b/code/base-project/base-vue/src/App.vue @@ -1,40 +1,15 @@ @@ -46,4 +21,9 @@ function showCustomMessage() { gap: 16px; margin-top: 40px; } + +.row { + display: flex; + gap: 12px; +} diff --git a/code/base-project/base-vue/src/components.d.ts b/code/base-project/base-vue/src/components.d.ts index 7f5af0d3..7b3a7e8b 100644 --- a/code/base-project/base-vue/src/components.d.ts +++ b/code/base-project/base-vue/src/components.d.ts @@ -19,6 +19,9 @@ declare module 'vue' { APopconfirm: typeof import('@ao/ao-design')['Popconfirm'] APopup: typeof import('@ao/ao-design')['Popup'] ASpin: typeof import('@ao/ao-design')['Spin'] + ATag: typeof import('@ao/ao-design')['Tag'] + ATagGroup: typeof import('@ao/ao-design')['TagGroup'] ATextarea: typeof import('@ao/ao-design')['Textarea'] + ATooltip: typeof import('@ao/ao-design')['Tooltip'] } } diff --git a/code/base-project/base-vue/test.html b/code/base-project/base-vue/test.html index a8469885..5d97917b 100644 --- a/code/base-project/base-vue/test.html +++ b/code/base-project/base-vue/test.html @@ -187,19 +187,7 @@ - - - - - - - - - - - - - +
Shipper: CHANNEL INTERNATIONAL FREIGHT CO.,LTD 24 FLOOR, YINYI GLOBAL CENTER,B BLOCK,NO.42 YAOHANG STREET HAISHUDISTRICT,NINGBO,CHINA TEL:0086-574-87337359 FAX:0086-574-87135310TO:宁波坤洲国际物流有限公司 ATTN: TEL: FROM:宁波驰诺/裘珊珊 TEL:0574-87237893 QQ:1597608137 业务编号:CNNBYS230201 开航日期:2023-02-0400:00:00.0 船公司:现代商船 柜量:2X2OTKFCL运费条款:FREIGHTPREPAID 运输条款:CY-CY
Consignee: NSINTERNATIONAL(THAILAND)CO.LTD 75/49 RICHMOND BLDG.,15 FL SUKHUMVIT SOI 26 KLONGTOEY,BANGKOK1011O THAILAND ATTN:IMPORT@NSINTER.COM TEL: +66-2-665-6710 FAX:+66-2-665-6494
Notify Party: NS INTERNATIONAL (THAILAND) CO.,LTD 75/49 RICHMOND BLDG., 15 FL SUKHUMVIT SOI 26 KLONGTOEY,BANGKOK1011O THAILAND ATTN:IMPORT@NSINTER.COM TEL:+66-2-665-6710 FAX: +66-2-665-6494
起运港(PortOf Loading):中转港(Port of Dischargc):目的港(Final Destination):
NINGBO,CHINALEAM CHA BANG
标志和号码件数和包装种类货名毛重(公斤)体积(立方米)
SMAU9992165 SMAU99921702TANKSBULK LIQUID ARGON CLASS:2.2 UN:195144000.000KGS50.000CBM