index.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. import request from '@/helpers/request'
  2. import {
  3. Button,
  4. closeToast,
  5. Icon,
  6. Image,
  7. showFailToast,
  8. showLoadingToast,
  9. showSuccessToast,
  10. showToast
  11. } from 'vant'
  12. import { defineComponent, onMounted, reactive } from 'vue'
  13. import { useRoute, useRouter } from 'vue-router'
  14. import styles from './index.module.less'
  15. import { postMessage, promisefiyPostMessage } from '@/helpers/native-message'
  16. import html2canvas from 'html2canvas'
  17. import { state as baseState } from '@/state'
  18. import OQrcode from '@/components/o-qrcode'
  19. import iconImage from './images/icon-image.png'
  20. import iconWeichat from './images/icon-weichat.png'
  21. import teacherTopBg from './images/teacher-top_bg.png'
  22. import manageTopBg from './images/manage-top_bg.png'
  23. import orchestraTopBg from './images/orchestra-top_bg.png'
  24. import defaultLogo from '@/common/images/logo@2x.png'
  25. import OHeader from '@/components/o-header'
  26. import { browser } from '@/helpers/utils'
  27. export default defineComponent({
  28. name: 'save-share-image',
  29. setup() {
  30. const route = useRoute()
  31. const router = useRouter()
  32. const state = reactive({
  33. type: route.query.type as any,
  34. paramValue: 2,
  35. schoolName: '',
  36. schoolId: '',
  37. url: null as any,
  38. schoolLogo: '',
  39. loading: false
  40. })
  41. const imgs = reactive({
  42. saveLoading: false,
  43. image: null as any,
  44. shareLoading: false
  45. })
  46. const onSaveImg = async () => {
  47. // 判断是否在保存中...
  48. if (imgs.saveLoading) {
  49. return
  50. }
  51. imgs.saveLoading = true
  52. // 判断是否已经生成图片
  53. if (imgs.image) {
  54. saveImg()
  55. } else {
  56. const container: any = document.getElementById(`preview-container`)
  57. html2canvas(container, {
  58. allowTaint: true,
  59. useCORS: true,
  60. backgroundColor: null
  61. })
  62. .then(async (canvas) => {
  63. const url = canvas.toDataURL('image/png')
  64. imgs.image = url
  65. saveImg()
  66. })
  67. .catch(() => {
  68. closeToast()
  69. imgs.saveLoading = false
  70. })
  71. }
  72. }
  73. const onShare = () => {
  74. if (imgs.shareLoading) {
  75. return
  76. }
  77. imgs.shareLoading = true
  78. if (imgs.image) {
  79. openShare()
  80. } else {
  81. const container: any = document.getElementById(`preview-container`)
  82. html2canvas(container, {
  83. allowTaint: true,
  84. useCORS: true,
  85. backgroundColor: null
  86. })
  87. .then(async (canvas) => {
  88. const url = canvas.toDataURL('image/png')
  89. imgs.image = url
  90. openShare()
  91. })
  92. .catch(() => {
  93. closeToast()
  94. imgs.shareLoading = false
  95. })
  96. }
  97. }
  98. const openShare = () => {
  99. const image = imgs.image
  100. setTimeout(() => {
  101. imgs.shareLoading = false
  102. }, 100)
  103. if (image) {
  104. postMessage(
  105. {
  106. api: 'shareTripartite',
  107. content: {
  108. title: '',
  109. desc: '',
  110. image,
  111. video: '',
  112. type: 'image',
  113. // button: ['copy']
  114. shareType: 'wechat'
  115. }
  116. },
  117. (res: any) => {
  118. if (res && res.content) {
  119. showToast(res.content.message || (res.content.status ? '分享成功' : '分享失败'))
  120. }
  121. }
  122. )
  123. }
  124. }
  125. const saveImg = async () => {
  126. showLoadingToast({ message: '图片生成中...', forbidClick: true })
  127. setTimeout(() => {
  128. imgs.saveLoading = false
  129. }, 100)
  130. const res = await promisefiyPostMessage({
  131. api: 'savePicture',
  132. content: {
  133. base64: imgs.image
  134. }
  135. })
  136. if (res?.content?.status === 'success') {
  137. showSuccessToast('保存成功')
  138. } else {
  139. showFailToast('保存失败')
  140. }
  141. }
  142. // 获取当前用户所在的学校
  143. const getDetail = async () => {
  144. try {
  145. const schoolId = (baseState.user.data.schoolInfos || [])
  146. .map((item) => {
  147. return item.id
  148. })
  149. .join(',')
  150. const res = await request.get(`/api-school/school/detail/${schoolId}`, {})
  151. state.schoolName = res.data.name
  152. state.schoolId = res.data.id
  153. state.schoolLogo = res.data.logo
  154. ? res.data.logo + '@base@tag=imgScale&w=570?t=' + +new Date()
  155. : ''
  156. // 生成二维码
  157. if (state.type === 'teacher') {
  158. state.url =
  159. location.origin +
  160. '/orchestra-school/#/companion-teacher-register?id=' +
  161. res.data.id +
  162. '&name=' +
  163. res.data.name +
  164. '&t=' +
  165. +new Date()
  166. } else if (state.type === 'manage') {
  167. state.url =
  168. location.origin +
  169. '/orchestra-school/#/manage-teacher-register?id=' +
  170. res.data.id +
  171. '&name=' +
  172. res.data.name +
  173. '&t=' +
  174. +new Date()
  175. }
  176. } catch {
  177. //
  178. }
  179. }
  180. // 获取当前用户所在的学校
  181. const getOrchestraDetail = async () => {
  182. try {
  183. const res = await request.get('/api-school/orchestra/detail/' + route.query.id)
  184. state.schoolName = res.data.name
  185. state.schoolId = res.data.id
  186. state.schoolLogo = res.data.schoolLogo + '@base@tag=imgScale&w=570?t=' + +new Date()
  187. // 生成二维码
  188. state.url = window.location.origin + '/orchestra-student/#/preApply?id=' + route.query.id
  189. } catch {
  190. //
  191. }
  192. }
  193. onMounted(async () => {
  194. if (state.type === 'teacher') {
  195. document.title = '乐团伴学指导注册'
  196. } else if (state.type === 'manage') {
  197. document.title = '乐团管理老师注册'
  198. } else if (state.type === 'orchestra') {
  199. document.title = '乐团报名'
  200. }
  201. try {
  202. const { data } = await request.get('/api-school/open/paramConfig/queryByParamName', {
  203. requestType: 'form',
  204. params: {
  205. paramName: 'qr_code_expire_hours'
  206. }
  207. })
  208. state.paramValue = data.paramValue
  209. } catch {
  210. //
  211. }
  212. if (state.type === 'orchestra') {
  213. getOrchestraDetail()
  214. } else {
  215. getDetail()
  216. }
  217. })
  218. const onBack = () => {
  219. if (browser().isApp) {
  220. postMessage({ api: 'goBack' })
  221. } else {
  222. router.back()
  223. }
  224. }
  225. return () => (
  226. <div class={[styles.saveShareImage]}>
  227. <OHeader background="transparent" style="position: fixed; width: 100%;">
  228. {{
  229. content: () => (
  230. <div class={styles.btnHeader} onClick={onBack}>
  231. <Icon name="arrow-left" class={styles.iconBack} />
  232. </div>
  233. )
  234. }}
  235. </OHeader>
  236. {state.type === 'teacher' && <Image src={teacherTopBg} class={styles.topImage} />}
  237. {state.type === 'manage' && <Image src={manageTopBg} class={styles.topImage} />}
  238. {state.type === 'orchestra' && <Image src={orchestraTopBg} class={styles.topImage} />}
  239. <div
  240. class={[styles.shareContaienr, state.type === 'orchestra' && styles.orchestraContainer]}
  241. >
  242. {state.type !== 'orchestra' ? (
  243. <>
  244. <img
  245. class={[styles.schoolLogo]}
  246. src={state.schoolLogo || defaultLogo}
  247. crossorigin="anonymous"
  248. style={{
  249. objectFit: 'cover'
  250. }}
  251. />
  252. <div class={styles.schoolName}>{state.schoolName}</div>
  253. <div class={styles.shareType}>
  254. 邀请您成为
  255. <span>
  256. {state.type === 'teacher' && '乐团伴学指导'}
  257. {state.type === 'manage' && '乐团管理老师'}
  258. </span>
  259. </div>
  260. </>
  261. ) : (
  262. <>
  263. <div class={styles.schoolName}>乐团报名</div>
  264. <div class={styles.shareType}>{state.schoolName}</div>
  265. </>
  266. )}
  267. <div class={styles.qrcodeSection}>
  268. <OQrcode text={state.url} logoSize={'small'} size={'100%'} />
  269. </div>
  270. <div class={styles.memo}>扫描上方二维码完成资料填写</div>
  271. {state.type !== 'orchestra' && (
  272. <div class={styles.endTime}>
  273. 二维码将在<span>{state.paramValue}小时后</span>失效,请及时登记
  274. </div>
  275. )}
  276. </div>
  277. <div class={styles.btnGroup}>
  278. <Button class={styles.btn} round block onClick={onSaveImg}>
  279. <Icon name={iconImage} class={styles.icon} />
  280. 保存图片
  281. </Button>
  282. <Button class={styles.btn} round block onClick={onShare}>
  283. <Icon name={iconWeichat} class={styles.icon} />
  284. 分享到微信
  285. </Button>
  286. </div>
  287. {!state.loading && (
  288. <div class={[styles.saveShareImage, styles.previewSection]} id="preview-container">
  289. {state.type === 'teacher' && <Image src={teacherTopBg} class={styles.topImage} />}
  290. {state.type === 'manage' && <Image src={manageTopBg} class={styles.topImage} />}
  291. {state.type === 'orchestra' && <Image src={orchestraTopBg} class={styles.topImage} />}
  292. <div
  293. class={[
  294. styles.shareContaienr,
  295. state.type === 'orchestra' && styles.orchestraContainer
  296. ]}
  297. >
  298. {state.type !== 'orchestra' ? (
  299. <>
  300. <img
  301. class={[styles.schoolLogo]}
  302. src={state.schoolLogo || defaultLogo}
  303. crossorigin="anonymous"
  304. style={{
  305. objectFit: 'cover'
  306. }}
  307. />
  308. <div class={styles.schoolName}>{state.schoolName}</div>
  309. <div class={styles.shareType}>
  310. 邀请您成为
  311. <span>
  312. {state.type === 'teacher' && '乐团伴学指导'}
  313. {state.type === 'manage' && '乐团管理老师'}
  314. </span>
  315. </div>
  316. </>
  317. ) : (
  318. <>
  319. <div class={styles.schoolName}>乐团报名</div>
  320. <div class={styles.shareType}>{state.schoolName}</div>
  321. </>
  322. )}
  323. <div class={styles.qrcodeSection}>
  324. <OQrcode text={state.url} logoSize={'small'} size={'100%'} />
  325. </div>
  326. <div class={styles.memo}>扫描上方二维码完成资料填写</div>
  327. {state.type !== 'orchestra' && (
  328. <div class={styles.endTime}>
  329. 二维码将在<span>{state.paramValue}小时后</span>失效,请及时登记
  330. </div>
  331. )}
  332. </div>
  333. </div>
  334. )}
  335. </div>
  336. )
  337. }
  338. })