content.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. import { Button, Dialog, Grid, GridItem, Popup, Toast } from 'vant'
  2. import { defineComponent, onBeforeUnmount, onMounted, ref, toRefs, watch } from 'vue'
  3. import qs from 'query-string'
  4. // import { getImageUrl } from '../../header'
  5. import appState from '/src/state'
  6. import detailState from '/src/pages/detail/state'
  7. import styles from './index.module.less'
  8. import backIcon from '../sound-effect/icons/back.svg'
  9. import iconBadge from './icons/icon-badge.svg'
  10. import iconLianxi from './icons/icon-lianxi.svg'
  11. import iconReport from './icons/icon-report.svg'
  12. import TryIcon from './icons/try.svg'
  13. import IntegrityIcon from './icons/integrity.svg'
  14. import IntonationIcon from './icons/intonation.svg'
  15. import CadenceIcon from './icons/cadence.svg'
  16. // import bg from './icons/bg.svg'
  17. import runtime from '/src/pages/detail/runtime'
  18. import { postMessage } from '/src/helpers/native-message'
  19. import { ResultContent } from './index'
  20. import { getLeveByScoreId } from '/src/pages/detail/evaluating/helper'
  21. import Image1 from './icons/5.svg'
  22. import Image2 from './icons/4.svg'
  23. import Image3 from './icons/3.svg'
  24. import Image4 from './icons/2.svg'
  25. import Image5 from './icons/1.png'
  26. import iconShare from './icons/icon-share.svg'
  27. import iconUpload from './icons/icon-upload.svg'
  28. import { useOriginSearch } from '../../uses'
  29. import { onChangeModelType } from '../../buttons'
  30. import { useRoute } from 'vue-router'
  31. const scoreInfos: any = {
  32. 1: {
  33. img: Image1,
  34. tips: '你的演奏不太好,音准和完整性还需加强,再练一练吧~',
  35. mome: '敢于尝试',
  36. },
  37. 2: {
  38. img: Image2,
  39. tips: '你的演奏还不熟练,音准和完整性还需加强,加紧训练才能有好成绩哦~',
  40. mome: '还要加油哦',
  41. },
  42. 3: {
  43. img: Image3,
  44. tips: '你的演奏还不流畅,音准和节奏还需加强,科学的练习才能更完美哦~',
  45. mome: '突破自我',
  46. },
  47. 4: {
  48. img: Image4,
  49. tips: '你的演奏还不错,继续加油吧,加强音准,离完美就差一步啦~',
  50. mome: '崭露头角',
  51. },
  52. 5: {
  53. img: Image5,
  54. tips: '你的演奏非常不错,音准的把握和节奏稍有瑕疵,完整性把握的很好~',
  55. mome: '你很棒',
  56. },
  57. }
  58. export const evaluatingShow = ref<boolean>(false)
  59. //效音组件
  60. export default defineComponent({
  61. name: 'ColexiuEvaluatingContent',
  62. props: {
  63. data: {
  64. type: Object as () => ResultContent | null,
  65. default: () => null,
  66. },
  67. },
  68. emits: ['restart', 'upload'],
  69. setup(props, { emit }) {
  70. const route = useRoute()
  71. /** 是否是单元测试 */
  72. const isUnitTest = route.query.unitId
  73. const search = useOriginSearch()
  74. const shareShow = ref(false)
  75. const shareLoadedPngData = ref('')
  76. const { data } = toRefs(props)
  77. const pathname =
  78. location.origin.indexOf('localhost') > -1 || location.origin.indexOf('192.168') > -1 ? '' : '/orchestra-music-score'
  79. const getShareUrl = () => {
  80. const shareData: any = {
  81. id: data.value?.recordId,
  82. musicId: search.id,
  83. name: appState.user?.username || '',
  84. subjectName: (appState.user?.subjectName || '').split(',')[0] || '',
  85. avatar: encodeURIComponent(appState.user?.avatar || ''),
  86. score: data.value?.score || 0,
  87. examSongName: detailState.activeDetail?.examSongName || '',
  88. }
  89. // if (!this.isStrike) {
  90. // data.intonation = data.value?.intonation
  91. // data.cadence = data.value?.cadence
  92. // data.integrity = data.value?.integrity
  93. // }
  94. if (!detailState.isPercussion) {
  95. shareData.intonation = data.value?.intonation
  96. shareData.cadence = data.value?.cadence
  97. shareData.integrity = data.value?.integrity
  98. }
  99. return `${location.origin}${pathname}/share-colexiu-evaluating/index.html?${qs.stringify(shareData)}`
  100. }
  101. const shareLoaded = (evt: Event) => {
  102. const el = evt.target as HTMLIFrameElement
  103. if (el) {
  104. // @ts-ignore
  105. el.contentWindow.setPng = (data: string) => {
  106. shareLoadedPngData.value = data
  107. }
  108. }
  109. }
  110. const shareNext = () => {
  111. if (!shareLoadedPngData.value) return
  112. postMessage(
  113. {
  114. api: 'shareAchievements',
  115. content: {
  116. title: '分享我的乐器练习进度,一起见证我的成长!',
  117. desc: '晒一下我的评测分数,快来“小酷AI”上和我PK一下吧!',
  118. image: shareLoadedPngData.value,
  119. video: '',
  120. type: 'image',
  121. button: ['copy'],
  122. url: getShareUrl(),
  123. },
  124. },
  125. (res) => {
  126. if (res?.content?.status) {
  127. shareShow.value = false
  128. }
  129. if (res?.content?.message) {
  130. Toast(res?.content?.message)
  131. }
  132. }
  133. )
  134. }
  135. const viewReport = () => {
  136. // this.$router.push('/report')
  137. postMessage({
  138. api: 'openWebView',
  139. content: {
  140. // url: location.origin + '/accompany/#/report/',
  141. url:
  142. location.origin +
  143. pathname +
  144. '/colexiu-report.html?source=evaluation&musicId=' +
  145. search.id +
  146. '&id=' +
  147. data.value?.recordId || '',
  148. orientation: 0,
  149. isHideTitle: true, // 此处兼容安卓,意思为隐藏全部头部
  150. statusBarTextColor: false,
  151. isOpenLight: true,
  152. },
  153. })
  154. }
  155. return () => {
  156. const info = getLeveByScoreId(data.value?.score)
  157. return (
  158. <div>
  159. <div class={styles.box}>
  160. <div class={styles.wrap}>
  161. <div class={styles.wrapContainer}>
  162. <div class={styles.top}>
  163. {/* {info === 5 && <img class={styles.iconLeft} src={iconBadge} />} */}
  164. <div class={styles.title}>
  165. <div style={{ position: 'relative', zIndex: 1, 'white-space': 'nowrap' }}>
  166. <span class={styles.num}>{data.value?.score}</span>
  167. <span class={styles.txt}>分 {scoreInfos[info].mome}</span>
  168. </div>
  169. <div class={styles.line}></div>
  170. </div>
  171. <img class={styles.iconTop} src={scoreInfos[info].img} />
  172. </div>
  173. <div class={styles.evaluatWrap}>
  174. <Grid>
  175. <GridItem
  176. vSlots={{
  177. icon: () => (
  178. <div>
  179. <img class={styles.evaluatIcon} src={IntonationIcon} />
  180. <span class={styles.evaluatTitle}>音准</span>
  181. </div>
  182. ),
  183. text: () => (
  184. <span class={styles.fraction}>
  185. {data.value?.intonation}
  186. <span>分</span>
  187. </span>
  188. ),
  189. }}
  190. ></GridItem>
  191. <div class={styles.line}></div>
  192. <GridItem
  193. vSlots={{
  194. icon: () => (
  195. <div>
  196. <img class={styles.evaluatIcon} src={CadenceIcon} />
  197. <span class={styles.evaluatTitle}>节奏</span>
  198. </div>
  199. ),
  200. text: () => (
  201. <span class={styles.fraction}>
  202. {data.value?.cadence}
  203. <span>分</span>
  204. </span>
  205. ),
  206. }}
  207. ></GridItem>
  208. <div class={styles.line}></div>
  209. <GridItem
  210. vSlots={{
  211. icon: () => (
  212. <div>
  213. <img class={styles.evaluatIcon} src={IntegrityIcon} />
  214. <span class={styles.evaluatTitle}>完整性</span>
  215. </div>
  216. ),
  217. text: () => (
  218. <span class={styles.fraction}>
  219. {data.value?.integrity}
  220. <span>分</span>
  221. </span>
  222. ),
  223. }}
  224. ></GridItem>
  225. </Grid>
  226. </div>
  227. <div class={styles.tips}>{scoreInfos[info].tips}</div>
  228. </div>
  229. <div class={styles.btns}>
  230. {detailState.frozenMode || isUnitTest ? null : (
  231. <Button onClick={() => {
  232. runtime.evaluatingStatus = false
  233. onChangeModelType('practice')
  234. }}>
  235. <img class={styles.btnIcon} src={iconLianxi} />
  236. </Button>
  237. )}
  238. <Button style={{ margin: '0 4px' }} onClick={() => emit('restart')}>
  239. <img class={styles.btnIcon} src={TryIcon} />
  240. </Button>
  241. {isUnitTest ? null : <Button onClick={viewReport}>
  242. <img class={styles.btnIcon} src={iconReport} alt="查看报告" />
  243. </Button>}
  244. </div>
  245. {isUnitTest ? null : <div class={styles.rigthBtns}>
  246. <div class={styles.skepBtn} onClick={() => emit('upload')}>
  247. <img src={iconUpload} />
  248. 上传
  249. </div>
  250. {/* <div class={styles.skepBtn} onClick={() => (shareShow.value = true)}>
  251. <img src={iconShare} />
  252. 分享
  253. </div> */}
  254. </div>}
  255. </div>
  256. <Popup
  257. teleport="body"
  258. show={shareShow.value}
  259. style={{
  260. background: 'transparent',
  261. }}
  262. >
  263. <div style={{ textAlign: 'right' }}>
  264. <Button class={styles.sbtn} onClick={shareNext} round type="primary" color="#2DC7AA">
  265. 分享
  266. </Button>
  267. <Button class={styles.sbtn} onClick={() => (shareShow.value = false)} round>
  268. 关闭
  269. </Button>
  270. </div>
  271. <iframe
  272. style={{
  273. width: '50vw',
  274. border: 'none',
  275. height: '70vh',
  276. marginTop: '1vh',
  277. }}
  278. src={getShareUrl()}
  279. onLoad={shareLoaded}
  280. />
  281. </Popup>
  282. <Button class={styles.button} icon={backIcon} onClick={() => emit('restart')}></Button>
  283. </div>
  284. </div>
  285. )
  286. }
  287. },
  288. })