sett.tsx 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. import { defineComponent, Ref, ref } from 'vue'
  2. import { Button, Cell, CellGroup, Col, Dialog, Divider, NoticeBar, Radio, RadioGroup, Row, Switch, Toast, Field } from 'vant'
  3. import InfoIcon from './info.svg'
  4. import iconDown from './icons/down.svg'
  5. import iconTv from './icons/tv.png'
  6. import iconYijian from './icons/yijian.png'
  7. import styles from './index.module.less'
  8. import evastyles from '../evaluating/index.module.less'
  9. import detailState from '/src/pages/detail/state'
  10. import SettingState from '/src/pages/detail/setting-state'
  11. import runtime from '/src/pages/detail/runtime'
  12. import iconTitle from '../evaluating/icons/title.svg'
  13. import iconCancel from '../evaluating/icons/cancel.svg'
  14. import iconConfirm from '../evaluating/icons/confirm.svg'
  15. import { show as helperShow } from '../helper'
  16. // import { settingPopup, suggestPopup, openSuggestPopup } from '../../buttons'
  17. import { svgtopng, additionalTitle } from '/src/subpages/colexiu/helpers/index'
  18. import { promisefiyPostMessage } from '/src/helpers/native-message'
  19. import Popups from '..'
  20. import Feedback from '../feedback'
  21. import { useReload } from '../../uses'
  22. export const suggestPopup: Ref<any> = ref(null)
  23. export const confirmShow: Ref<boolean> = ref(false)
  24. export const switchProps = {
  25. 'active-color': '#2dc7aa',
  26. 'inactive-color': '#CCCCCC',
  27. size: '20px',
  28. }
  29. /** 生成水印 */
  30. const addWatermark = (svg: SVGAElement, text: string, w: number, h: number) => {
  31. const lastg = svg.querySelector('g')
  32. const g = document.createElementNS('http://www.w3.org/2000/svg', 'g')
  33. g.setAttribute('transform', `rotate(-30)`)
  34. // g.setAttribute('fill', 'rgba(0,0,0,.3)')
  35. const mw = Math.max(w, h)
  36. const ww = 600
  37. const wh = 500
  38. const xnum = Math.floor((mw * 2) / ww) + 1
  39. const ynum = Math.floor((mw * 2) / wh) + 1
  40. const createEl = (x: number, y: number) => {
  41. const textNS = document.createElementNS('http://www.w3.org/2000/svg', 'text')
  42. textNS.textContent = text
  43. textNS.setAttribute('fill', 'rgba(0,0,0,.3)')
  44. // textNS.setAttribute('transform', 'rotate(-30)')
  45. textNS.setAttribute('font-size', '55')
  46. textNS.setAttribute('text-anchor', 'middle')
  47. textNS.setAttribute('y', '' + y)
  48. textNS.setAttribute('x', '' + x)
  49. return textNS
  50. }
  51. for (let i = 0; i < ynum; i++) {
  52. for (let j = 0; j < xnum; j++) {
  53. g.appendChild(createEl((j - 1) * ww, (i - 1) * wh))
  54. }
  55. }
  56. lastg?.insertAdjacentElement('afterend', g)
  57. // lastg?.insertAdjacentHTML('beforeend', `<g><text X="50" y="150" fill="rgba(0,0,0,.3)" transform="rotate(-30)" font-size="55">酷乐秀</text></g>`)
  58. }
  59. export default defineComponent({
  60. name: 'ColexiuSettingSett',
  61. setup() {
  62. const download = async () => {
  63. Toast.loading({
  64. message: '正在生成图片...',
  65. duration: 0,
  66. })
  67. try {
  68. if (runtime.osmd) {
  69. const svgNode = document.querySelector('#osmdSvgPage1')!
  70. const rectData: any = svgNode.getBoundingClientRect()
  71. let backend = {
  72. ctx: {
  73. svg: svgNode,
  74. width: rectData.width,
  75. height: rectData.height,
  76. },
  77. }
  78. if (Array.isArray(runtime.osmd?.Drawer?.Backends)) {
  79. backend = runtime.osmd.Drawer.Backends[0]
  80. }
  81. const { width, height } = (backend as any).ctx
  82. console.log('🚀 ~ width', backend)
  83. const cw = width
  84. const ch = height
  85. const svg: SVGAElement = (backend as any).ctx.svg.cloneNode(true)
  86. const firstg = svg.querySelector('g')
  87. // 前面插入一个空白的矩形背景
  88. firstg?.insertAdjacentHTML(
  89. 'beforebegin',
  90. `<rect x="0" y="0" width="${cw * 2}" height="${ch * 2}" fill="#fff"/>`
  91. )
  92. // addWatermark(svg, '酷乐秀', cw, ch)
  93. const cont = new XMLSerializer().serializeToString(svg)
  94. const png = await svgtopng(cont, cw, ch)
  95. const titlePng = await additionalTitle(detailState.activeDetail?.musicSheetName, png as string)
  96. const res = await promisefiyPostMessage({
  97. api: 'savePicture',
  98. content: {
  99. base64: titlePng,
  100. },
  101. })
  102. if (res?.content?.status === 'success') {
  103. Toast.success('保存成功')
  104. } else {
  105. Toast.fail('保存失败')
  106. }
  107. }
  108. } catch (error) {
  109. Toast.fail('保存失败')
  110. }
  111. }
  112. // 自定义每行小节数量
  113. const confirmCustomNum = () => {
  114. let customNoduleInfo = JSON.parse(localStorage.getItem('customNoduleInfo')) || []
  115. const matchIdx = customNoduleInfo.findIndex((n: any) => n.id === detailState.activeDetail?.examSongId)
  116. if (matchIdx > -1) {
  117. customNoduleInfo[matchIdx].customNum = Number(SettingState.sett.customNoduleNum)
  118. } else {
  119. customNoduleInfo.push({
  120. id: detailState.activeDetail?.examSongId,
  121. customNum: Number(SettingState.sett.customNoduleNum)
  122. })
  123. }
  124. localStorage.setItem('customNoduleInfo', JSON.stringify(customNoduleInfo))
  125. useReload()
  126. }
  127. return () => {
  128. return (
  129. <>
  130. <div>
  131. <NoticeBar
  132. class={styles.noticebar}
  133. left-icon={InfoIcon}
  134. background="#FFF6E8"
  135. color="var(--van-primary-color)"
  136. text="全局设置会更改所有乐谱练习及评测"
  137. />
  138. <div class={styles.groupBox} style={{ height: 'auto' }}>
  139. <Cell center border={false} title="护眼模式">
  140. <Switch v-model={SettingState.sett.eyeProtection} {...switchProps}></Switch>
  141. </Cell>
  142. {/** 大雅金唐曲目自定义小节数 */}
  143. {
  144. detailState.isDaYaCategory &&
  145. <>
  146. <Cell center border={false} title="自定义每行小节数">
  147. <Switch v-model={SettingState.sett.openCustomNodule} {...switchProps}></Switch>
  148. </Cell>
  149. {
  150. <div class={styles.columnItem}>
  151. <Cell center border={false} title="每行小节数">
  152. <Field
  153. type="number"
  154. disabled={!SettingState.sett.openCustomNodule}
  155. value={SettingState.sett.customNoduleNum}
  156. v-model={SettingState.sett.customNoduleNum}
  157. inputAlign="right"
  158. maxlength={2}
  159. formatter={(value) => value.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}
  160. />
  161. </Cell>
  162. <div class={styles.columnBtn} onClick={confirmCustomNum}>确认</div>
  163. </div>
  164. }
  165. </>
  166. }
  167. <Divider />
  168. {/* <Cell center border={false} title="乐谱大小">
  169. <div style={{ display: 'flex' }}>
  170. <RadioGroup
  171. iconSize={20}
  172. class={styles.radioGroup}
  173. v-model={SettingState.sett.scoreSize}
  174. onChange={(val) => {
  175. confirmShow.value = true
  176. // MusicSheetRef.value?.setRender?.()
  177. // SettingState.sett.scoreSize = val
  178. }}
  179. >
  180. <Radio name={'small'}>小</Radio>
  181. <Radio name={'middle'}>中</Radio>
  182. <Radio name={'large'}>大</Radio>
  183. </RadioGroup>
  184. </div>
  185. </Cell> */}
  186. {/* <Cell border={false} title="开启节拍器">
  187. <Switch v-model={SettingState.sett.fingering}>off</Switch>
  188. </Cell> */}
  189. </div>
  190. <div class={styles.btnsbar}>
  191. {/* <div class={styles.btn} onClick={download}>
  192. <img class={styles.iconBtn} src={iconTv} />
  193. 下载曲谱
  194. </div> */}
  195. <div class={styles.btn} onClick={() => (helperShow.value = true)}>
  196. <img class={styles.iconBtn} src={iconTv} />
  197. 投屏帮助
  198. </div>
  199. <div
  200. class={styles.btn}
  201. onClick={() => {
  202. suggestPopup.value?.onShow()
  203. }}
  204. >
  205. <img class={styles.iconBtn} src={iconYijian} />
  206. 意见反馈
  207. </div>
  208. </div>
  209. </div>
  210. <Popups
  211. ref={suggestPopup}
  212. overlay={false}
  213. style={{
  214. borderRadius: '8px',
  215. }}
  216. >
  217. <Feedback />
  218. </Popups>
  219. <Dialog.Component
  220. teleport="body"
  221. class={evastyles.confirm}
  222. style={{
  223. overflow: 'initial',
  224. }}
  225. vSlots={{
  226. title: () => <img class={evastyles.iconTitle} src={iconTitle} />,
  227. footer: () => (
  228. <div class={evastyles.footer}>
  229. <img src={iconCancel} onClick={() => (confirmShow.value = false)} />
  230. <img src={iconConfirm} onClick={useReload} />
  231. </div>
  232. ),
  233. }}
  234. v-model:show={confirmShow.value}
  235. message={'设置成功,是否立即重新加载?'}
  236. />
  237. </>
  238. )
  239. }
  240. },
  241. })