index.tsx 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. import { defineComponent, nextTick, reactive, watch } from "vue";
  2. import styles from "./index.module.less";
  3. import iconClose from "../image/close2.svg";
  4. import {
  5. Cell,
  6. Field,
  7. NoticeBar,
  8. Popup,
  9. Radio,
  10. RadioGroup,
  11. Slider,
  12. Switch,
  13. Tab,
  14. Tabs,
  15. closeToast,
  16. showLoadingToast,
  17. showToast,
  18. } from "vant";
  19. import state, { IPlatform } from "/src/state";
  20. import { api_closeCamera, api_openCamera, api_savePicture } from "/src/helpers/communication";
  21. import iconInfo from "../image/info.svg";
  22. import iconDown from "../image/down.svg";
  23. import iconTv from "../image/tv.svg";
  24. import iconYijian from "../image/yijian.svg";
  25. import ScreenModel from "../../custom-plugins/helper-model/screen-model";
  26. import Recommendation from "../../custom-plugins/helper-model/recommendation";
  27. import { svg2canvas } from "/src/utils/svg2canvas";
  28. import { getQuery } from "/src/utils/queryString";
  29. import { browser } from "/src/utils";
  30. export default defineComponent({
  31. name: "header-settting",
  32. setup() {
  33. const query = getQuery();
  34. const helperData = reactive({
  35. show: false,
  36. recommendationShow: false, // 建议
  37. });
  38. const downPng = () => {
  39. showLoadingToast({ message: "下载中", duration: 0 });
  40. setTimeout(async () => {
  41. const svg: any = document.getElementById("osmdSvgPage1")?.cloneNode(true);
  42. if (!svg) return showToast({ message: "保存失败", type: "fail" });
  43. const cw = svg.width.animVal.value;
  44. const ch = svg.height.animVal.value;
  45. const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
  46. rect.setAttribute("x", "0");
  47. rect.setAttribute("y", "0");
  48. rect.setAttribute("width", `${cw * 2}`);
  49. rect.setAttribute("height", `${ch * 2}`);
  50. rect.setAttribute("fill", "#fff");
  51. svg.prepend(rect);
  52. if (svg) {
  53. const _canvas = svg2canvas(svg.outerHTML);
  54. const browserInfo = browser();
  55. if (state.platform === IPlatform.PC || !browserInfo.isApp) {
  56. let el: any = document.createElement("a");
  57. // 设置 href 为图片经过 base64 编码后的字符串,默认为 png 格式
  58. el.href = _canvas.toDataURL();
  59. el.download = state.examSongName;
  60. // 创建一个点击事件并对 a 标签进行触发
  61. const event = new MouseEvent("click");
  62. el.dispatchEvent(event);
  63. setTimeout(() => {
  64. showToast({ message: "保存成功", type: "success" });
  65. el = null;
  66. }, 300);
  67. } else {
  68. const base64 = _canvas.toDataURL("image/png", 1);
  69. const res = await api_savePicture({
  70. base64,
  71. });
  72. if (res?.content?.status === "success") {
  73. showToast({ message: "保存成功", type: "success" });
  74. } else {
  75. showToast({ message: "保存失败", type: "fail" });
  76. }
  77. }
  78. }
  79. }, 500);
  80. };
  81. return () => (
  82. <div class={styles["header-settting"]}>
  83. <div class={styles.content}>
  84. <Tabs border animated swipeable>
  85. <Tab title="全局设置">
  86. <NoticeBar
  87. class={styles.noticebar}
  88. left-icon={iconInfo}
  89. text="全局设置会更改所有乐谱练习及评测"
  90. />
  91. <Cell title="护眼模式" center>
  92. {{
  93. extra: () => <Switch v-model={state.setting.eyeProtection}></Switch>,
  94. }}
  95. </Cell>
  96. <div class={styles.btnsbar}>
  97. <div class={styles.btn} onClick={downPng}>
  98. <img src={iconDown} />
  99. 下载曲谱
  100. </div>
  101. <div class={styles.btn} onClick={() => (helperData.show = true)}>
  102. <img src={iconTv} />
  103. 投屏帮助
  104. </div>
  105. <div class={styles.btn} onClick={() => (helperData.recommendationShow = true)}>
  106. <img src={iconYijian} />
  107. 意见反馈
  108. </div>
  109. </div>
  110. </Tab>
  111. <Tab title="练习设置">
  112. <Cell title="循环播放" center>
  113. {{
  114. extra: () => <Switch v-model={state.setting.repeatAutoPlay}></Switch>,
  115. }}
  116. </Cell>
  117. <Cell class={[state.modeType == "evaluating" && styles.disabled]} title="显示指法" center>
  118. {{
  119. extra: () => <Switch v-model={state.setting.displayFingering}></Switch>,
  120. }}
  121. </Cell>
  122. </Tab>
  123. <Tab title="评测">
  124. <Cell class={[query.workRecord && styles.disabled]} title="评测难度" center>
  125. {{
  126. extra: () => (
  127. <RadioGroup
  128. iconSize={20}
  129. class={styles.radioGroup}
  130. v-model={state.setting.evaluationDifficulty}
  131. >
  132. <Radio name="BEGINNER">入门</Radio>
  133. <Radio name="ADVANCED">进阶</Radio>
  134. <Radio name="PERFORMER">大师</Radio>
  135. </RadioGroup>
  136. ),
  137. }}
  138. </Cell>
  139. <Cell title="延迟检测" center>
  140. {{
  141. extra: () => <Switch v-model={state.setting.soundEffect}></Switch>,
  142. }}
  143. </Cell>
  144. <Cell title="摄像头" center>
  145. {{
  146. extra: () => (
  147. <Switch
  148. v-model={state.setting.camera}
  149. onChange={(value) => {
  150. if (value) {
  151. api_openCamera();
  152. } else {
  153. api_closeCamera();
  154. }
  155. }}
  156. ></Switch>
  157. ),
  158. }}
  159. </Cell>
  160. <Cell
  161. style={{ display: state.setting.camera ? "" : "none" }}
  162. title="透明度"
  163. class={styles.sliderWrap}
  164. center
  165. >
  166. {{
  167. extra: () => (
  168. <Slider
  169. class={styles.slider}
  170. min={0}
  171. max={100}
  172. v-model:modelValue={state.setting.cameraOpacity}
  173. >
  174. {{
  175. button: () => <div class={styles.sliderBtn}>{state.setting.cameraOpacity}</div>,
  176. }}
  177. </Slider>
  178. ),
  179. }}
  180. </Cell>
  181. <Cell title="保存到相册" center>
  182. {{
  183. extra: () => <Switch v-model={state.setting.saveToAlbum}></Switch>,
  184. }}
  185. </Cell>
  186. <Cell title="开启伴奏" center>
  187. {{
  188. extra: () => <Switch v-model={state.setting.enableAccompaniment}></Switch>,
  189. }}
  190. </Cell>
  191. <Cell title="标准音高" center>
  192. {{
  193. extra: () => (
  194. <RadioGroup
  195. iconSize={20}
  196. class={styles.radioGroup}
  197. v-model={state.setting.frequency}
  198. >
  199. <Radio name={440}>440Hz</Radio>
  200. <Radio name={442}>442Hz</Radio>
  201. </RadioGroup>
  202. ),
  203. }}
  204. </Cell>
  205. {/* <Field class={styles.reactionTime} label="反应时间(毫秒)" type="digit" v-model:modelValue={state.setting.reactionTimeMs} /> */}
  206. </Tab>
  207. </Tabs>
  208. </div>
  209. <Popup
  210. class={["popup-custom", styles.screen]}
  211. v-model:show={helperData.show}
  212. onClose={() => {
  213. helperData.show = false;
  214. }}
  215. position="right"
  216. teleport="body"
  217. >
  218. <ScreenModel
  219. onClose={(open: Boolean) => {
  220. helperData.show = false;
  221. }}
  222. />
  223. </Popup>
  224. <Popup
  225. v-model:show={helperData.recommendationShow}
  226. class="popup-custom van-scale center-closeBtn"
  227. transition="van-scale"
  228. teleport="body"
  229. closeable
  230. >
  231. <Recommendation
  232. onClose={() => {
  233. helperData.recommendationShow = false;
  234. }}
  235. />
  236. </Popup>
  237. </div>
  238. );
  239. },
  240. });