Alert.vue 6.2 KB

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