index.tsx 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. import {
  2. Image,
  3. Icon,
  4. showLoadingToast,
  5. showSuccessToast,
  6. showFailToast,
  7. Popup,
  8. Swipe,
  9. SwipeItem
  10. } from 'vant';
  11. import {
  12. PropType,
  13. defineComponent,
  14. nextTick,
  15. onMounted,
  16. reactive,
  17. ref,
  18. watch
  19. } from 'vue';
  20. import styles from './index.module.less';
  21. import iconPreviewClose from '@/common/images/icon-preview-close.png';
  22. import iconPreviewDownload from '@/common/images/icon-preview-download.png';
  23. import { promisefiyPostMessage } from '@/helpers/native-message';
  24. import { checkFile } from '@/helpers/toolsValidate';
  25. import MVideo from '../m-video';
  26. import { state } from '@/state';
  27. import { browser } from '@/helpers/utils';
  28. import deepClone from '@/helpers/deep-clone';
  29. export default defineComponent({
  30. name: 'm-image-preview',
  31. props: {
  32. show: {
  33. tyep: Boolean,
  34. default: false
  35. },
  36. images: {
  37. type: Array as PropType<string[]>,
  38. default: () => []
  39. },
  40. showIndex: {
  41. type: Boolean,
  42. default: true
  43. },
  44. startPosition: {
  45. type: Number,
  46. default: 0
  47. },
  48. loop: {
  49. type: Boolean,
  50. default: false
  51. },
  52. showDownload: {
  53. type: Boolean,
  54. default: true
  55. },
  56. teleport: {
  57. type: String,
  58. default: ''
  59. }
  60. },
  61. emits: ['update:show'],
  62. setup(props, { emit }) {
  63. const forms = reactive({
  64. show: false,
  65. showButton: true,
  66. index: props.startPosition + 1,
  67. saveLoading: false,
  68. preLoading: false
  69. });
  70. const onSave = async (img: string) => {
  71. if (forms.saveLoading) return;
  72. forms.saveLoading = true;
  73. showLoadingToast({ message: '下载中...', forbidClick: true });
  74. try {
  75. const res = await promisefiyPostMessage({
  76. api: 'saveFile',
  77. content: {
  78. img,
  79. type: checkFile(img, 'image') ? 'image' : 'video'
  80. }
  81. });
  82. if (res?.content?.status === 'success') {
  83. showSuccessToast('保存成功');
  84. } else {
  85. showFailToast('保存失败');
  86. }
  87. } catch {
  88. //
  89. }
  90. forms.saveLoading = false;
  91. };
  92. const videoRef: any = ref([]);
  93. const onPlay = (index: any) => {
  94. videoRef.value.forEach((item: any, child: any) => {
  95. if (child !== index) {
  96. item?.onStop();
  97. item?.onExitScreen();
  98. }
  99. });
  100. };
  101. onMounted(() => {
  102. forms.show = props.show;
  103. // console.log(window.document.body.clientWidth);
  104. document.documentElement.style.setProperty(
  105. '--window-page-width',
  106. (window.document.body.clientWidth || window.document.body.offsetWidth) +
  107. 'px'
  108. );
  109. onChnageLeftWidth(forms.index - 1);
  110. });
  111. const onChnageLeftWidth = (index: number) => {
  112. document.documentElement.style.setProperty(
  113. '--window-page-position-left',
  114. (window.document.body.clientWidth || window.document.body.offsetWidth) *
  115. index +
  116. 'px'
  117. );
  118. };
  119. watch(
  120. () => props.show,
  121. () => {
  122. forms.show = props.show;
  123. forms.index = props.startPosition + 1;
  124. forms.preLoading = props.show;
  125. onChnageLeftWidth(props.startPosition);
  126. // console.log(forms.preLoading, 'show');
  127. // nextTick(() => {
  128. // // 判断打开的内容是否为视频,是则自动播放
  129. // const defaultUrl = props.images[props.startPosition];
  130. // console.log(defaultUrl, 'defaultUrl');
  131. // if (checkFile(defaultUrl, 'video') && props.show) {
  132. // console.log(1111, videoRef.value);
  133. // // videoRef.value[props.startPosition]?.onPlay();
  134. // videoRef.value.forEach((item: any, child: any) => {
  135. // if (child === props.startPosition) {
  136. // console.log(item, 'item');
  137. // item?.onPlay();
  138. // }
  139. // });
  140. // }
  141. // });
  142. }
  143. );
  144. watch(
  145. () => props.startPosition,
  146. () => {
  147. forms.index = props.startPosition + 1;
  148. onChnageLeftWidth(props.startPosition);
  149. }
  150. );
  151. return () => (
  152. <Popup
  153. teleport={props.teleport}
  154. v-model:show={forms.show}
  155. overlay-class={styles.overlayPreview}
  156. class={['van-image-preview', styles.imagePreview]}>
  157. {forms.show ? (
  158. <>
  159. {forms.showButton && (
  160. <>
  161. <Icon
  162. name={iconPreviewClose}
  163. class="van-image-preview__close-icon van-image-preview__close-icon--top-left van-haptics-feedback"
  164. style={{
  165. top: state.navBarHeight
  166. ? state.navBarHeight + 'px'
  167. : 'var(--van-padding-md)'
  168. }}
  169. onClick={() => {
  170. onPlay(-1);
  171. emit('update:show', false);
  172. }}
  173. />
  174. <div
  175. class={'van-image-preview__index'}
  176. style={{
  177. top: state.navBarHeight
  178. ? state.navBarHeight + 'px'
  179. : 'var(--van-padding-md)'
  180. }}>
  181. {forms.index} / {props.images.length}
  182. </div>
  183. {props.showDownload && browser().isApp ? (
  184. <Icon
  185. name={iconPreviewDownload}
  186. class="van-image-preview__close-icon van-image-preview__close-icon--top-right van-haptics-feedback"
  187. style={{
  188. top: state.navBarHeight
  189. ? state.navBarHeight + 'px'
  190. : 'var(--van-padding-md)'
  191. }}
  192. onClick={() => {
  193. // console.log(
  194. // forms.index,
  195. // 'index',
  196. // props.images[forms.index - 1]
  197. // );
  198. onSave(props.images[forms.index - 1]);
  199. }}
  200. />
  201. ) : (
  202. ''
  203. )}
  204. </>
  205. )}
  206. <Swipe
  207. autoplay={0}
  208. loop={false}
  209. class={'van-image-preview__swipe'}
  210. showIndicators={false}
  211. initialSwipe={props.startPosition}
  212. onChange={(index: number) => {
  213. forms.index = index + 1;
  214. // forms.preLoading = true;
  215. onPlay(index);
  216. onChnageLeftWidth(index);
  217. }}
  218. lazyRender>
  219. {props.images.map((url: string, index: number) => (
  220. <SwipeItem
  221. class={'van-image-preview__swipe-item'}
  222. onClick={() => {
  223. onPlay(-1);
  224. emit('update:show', false);
  225. }}>
  226. {checkFile(url, 'image') ? (
  227. <Image class="van-image-preview__image" src={url} />
  228. ) : (
  229. <div
  230. class="van-image-preview__image"
  231. onClick={(e: MouseEvent) => {
  232. e.stopPropagation();
  233. e.preventDefault();
  234. }}>
  235. <MVideo
  236. ref={(el: any) => {
  237. videoRef.value[index] = el;
  238. // if (forms.index === index + 1 && forms.preLoading) {
  239. // console.log(el, 'player');
  240. // el?.onPlay();
  241. // forms.preLoading = false;
  242. // }
  243. }}
  244. // onReady={player => {
  245. // if (
  246. // props.startPosition === index &&
  247. // forms.preLoading
  248. // ) {
  249. // console.log(player, 'player');
  250. // player?.play();
  251. // forms.preLoading = false;
  252. // }
  253. // }}
  254. src={url}
  255. onPlay={() => onPlay(index)}
  256. // preLoading={false}
  257. // onEnterfullscreen={() => (forms.showButton = false)}
  258. // onExitfullscreen={() => (forms.showButton = true)}
  259. />
  260. </div>
  261. )}
  262. </SwipeItem>
  263. ))}
  264. </Swipe>
  265. </>
  266. ) : (
  267. ''
  268. )}
  269. </Popup>
  270. );
  271. }
  272. });