UploaderListItem.vue 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. <template>
  2. <Touchable
  3. touchable
  4. overflow="hidden"
  5. :width="isListStyle ? undefined : itemSize.width"
  6. :height="isListStyle ? undefined : itemSize.height"
  7. :innerStyle="{
  8. ...(isListStyle ? themeStyles.itemListStyle.value : themeStyles.item.value),
  9. ...(isListStyle ? selectStyleType(item.state, 'notstart', {
  10. notstart: {},
  11. uploading: themeStyles.itemListStyleUploading.value,
  12. success: themeStyles.itemListStyleSuccess.value,
  13. fail: themeStyles.itemListStyleFail.value,
  14. }) : {}),
  15. ...props.style as ViewStyle,
  16. }"
  17. :direction="isListStyle ? 'row' : 'column'"
  18. center
  19. @click="emit('click')"
  20. >
  21. <template v-if="!isListStyle">
  22. <IconButton
  23. v-if="showDelete"
  24. icon="delete-filling"
  25. color="danger"
  26. :buttonStyle="themeStyles.itemDeleteButton.value"
  27. @click.stop="emit('delete')" />
  28. <FlexView v-if="item.state === 'uploading'" :innerStyle="{
  29. ...itemMaskStyle,
  30. backgroundColor: themeContext.resolveThemeColor('UploaderListItemUploadingBackgroundColor', 'mask.primary'),
  31. }">
  32. <ActivityIndicator :color="itemMaskTextColor" :size="loadingSize" />
  33. <Text :color="itemMaskTextColor" :style="itemMaskTextStyle" textAlign="center" :text="item.message" />
  34. </FlexView>
  35. <FlexView v-if="item.state === 'fail'" :innerStyle="{
  36. ...itemMaskStyle,
  37. backgroundColor: themeContext.resolveThemeColor('UploaderListItemUploadingBackgroundColor', 'mask.danger'),
  38. }">
  39. <Icon icon="error" :color="itemMaskTextColor" :size="iconSize" />
  40. <Text :color="itemMaskTextColor" :style="itemMaskTextStyle" textAlign="center" :text="item.message" />
  41. </FlexView>
  42. </template>
  43. <Image
  44. v-if="isImage"
  45. :src="item.previewPath || item.filePath"
  46. :defaultImage="defaultSource ?? fileIcon"
  47. :width="isListStyle ? itemListStyleImageSize : itemSize.width"
  48. :height="isListStyle ? itemListStyleImageSize : itemSize.height"
  49. :innerStyle="{
  50. ...(isListStyle ? themeStyles.itemListStyleImage.value : themeStyles.itemImage.value),
  51. ...imageStyle,
  52. }"
  53. mode="aspectFill"
  54. />
  55. <Icon v-else :icon="fileIcon" :color="itemMaskTextColor" :size="itemListStyleImageSize" />
  56. <FlexRow v-if="isListStyle" :flex="1" align="center">
  57. <Width :size="20" />
  58. <FlexCol :flex="1">
  59. <Text :fontSize="26" :text="StringUtils.path.getFileName(item.filePath)" />
  60. <Text :fontSize="22" :text="item.message" />
  61. <Height :size="10" />
  62. <Progress :progressColor="selectStyleType(item.state, 'notstart', {
  63. notstart: 'primary',
  64. uploading: 'primary',
  65. success: 'success',
  66. fail: 'danger',
  67. })" :value="item.progress || 0" />
  68. </FlexCol>
  69. <Icon v-if="item.state === 'success'" icon="select-bold" color="success" />
  70. <Icon v-else-if="item.state === 'fail'" icon="close-bold" color="danger" />
  71. <ActivityIndicator v-else-if="item.state === 'uploading'" :size="45" />
  72. <Width :size="20" />
  73. <IconButton v-if="showDelete" icon="trash" @click.stop="emit('delete')" />
  74. </FlexRow>
  75. </Touchable>
  76. </template>
  77. <script setup lang="ts">
  78. import { computed, watch } from 'vue';
  79. import { useTheme, type TextStyle, type ViewStyle } from '../theme/ThemeDefine';
  80. import { DynamicColor, DynamicSize, DynamicSize2, selectStyleType } from '../theme/ThemeTools';
  81. import { StringUtils } from '@imengyu/imengyu-utils';
  82. import ActivityIndicator from '../basic/ActivityIndicator.vue';
  83. import Icon from '../basic/Icon.vue';
  84. import IconButton from '../basic/IconButton.vue';
  85. import Image from '../basic/Image.vue';
  86. import Text from '../basic/Text.vue';
  87. import FlexCol from '../layout/FlexCol.vue';
  88. import FlexView from '../layout/FlexView.vue';
  89. import FlexRow from '../layout/FlexRow.vue';
  90. import Progress from '../display/Progress.vue';
  91. import IconApk from '../images/files/apk.png';
  92. import IconAudio from '../images/files/audio.png';
  93. import IconDefault from '../images/files/default.png';
  94. import IconExcel from '../images/files/excel.png';
  95. import IconPowerpoint from '../images/files/powerpoint.png';
  96. import IconUnknown from '../images/files/unknown.png';
  97. import IconVideo from '../images/files/video.png';
  98. import IconWord from '../images/files/word.png';
  99. import IconZip from '../images/files/zip.png';
  100. import IconPdf from '../images/files/pdf.png';
  101. import Width from '../layout/space/Width.vue';
  102. import Height from '../layout/space/Height.vue';
  103. import Touchable from '../feedback/Touchable.vue';
  104. import type { UploaderItem } from './Uploader';
  105. export interface UploaderListItemProps {
  106. item: UploaderItem;
  107. style?: ViewStyle;
  108. imageStyle?: ViewStyle;
  109. itemMaskTextStyle?: TextStyle;
  110. itemMaskStyle?: ViewStyle;
  111. itemSize: { width: number, height: number };
  112. showDelete: boolean;
  113. defaultSource: string | undefined;
  114. isListStyle: boolean;
  115. }
  116. const props = defineProps<UploaderListItemProps>();
  117. const emit = defineEmits(['click', 'delete'])
  118. const themeContext = useTheme();
  119. const itemListStyleImageSize = computed(() => themeContext.resolveThemeSize('UploaderListItemListImageSize', 64));
  120. const themeStyles = themeContext.useThemeStyles({
  121. item: {
  122. position: 'relative',
  123. borderRadius: DynamicSize('UploaderListItemBorderRadius', 20),
  124. backgroundColor: DynamicColor('UploaderListItemBackgroundColor', 'grey'),
  125. marginHorizontal: DynamicSize('UploaderListItemGridMargin', 4),
  126. marginBottom: DynamicSize('UploaderListItemGridMargin', 4),
  127. },
  128. itemListStyle: {
  129. position: 'relative',
  130. borderStyle: 'solid',
  131. borderWidth: DynamicSize('UploaderListItemListBorderWidth', 2),
  132. borderRadius: DynamicSize('UploaderListItemBorderRadius', 15),
  133. backgroundColor: DynamicColor('UploaderListItemBackgroundColor', 'white'),
  134. padding: DynamicSize2('UploaderListItemListPaddingVertical', 'UploaderListItemListPaddingHorizontal', 20, 20),
  135. marginBottom: DynamicSize('UploaderListItemListMargin', 10),
  136. },
  137. itemListStyleUploading: {
  138. borderColor: DynamicColor('UploaderListItemListUploadingBorderColor', 'primary'),
  139. backgroundColor: DynamicColor('UploaderListItemListUploadingBackgroundColor', 'background.primary'),
  140. },
  141. itemListStyleSuccess: {
  142. borderColor: DynamicColor('UploaderListItemListSuccessBorderColor', 'success'),
  143. backgroundColor: DynamicColor('UploaderListItemListSuccessBackgroundColor', 'background.success'),
  144. },
  145. itemListStyleFail: {
  146. borderColor: DynamicColor('UploaderListItemListFailBorderColor', 'danger'),
  147. backgroundColor: DynamicColor('UploaderListItemListFailBackgroundColor', 'background.danger'),
  148. },
  149. itemListStyleImage: {
  150. borderRadius: DynamicSize('UploaderListItemListBorderRadius', 20),
  151. },
  152. itemImage: {
  153. position: 'absolute',
  154. top: 0,
  155. left: 0,
  156. borderRadius: DynamicSize('UploaderListItemGridBorderRadius', 15),
  157. zIndex: 5,
  158. },
  159. itemList: {
  160. },
  161. itemMask: {
  162. backgroundColor: 'rgba(0,0,0,0.3)',
  163. position: 'absolute',
  164. top: 0,
  165. left: 0,
  166. right: 0,
  167. bottom: 0,
  168. justifyContent: 'center',
  169. alignItems: 'center',
  170. borderRadius: DynamicSize('UploaderListItemGridBorderRadius', 20),
  171. zIndex: 10,
  172. },
  173. itemMaskText: {
  174. marginTop: 5,
  175. color: '#fff',
  176. },
  177. itemDeleteButton: {
  178. position: 'absolute',
  179. right: 0,
  180. top: 0,
  181. zIndex: 30,
  182. borderRadius: DynamicSize('UploaderListItemGridBorderRadius', 20),
  183. backgroundColor: '#fff',
  184. },
  185. });
  186. const itemMaskTextStyle = computed(() => ({ ...themeStyles.itemMaskText.value, ...props.itemMaskTextStyle }));
  187. const itemMaskStyle = computed(() => ({ ...themeStyles.itemMask.value, ...props.itemMaskStyle }));
  188. const itemMaskTextColor = computed(() => props.itemMaskTextStyle?.color || themeStyles.itemMaskText.value.color as string);
  189. const iconSize = computed(() => themeContext.resolveThemeSize('UploaderListItemIconSize', 65));
  190. const loadingSize = computed(() => themeContext.resolveThemeSize('UploaderListItemLoadingSize', 35));
  191. const imageExts = [ 'jpg', 'jpeg', 'png', 'webp', 'gif' ]
  192. const isImage = computed(() => {
  193. return props.item.isImage
  194. || (props.item.previewPath && imageExts.includes(StringUtils.path.getFileExt(props.item.previewPath)))
  195. || (props.item.filePath && imageExts.includes(StringUtils.path.getFileExt(props.item.filePath || ''))
  196. )
  197. });
  198. const fileIcon = computed(() => {
  199. const ext = StringUtils.path.getFileExt(props.item.filePath || '');
  200. switch (ext) {
  201. case 'apk1':
  202. case 'apk': return IconApk;
  203. case 'audio':
  204. case 'mp3':
  205. case 'wav':
  206. case 'wma':
  207. case 'ogg':
  208. case 'flac':
  209. return IconAudio;
  210. case 'default':
  211. return IconDefault;
  212. case 'excel':
  213. case 'xls':
  214. case 'xlsx':
  215. return IconExcel;
  216. case 'powerpoint':
  217. case 'ppt':
  218. case 'pptx':
  219. return IconPowerpoint;
  220. case 'unknown':
  221. case 'video':
  222. case 'mp4':
  223. case 'mov':
  224. case 'wmv':
  225. case 'avi':
  226. case 'flv':
  227. return IconVideo;
  228. case 'word':
  229. case 'docx':
  230. case 'doc':
  231. case 'txt':
  232. case 'rtf':
  233. return IconWord;
  234. case 'zip':
  235. case '7z':
  236. case 'rar':
  237. return IconZip;
  238. case 'pdf':
  239. return IconPdf;
  240. }
  241. return IconUnknown;
  242. });
  243. </script>