index.tsx 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. import { computed, defineComponent, onMounted, reactive, ref } from "vue";
  2. import styles from "./index.module.less";
  3. import Title from "./title";
  4. import icons from "./image/headerTop.json";
  5. import { Badge, Circle, Popover } from "vant";
  6. import { metronomeData } from "../../helpers/metronome";
  7. import Speed from "./speed";
  8. import { handleStartEvaluat } from "/src/view/evaluating";
  9. import { Popup } from "@varlet/ui";
  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 { toggleFollow } from "/src/view/follow-practice";
  15. import { api_back } from "/src/helpers/communication";
  16. import MusicType from "./music-type";
  17. export const headData = reactive({
  18. speedShow: false,
  19. musicTypeShow: false,
  20. });
  21. export default defineComponent({
  22. name: "header-top",
  23. setup() {
  24. const headerData = reactive({
  25. settingMode: false,
  26. modeMode: true, // 模式弹框
  27. });
  28. const headRef = ref();
  29. const toggleEvaluat = () => {
  30. handleStartEvaluat();
  31. };
  32. /** 切换模式 */
  33. const handleChangeModeType = (value: "practise" | "follow" | "evaluating") => {
  34. if (value === "evaluating") {
  35. toggleEvaluat();
  36. } else if (value === "follow") {
  37. toggleFollow();
  38. }
  39. headerData.modeMode = false;
  40. };
  41. const disabledList = ["evaluating"];
  42. /** 按钮禁用 */
  43. /** 重播按钮显示条件 */
  44. const resetBtnDisplay = computed(() => {
  45. const currentTime = getAudioCurrentTime();
  46. const playState = state.playState;
  47. const modeType = state.modeType;
  48. return currentTime !== 0 && playState !== "play" && modeType === "practise";
  49. });
  50. /** 播放按钮显示条件 */
  51. const playBtnDisplay = computed(() => {
  52. const modeType = state.modeType;
  53. return modeType === "practise";
  54. });
  55. /** 指法显示条件 */
  56. const fingeringDisplay = computed(() => {
  57. return state.fingeringInfo.name;
  58. });
  59. /** 返回 */
  60. const handleBack = () => {
  61. api_back();
  62. };
  63. return () => (
  64. <div ref={headRef} class={styles.headerTop}>
  65. <div class={styles.back} onClick={handleBack}>
  66. <img src={icons['icon-back']} />
  67. </div>
  68. <Title text={state.examSongName} rightView={false} />
  69. <div class={styles.headRight} style={{ display: headerData.modeMode ? "none" : "" }}>
  70. <div
  71. class={styles.btn}
  72. onClick={() => {
  73. handleRessetState();
  74. headerData.modeMode = true;
  75. }}
  76. >
  77. <img class={styles.iconBtn} src={icons.modelType} />
  78. <span>模式</span>
  79. </div>
  80. <div
  81. class={[styles.btn, disabledList.includes(state.modeType) && styles.disable]}
  82. id="tips-step-6"
  83. onClick={() => {
  84. state.playSource = state.playSource === "music" ? "background" : "music";
  85. }}
  86. >
  87. <img class={styles.iconBtn} src={state.playSource === "music" ? icons.music : icons.accompaniment} />
  88. <span>{state.playSource === "music" ? "原声" : "伴奏"}</span>
  89. </div>
  90. <div class={[styles.btn, disabledList.includes(state.modeType) && styles.disable]} id="tips-step-4" onClick={() => handleChangeSection()}>
  91. <img class={styles.iconBtn} src={state.section.length === 0 ? icons.section : state.section.length === 1 ? icons.section1 : icons.section2} />
  92. <span>选段</span>
  93. </div>
  94. <div
  95. style={{ display: fingeringDisplay.value ? "" : "none" }}
  96. class={[styles.btn, disabledList.includes(state.modeType) && styles.disable]}
  97. onClick={() => {
  98. state.setting.displayFingering = !state.setting.displayFingering;
  99. }}
  100. >
  101. <img class={styles.iconBtn} src={state.setting.displayFingering ? icons.fingeringOn : icons.fingeringOff} />
  102. <span>指法</span>
  103. </div>
  104. {/* <div
  105. class={[styles.btn]}
  106. onClick={async () => {
  107. metronomeData.lineShow = !metronomeData.lineShow;
  108. }}
  109. >
  110. <img class={styles.iconBtn} src={headImg("iconStep.png")} />
  111. <span>{metronomeData.lineShow ? "高级" : "初级"}</span>
  112. </div> */}
  113. {/* <div
  114. class={styles.btn}
  115. onClick={async () => {
  116. metronomeData.disable = !metronomeData.disable;
  117. metronomeData.metro?.initPlayer();
  118. }}
  119. >
  120. <img style={{ display: metronomeData.disable ? "block" : "none" }} class={styles.iconBtn} src={headImg("tickoff.png")} />
  121. <img style={{ display: !metronomeData.disable ? "block" : "none" }} class={styles.iconBtn} src={headImg("tickon.png")} />
  122. <span style={{ whiteSpace: "nowrap" }}>节拍器</span>
  123. </div> */}
  124. <Popover trigger="manual" v-model:show={headData.speedShow} placement="bottom" overlay={false}>
  125. {{
  126. reference: () => (
  127. <div
  128. id="tips-step-8"
  129. class={[styles.btn, disabledList.includes(state.modeType) && styles.disable]}
  130. onClick={(e: Event) => {
  131. e.stopPropagation();
  132. headData.speedShow = !headData.speedShow;
  133. }}
  134. >
  135. <Badge class={styles.badge} content={state.speed}>
  136. <img class={styles.iconBtn} src={icons.speed} />
  137. </Badge>
  138. <span>速度</span>
  139. </div>
  140. ),
  141. default: () => <Speed />,
  142. }}
  143. </Popover>
  144. <Popover trigger="manual" v-model:show={headData.musicTypeShow} placement="bottom-end" overlay={false}>
  145. {{
  146. reference: () => (
  147. <div
  148. class={[styles.btn, disabledList.includes(state.modeType) && styles.disable]}
  149. onClick={(e: Event) => {
  150. e.stopPropagation();
  151. headData.musicTypeShow = !headData.musicTypeShow;
  152. }}
  153. >
  154. <img class={styles.iconBtn} src={icons['icon-zhuanpu']} />
  155. <span>转简谱</span>
  156. </div>
  157. ),
  158. default: () => <MusicType />,
  159. }}
  160. </Popover>
  161. <div class={[styles.btn, disabledList.includes(state.modeType) && styles.disable]} onClick={() => (headerData.settingMode = true)}>
  162. <img class={styles.iconBtn} src={icons.setting} />
  163. <span>设置</span>
  164. </div>
  165. <div
  166. style={{ display: playBtnDisplay.value ? "" : "none" }}
  167. class={[styles.btn, styles.playBtn, disabledList.includes(state.modeType) && styles.disable]}
  168. id="tips-step-5"
  169. onClick={() => togglePlay()}
  170. >
  171. <div class={styles.btnWrap}>
  172. <img class={styles.iconBtn} src={state.playState === "paused" ? icons.play : icons.pause} />
  173. <Circle
  174. class={styles.progress}
  175. stroke-width={80}
  176. currentRate={state.playProgress}
  177. rate={100}
  178. color="#FFC830"
  179. />
  180. </div>
  181. </div>
  182. <div
  183. style={{ display: resetBtnDisplay.value ? "" : "none" }}
  184. class={[styles.btn, styles.resetBtn, disabledList.includes(state.modeType) && styles.disable]}
  185. id="tips-step-7"
  186. onClick={() => handleResetPlay()}
  187. >
  188. <img class={styles.iconBtn} src={icons['reset']} />
  189. </div>
  190. </div>
  191. <Popup teleport="body" defaultStyle={false} v-model:show={headerData.settingMode}>
  192. <Settting onClose={() => (headerData.settingMode = false)} />
  193. </Popup>
  194. <Popup teleport="body" position="bottom" closeOnClickOverlay={false} overlay={false} defaultStyle={false} v-model:show={headerData.modeMode}>
  195. <ModeTypeMode onClose={(value) => handleChangeModeType(value)} />
  196. </Popup>
  197. </div>
  198. );
  199. },
  200. });