UploaderListItem.vue 9.0 KB

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