Alert.vue 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. <template>
  2. <FlexRow
  3. :innerClass="['nana-alert', `nana-alert-${type}`]"
  4. :innerStyle="{
  5. ...themeStyles.container.value,
  6. backgroundColor: themeContext.resolveThemeColor(backgroundColor|| selectStyleType(
  7. type, 'default', {
  8. default: '',
  9. primary: 'background.primary',
  10. success: 'background.success',
  11. warning: 'background.warning',
  12. danger: 'background.danger',
  13. error: 'background.danger',
  14. info: 'background.info',
  15. }
  16. )),
  17. borderStyle: 'solid',
  18. borderColor: themeContext.resolveThemeColor(borderColor || selectStyleType(
  19. type, 'default', {
  20. default: 'border.default',
  21. primary: 'mask.primary',
  22. success: 'mask.success',
  23. warning: 'mask.warning',
  24. danger: 'mask.danger',
  25. error: 'mask.danger',
  26. info: 'mask.info',
  27. }
  28. )),
  29. borderRadius: themeContext.resolveThemeSize('AlertBorderRadius', 16),
  30. }"
  31. @click="emit('click')"
  32. >
  33. <!-- 图标 -->
  34. <slot name="icon">
  35. <Icon
  36. :icon="icon ?? iconTypeMap[type]"
  37. :color="color"
  38. :innerStyle="{
  39. ...themeStyles.icon.value,
  40. marginRight: hasTitle || hasContent ? themeContext.resolveThemeSize('AlertIconMarginRight', 12) : 0,
  41. }"
  42. />
  43. </slot>
  44. <!-- 内容区域 -->
  45. <FlexCol
  46. :innerClass="['nana-alert-content']"
  47. :flexGrow="1"
  48. :flexShrink="1"
  49. >
  50. <!-- 标题 -->
  51. <slot name="message">
  52. <Text
  53. v-if="message"
  54. :text="message"
  55. :fontSize="themeContext.resolveThemeSize('AlertTitleFontSize', 'fontSize.medium')"
  56. :fontWeight="themeContext.getVar('AlertTitleFontWeight', 'bold')"
  57. :color="color"
  58. :innerStyle="themeStyles.message.value"
  59. />
  60. </slot>
  61. <!-- 内容 -->
  62. <slot name="description">
  63. <Text
  64. v-if="description"
  65. :text="description"
  66. :fontSize="themeContext.resolveThemeSize('AlertMessageFontSize', 'fontSize.small')"
  67. :color="color"
  68. :lines="lines"
  69. :innerStyle="{
  70. ...themeStyles.description.value,
  71. marginTop: hasTitle ? themeContext.resolveThemeSize('AlertMessageMarginTop', 8) : 0,
  72. }"
  73. />
  74. </slot>
  75. <!-- 自定义内容 -->
  76. <slot />
  77. </FlexCol>
  78. <!-- 关闭按钮 -->
  79. <slot name="close">
  80. <IconButton
  81. v-if="closeable"
  82. :icon="closeIcon"
  83. :color="closeIconColor"
  84. :size="themeContext.resolveThemeSize('AlertCloseButtonSize', 42)"
  85. :innerStyle="{
  86. ...themeStyles.closeButton.value,
  87. marginLeft: themeContext.resolveThemeSize('AlertCloseButtonMarginLeft', 12),
  88. }"
  89. @click.stop="handleClose"
  90. />
  91. </slot>
  92. </FlexRow>
  93. </template>
  94. <script setup lang="ts">
  95. import { computed } from 'vue';
  96. import { useTheme, type TextStyle, type ViewStyle } from '../theme/ThemeDefine';
  97. import { DynamicSize, DynamicColor, selectStyleType, DynamicVar } from '../theme/ThemeTools';
  98. import Text from '../basic/Text.vue';
  99. import Icon from '../basic/Icon.vue';
  100. import IconButton from '../basic/IconButton.vue';
  101. import FlexRow from '../layout/FlexRow.vue';
  102. import FlexCol from '../layout/FlexCol.vue';
  103. export type AlertType = 'primary' | 'success' | 'warning' | 'danger' | 'error' | 'info' | 'default';
  104. export interface AlertProps {
  105. /**
  106. * 提示类型,可选值为 primary, success, warning, danger, error, info, default
  107. * @default 'default'
  108. */
  109. type?: AlertType;
  110. /**
  111. * 提示标题
  112. */
  113. message?: string;
  114. /**
  115. * 提示内容
  116. */
  117. description?: string;
  118. /**
  119. * 自定义图标
  120. */
  121. icon?: string;
  122. /**
  123. * 文字和图标的颜色
  124. */
  125. color?: string;
  126. /**
  127. * 背景颜色
  128. */
  129. backgroundColor?: string;
  130. /**
  131. * 边框颜色
  132. */
  133. borderColor?: string;
  134. /**
  135. * 是否显示关闭按钮
  136. * @default false
  137. */
  138. closeable?: boolean;
  139. /**
  140. * 关闭图标
  141. * @default 'close'
  142. */
  143. closeIcon?: string;
  144. /**
  145. * 关闭图标颜色
  146. */
  147. closeIconColor?: string;
  148. /**
  149. * 内容最多显示的行数,0表示不限制
  150. * @default 0
  151. */
  152. lines?: number;
  153. }
  154. const emit = defineEmits(['close', 'click']);
  155. const props = withDefaults(defineProps<AlertProps>(), {
  156. type: 'default',
  157. closeable: false,
  158. closeIcon: 'close',
  159. lines: 0,
  160. });
  161. const themeContext = useTheme();
  162. const iconTypeMap = {
  163. primary: 'prompt',
  164. success: 'success',
  165. warning: 'warning',
  166. danger: 'error',
  167. error: 'delete-filling',
  168. info: 'prompt',
  169. default: '',
  170. } as const;
  171. // 判断是否有标题和内容
  172. const hasTitle = computed(() => !!props.message);
  173. const hasContent = computed(() => !!props.description);
  174. const color = computed(() => {
  175. if (props.color)
  176. return themeContext.resolveThemeColor(props.color);
  177. return selectStyleType(props.type, 'default', {
  178. default: 'text.content',
  179. primary: 'text.primary',
  180. success: 'text.success',
  181. warning: 'text.warning',
  182. danger: 'text.danger',
  183. error: 'text.danger',
  184. info: 'text.info',
  185. });
  186. });
  187. const closeIconColor = computed(() => {
  188. if (props.closeIconColor)
  189. return themeContext.resolveThemeColor(props.closeIconColor);
  190. return themeContext.resolveThemeColor('text.second');
  191. });
  192. // 处理关闭事件
  193. const handleClose = () => {
  194. emit('close');
  195. };
  196. // 定义主题样式
  197. const themeStyles = themeContext.useThemeStyles({
  198. container: {
  199. position: 'relative',
  200. display: 'flex',
  201. flexDirection: 'row',
  202. alignItems: 'center',
  203. padding: DynamicSize('AlertPadding', 16),
  204. borderWidth: DynamicSize('AlertBorderWidth', '1px'),
  205. borderStyle: DynamicVar('AlertBorderStyle', 'solid'),
  206. } as ViewStyle,
  207. icon: {
  208. fontSize: DynamicSize('AlertIconSize', 20),
  209. flexShrink: 0,
  210. marginTop: DynamicSize('AlertIconMarginTop', 2),
  211. } as ViewStyle,
  212. message: {
  213. margin: 0,
  214. padding: 0,
  215. flexShrink: 0,
  216. } as TextStyle,
  217. description: {
  218. margin: 0,
  219. padding: 0,
  220. flexShrink: 1,
  221. } as TextStyle,
  222. closeButton: {} as ViewStyle,
  223. });
  224. </script>
  225. <style lang="scss">
  226. .nana-alert {
  227. transition: all 0.3s ease;
  228. box-sizing: border-box;
  229. &-content {
  230. flex: 1;
  231. min-width: 0;
  232. }
  233. }
  234. </style>