index.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. import { computed, defineComponent, onBeforeUnmount, onMounted, reactive, ref, watch } from "vue";
  2. import styles from "./index.module.less";
  3. import store from 'store'
  4. import Title from "./title";
  5. import icons from "./image/headerTop.json";
  6. import { Badge, Circle, Popover, Popup } from "vant";
  7. import { metronomeData } from "../../helpers/metronome";
  8. import Speed from "./speed";
  9. import { evaluatingData, handleEndBegin, handleStartEvaluat } from "/src/view/evaluating";
  10. import Settting from "./settting";
  11. import ModeTypeMode from "./mode-type-mode";
  12. import state, { handleChangeSection, handleResetPlay, handleRessetState, togglePlay } from "/src/state";
  13. import { getAudioCurrentTime } from "/src/view/audio-list";
  14. import { followData, toggleFollow } from "/src/view/follow-practice";
  15. import { api_back, api_suspendPlay } from "/src/helpers/communication";
  16. import MusicType from "./music-type";
  17. import { handleNoEndExit } from "../custom-plugins/recording-time";
  18. import { handle_stopFollow } from "../follow-model";
  19. export const headData = reactive({
  20. speedShow: false,
  21. musicTypeShow: false,
  22. modeMode: true, // 模式弹框
  23. settingMode: false, // 设置弹框
  24. });
  25. /** 关闭模式选择 */
  26. export const handleCloseModeMode = (type = false) => {
  27. headData.modeMode = type;
  28. };
  29. export default defineComponent({
  30. name: "header-top",
  31. setup() {
  32. /** 切换模式 */
  33. const handleChangeModeType = (value: "practise" | "follow" | "evaluating") => {
  34. if (value === "evaluating") {
  35. handleStartEvaluat();
  36. } else if (value === "follow") {
  37. toggleFollow();
  38. }
  39. headData.modeMode = false;
  40. };
  41. /** 设置按钮 */
  42. const settingBtn = computed(() => {
  43. // 音频播放中 禁用
  44. if (state.playState === "play") return { display: true, disabled: true };
  45. // 评测开始 禁用, 跟练开始 禁用
  46. if (evaluatingData.startBegin || followData.start) return { display: true, disabled: true };
  47. return {
  48. display: true,
  49. disabled: false,
  50. };
  51. });
  52. /** 转谱按钮 */
  53. const converBtn = computed(() => {
  54. // 音频播放中 禁用
  55. if (state.playState === "play") return { display: true, disabled: true };
  56. // 评测开始 禁用
  57. if (evaluatingData.startBegin || followData.start) return { display: true, disabled: true };
  58. return {
  59. disabled: false,
  60. display: true,
  61. };
  62. });
  63. /** 速度按钮 */
  64. const speedBtn = computed(() => {
  65. // 选择模式 不显示
  66. if (headData.modeMode) return { display: false, disabled: false };
  67. // 音频播放中 禁用
  68. if (state.playState === "play") return { display: true, disabled: true };
  69. // 评测模式 禁用
  70. if (state.modeType === "evaluating") return { display: true, disabled: true };
  71. // 跟练模式 不显示
  72. if (state.modeType === "follow") return { display: false, disabled: true };
  73. return {
  74. disabled: false,
  75. display: true,
  76. };
  77. });
  78. /** 指法按钮 */
  79. const fingeringBtn = computed(() => {
  80. // 没有指法 不显示
  81. if (!state.fingeringInfo.name) return { display: false, disabled: true };
  82. // 选择模式 不显示
  83. if (headData.modeMode) return { display: false, disabled: false };
  84. // 音频播放中 禁用
  85. if (state.playState === "play") return { display: true, disabled: true };
  86. // 评测模式 不显示,跟练模式 不显示
  87. if (["evaluating", "follow"].includes(state.modeType)) return { display: false, disabled: true };
  88. return {
  89. disabled: false,
  90. display: true,
  91. };
  92. });
  93. /** 选段按钮 */
  94. const selectBtn = computed(() => {
  95. // 选择模式 不显示
  96. if (headData.modeMode) return { display: false, disabled: false };
  97. // 音频播放中 禁用
  98. if (state.playState === "play") return { display: true, disabled: true };
  99. // 评测模式 不显示,跟练模式 不显示
  100. if (["evaluating", "follow"].includes(state.modeType)) return { display: false, disabled: true };
  101. return {
  102. disabled: false,
  103. display: true,
  104. };
  105. });
  106. /** 原声按钮 */
  107. const originBtn = computed(() => {
  108. // 选择模式 不显示
  109. if (headData.modeMode) return { display: false, disabled: false };
  110. // 跟练模式 不显示
  111. if (state.modeType === "follow") return { display: false, disabled: true };
  112. // 评测开始 禁用
  113. if (state.modeType === "evaluating") return { display: true, disabled: true };
  114. return {
  115. disabled: false,
  116. display: true,
  117. };
  118. });
  119. /** 模式切换按钮 */
  120. const toggleBtn = computed(() => {
  121. // 选择模式 不显示
  122. if (headData.modeMode) return { display: false, disabled: false };
  123. // 跟练开始 禁用
  124. if (followData.start) return { display: true, disabled: true };
  125. return {
  126. display: true,
  127. disabled: false,
  128. };
  129. });
  130. /** 播放按钮 */
  131. const playBtn = computed(() => {
  132. // 选择模式 不显示
  133. if (headData.modeMode) return { display: false, disabled: false };
  134. // 评测模式 不显示,跟练模式 不显示
  135. if (["evaluating", "follow"].includes(state.modeType)) return { display: false, disabled: true };
  136. return {
  137. display: true,
  138. disabled: false,
  139. };
  140. });
  141. /** 重播按钮 */
  142. const resetBtn = computed(() => {
  143. // 选择模式 不显示
  144. if (headData.modeMode) return { display: false, disabled: false };
  145. // 评测模式 不显示,跟练模式 不显示
  146. if (["evaluating", "follow"].includes(state.modeType)) return { display: false, disabled: true };
  147. // 播放进度为0 不显示, 不是暂停状态不显示
  148. const currentTime = getAudioCurrentTime();
  149. if (currentTime === 0 || state.playState !== "paused") return { display: false, disabled: true };
  150. return {
  151. display: true,
  152. disabled: false,
  153. };
  154. });
  155. /** 返回 */
  156. const handleBack = () => {
  157. handleNoEndExit();
  158. api_back();
  159. };
  160. onMounted(() => {
  161. api_suspendPlay(() => {
  162. if (state.modeType === "practise") {
  163. togglePlay("paused");
  164. } else if (state.modeType === "evaluating") {
  165. handleEndBegin();
  166. } else if (state.modeType === "follow") {
  167. handle_stopFollow();
  168. }
  169. });
  170. });
  171. // 设置改变触发
  172. watch(state.setting, () => {
  173. store.set("musicscoresetting", state.setting);
  174. });
  175. return () => (
  176. <div class={styles.headerTop}>
  177. <div class={styles.back} onClick={handleBack}>
  178. <img src={icons["icon-back"]} />
  179. </div>
  180. <Title text={state.examSongName} rightView={false} />
  181. <div class={styles.headRight}>
  182. {/* 模式切换按钮 */}
  183. <div
  184. id="tips-step-0"
  185. style={{ display: toggleBtn.value.display ? "" : "none" }}
  186. class={[styles.btn, toggleBtn.value.disabled && styles.disabled]}
  187. onClick={() => {
  188. handleRessetState();
  189. headData.modeMode = true;
  190. }}
  191. >
  192. <img class={styles.iconBtn} src={icons.modelType} />
  193. <span>模式</span>
  194. </div>
  195. {/* 原声按钮 */}
  196. <div
  197. id="tips-step-1"
  198. style={{ display: originBtn.value.display ? "" : "none" }}
  199. class={[styles.btn, originBtn.value.disabled && styles.disabled]}
  200. onClick={() => {
  201. state.playSource = state.playSource === "music" ? "background" : "music";
  202. }}
  203. >
  204. <img class={styles.iconBtn} src={state.playSource === "music" ? icons.music : icons.accompaniment} />
  205. <span>{state.playSource === "music" ? "原声" : "伴奏"}</span>
  206. </div>
  207. {/* 选段按钮 */}
  208. <div
  209. id="tips-step-2"
  210. style={{ display: selectBtn.value.display ? "" : "none" }}
  211. class={[styles.btn, selectBtn.value.disabled && styles.disabled]}
  212. onClick={() => handleChangeSection()}
  213. >
  214. <img class={styles.iconBtn} src={state.section.length === 0 ? icons.section : state.section.length === 1 ? icons.section1 : icons.section2} />
  215. <span>选段</span>
  216. </div>
  217. {/* 指法按钮 */}
  218. <div
  219. id="tips-step-3"
  220. style={{ display: fingeringBtn.value.display ? "" : "none" }}
  221. class={[styles.btn, fingeringBtn.value.disabled && styles.disabled]}
  222. onClick={() => {
  223. state.setting.displayFingering = !state.setting.displayFingering;
  224. }}
  225. >
  226. <img class={styles.iconBtn} src={state.setting.displayFingering ? icons.fingeringOn : icons.fingeringOff} />
  227. <span>指法</span>
  228. </div>
  229. {/* <div
  230. class={[styles.btn]}
  231. onClick={async () => {
  232. metronomeData.lineShow = !metronomeData.lineShow;
  233. }}
  234. >
  235. <img class={styles.iconBtn} src={headImg("iconStep.png")} />
  236. <span>{metronomeData.lineShow ? "高级" : "初级"}</span>
  237. </div> */}
  238. {/* <div
  239. class={styles.btn}
  240. onClick={async () => {
  241. metronomeData.disable = !metronomeData.disable;
  242. metronomeData.metro?.initPlayer();
  243. }}
  244. >
  245. <img style={{ display: metronomeData.disable ? "block" : "none" }} class={styles.iconBtn} src={headImg("tickoff.png")} />
  246. <img style={{ display: !metronomeData.disable ? "block" : "none" }} class={styles.iconBtn} src={headImg("tickon.png")} />
  247. <span style={{ whiteSpace: "nowrap" }}>节拍器</span>
  248. </div> */}
  249. {/* 速度按钮 */}
  250. <Popover trigger="manual" v-model:show={headData.speedShow} placement="bottom" overlay={false}>
  251. {{
  252. reference: () => (
  253. <div
  254. id="tips-step-4"
  255. style={{ display: speedBtn.value.display ? "" : "none" }}
  256. class={[styles.btn, speedBtn.value.disabled && styles.disabled]}
  257. onClick={(e: Event) => {
  258. e.stopPropagation();
  259. headData.speedShow = !headData.speedShow;
  260. }}
  261. >
  262. <Badge class={styles.badge} content={state.speed}>
  263. <img class={styles.iconBtn} src={icons.speed} />
  264. </Badge>
  265. <span>速度</span>
  266. </div>
  267. ),
  268. default: () => <Speed />,
  269. }}
  270. </Popover>
  271. {/* 转简谱按钮 */}
  272. <Popover trigger="manual" v-model:show={headData.musicTypeShow} placement="bottom-end" overlay={false}>
  273. {{
  274. reference: () => (
  275. <div
  276. style={{ display: converBtn.value.display ? "" : "none" }}
  277. class={[styles.btn, converBtn.value.disabled && styles.disabled]}
  278. onClick={(e: Event) => {
  279. e.stopPropagation();
  280. headData.musicTypeShow = !headData.musicTypeShow;
  281. }}
  282. >
  283. <img class={styles.iconBtn} src={icons["icon-zhuanpu"]} />
  284. <span>转简谱</span>
  285. </div>
  286. ),
  287. default: () => <MusicType />,
  288. }}
  289. </Popover>
  290. {/* 设置按钮 */}
  291. <div
  292. style={{ display: settingBtn.value.display ? "" : "none" }}
  293. class={[styles.btn, settingBtn.value.disabled && styles.disabled]}
  294. onClick={() => (headData.settingMode = true)}
  295. >
  296. <img class={styles.iconBtn} src={icons.setting} />
  297. <span>设置</span>
  298. </div>
  299. {/* 播放按钮 */}
  300. <div
  301. style={{ display: playBtn.value.display ? "" : "none" }}
  302. class={[styles.btn, styles.playBtn, playBtn.value.disabled && styles.disabled]}
  303. id="tips-step-5"
  304. onClick={() => togglePlay()}
  305. >
  306. <div class={styles.btnWrap}>
  307. <img class={styles.iconBtn} src={state.playState === "paused" ? icons.play : icons.pause} />
  308. <Circle
  309. style={{ display: state.playState === "paused" ? "none" : "" }}
  310. class={styles.progress}
  311. stroke-width={80}
  312. currentRate={state.playProgress}
  313. rate={100}
  314. color="#FFC830"
  315. />
  316. </div>
  317. </div>
  318. {/* 重播按钮 */}
  319. <div
  320. style={{ display: resetBtn.value.display ? "" : "none" }}
  321. class={[styles.btn, styles.resetBtn, resetBtn.value.disabled && styles.disabled]}
  322. id="tips-step-7"
  323. onClick={() => handleResetPlay()}
  324. >
  325. <img class={styles.iconBtn} src={icons["reset"]} />
  326. </div>
  327. </div>
  328. <Popup v-model:show={headData.settingMode} class="popup-custom van-scale" transition="van-scale" teleport="body">
  329. <Settting onClose={() => (headData.settingMode = false)} />
  330. </Popup>
  331. <Popup v-model:show={headData.modeMode} teleport="body" class="popup-custom" position="bottom" closeOnClickOverlay={false} overlay={false}>
  332. <ModeTypeMode onClose={(value) => handleChangeModeType(value)} />
  333. </Popup>
  334. </div>
  335. );
  336. },
  337. });