content.tsx 11 KB

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