Icon.vue 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. <template>
  2. <text
  3. v-if="!iconData"
  4. :style="{ color: '#f00', fontSize: '16px' }"
  5. :class="innerClass"
  6. >
  7. {{ icon ? `Missing icon: ${icon}` : 'Empty' }} !
  8. </text>
  9. <text
  10. v-else-if="iconData.type == 'iconfont'"
  11. :style="style"
  12. :class="['iconfont', innerClass, iconData.value]"
  13. />
  14. <!-- #ifdef H5 || APP-PLUS -->
  15. <view
  16. v-else-if="iconData.type == 'svg'"
  17. :style="{
  18. ...style,
  19. display: 'flex'
  20. }"
  21. :class="innerClass"
  22. v-html="iconData.rawSvg"
  23. />
  24. <!-- #endif -->
  25. <!-- #ifndef H5 -->
  26. <image
  27. v-else-if="iconData.type == 'svg' && style.color && iconData.rawSvg"
  28. :style="style"
  29. :class="innerClass"
  30. :src="IconUtils.getColoredSvg(iconData.rawSvg, style.color)"
  31. />
  32. <image
  33. v-else-if="iconData.type == 'svg'"
  34. :style="style"
  35. :class="innerClass"
  36. :src="iconData.value"
  37. />
  38. <!-- #endif -->
  39. <WrappedImage
  40. v-else-if="iconData.type == 'image' && iconData.value"
  41. :innerStyle="style"
  42. :innerClass="innerClass"
  43. :src="iconData.value"
  44. :showFailed="!noError"
  45. mode="aspectFill"
  46. />
  47. </template>
  48. <script setup lang="ts">
  49. import { computed } from 'vue';
  50. import { IconUtils, type IconItem } from './IconUtils';
  51. import { useTheme } from '../theme/ThemeDefine';
  52. import WrappedImage from './Image.vue';
  53. export interface IconProps {
  54. /**
  55. * 图标名称或图片 URL
  56. */
  57. icon?: string;
  58. /**
  59. * 同 icon 属性
  60. */
  61. name?: string;
  62. /**
  63. * 图标大小
  64. */
  65. size?: number|string;
  66. /**
  67. * 图标颜色
  68. */
  69. color?: string;
  70. /**
  71. * 图标旋转角度
  72. */
  73. rotate?: number;
  74. /**
  75. * 自定义样式
  76. */
  77. innerStyle?: object,
  78. /**
  79. * 自定义类名
  80. */
  81. innerClass?: string,
  82. /**
  83. * 是否不显示错误图标
  84. */
  85. noError?: boolean,
  86. }
  87. const theme = useTheme();
  88. const props = withDefaults(defineProps<IconProps>(), {
  89. size: 45,
  90. noError: false,
  91. });
  92. const icon = computed(() => props.icon || props.name);
  93. const iconData = computed(() => {
  94. if (IconUtils.iconCount.value === 0) {
  95. return {
  96. type: 'none',
  97. value: '',
  98. } as IconItem
  99. }
  100. const data = icon.value ? IconUtils.getIconDataFromMap(icon.value) : undefined;
  101. if (!data && icon.value && icon.value.startsWith('icon-')) {
  102. return {
  103. type: 'iconfont',
  104. value: icon.value,
  105. fontFamily: 'iconfont',
  106. } as IconItem
  107. } else if (!data && icon.value) {
  108. return {
  109. type: 'image',
  110. value: icon.value,
  111. } as IconItem
  112. } else if (!data) {
  113. return {
  114. type: 'none',
  115. value: '',
  116. } as IconItem
  117. }
  118. return data;
  119. });
  120. const style = computed(() => {
  121. const size = theme.resolveThemeSize(props.size);
  122. return {
  123. flexShrink: 0,
  124. fontSize: size,
  125. fontFamily: iconData.value.fontFamily || 'iconfont',
  126. width: size,
  127. height: size,
  128. color: theme.resolveThemeColor(props.color, 'text.content'),
  129. fill: theme.resolveThemeColor(props.color, 'text.content'),
  130. transform: props.rotate ? `rotate(${props.rotate}deg)` : undefined,
  131. overflow: 'visible',
  132. ...props.innerStyle,
  133. };
  134. });
  135. defineOptions({
  136. options: {
  137. virtualHost: true,
  138. }
  139. });
  140. </script>