1- import { computed , defineComponent , onMounted , ref } from "vue" ;
2- import { Button , addToast } from "@munet/ui" ;
1+ import { computed , defineComponent , onMounted , ref , watch } from "vue" ;
2+ import { Button , NumberInput , addToast } from "@munet/ui" ;
33import WaveSurfer from "wavesurfer.js" ;
44import { globalCapture , selectedADir , selectMusicId } from "@/store/refs" ;
55import ZoomPlugin from 'wavesurfer.js/dist/plugins/zoom'
@@ -15,7 +15,7 @@ export default defineComponent({
1515 props : {
1616 closeModel : { type : Function , required : true }
1717 } ,
18- setup ( props , { emit} ) {
18+ setup ( props , { emit, expose } ) {
1919 const waveSurferContainer = ref ( )
2020 const region = ref < Region > ( )
2121 const ws = ref < WaveSurfer > ( )
@@ -24,6 +24,42 @@ export default defineComponent({
2424 const load = ref ( false )
2525 const dataLoad = ref ( true )
2626 const { ctrl, shift} = useMagicKeys ( )
27+ const startTime = ref ( 0 )
28+ const endTime = ref ( 0 )
29+ const duration = ref ( 0 )
30+ // 标记是否正在由输入框触发 region 更新,防止循环
31+ const updatingFromInput = ref ( false )
32+
33+ const syncTimesFromRegion = ( ) => {
34+ if ( region . value && ! updatingFromInput . value ) {
35+ startTime . value = region . value . start
36+ endTime . value = region . value . end
37+ }
38+ }
39+
40+ const onStartTimeChange = ( ) => {
41+ if ( ! region . value ) return
42+ if ( startTime . value >= endTime . value ) {
43+ addToast ( { message : t ( 'music.edit.audioPreviewStartGtEnd' ) , type : 'warning' } )
44+ startTime . value = region . value . start
45+ return
46+ }
47+ updatingFromInput . value = true
48+ region . value . setOptions ( { start : startTime . value } )
49+ updatingFromInput . value = false
50+ }
51+
52+ const onEndTimeChange = ( ) => {
53+ if ( ! region . value ) return
54+ if ( endTime . value <= startTime . value ) {
55+ addToast ( { message : t ( 'music.edit.audioPreviewEndLtStart' ) , type : 'warning' } )
56+ endTime . value = region . value . end
57+ return
58+ }
59+ updatingFromInput . value = true
60+ region . value . setOptions ( { end : endTime . value , start : region . value . start } )
61+ updatingFromInput . value = false
62+ }
2763
2864
2965 onMounted ( async ( ) => {
@@ -57,15 +93,20 @@ export default defineComponent({
5793 ] ,
5894 } )
5995
60- ws . value . on ( 'decode' , ( duration ) => {
96+ ws . value . on ( 'decode' , ( dur ) => {
97+ duration . value = dur
6198 // Regions
6299 region . value = regions . addRegion ( {
63100 start : savedRegion ! . startTime ! >= 0 ? savedRegion . startTime ! : 0 ,
64- end : savedRegion ! . endTime ! >= 0 ? savedRegion . endTime ! : duration ,
101+ end : savedRegion ! . endTime ! >= 0 ? savedRegion . endTime ! : dur ,
65102 drag : true ,
66103 resize : true ,
67104 id : 'selection' ,
68105 } )
106+ syncTimesFromRegion ( )
107+
108+ region . value . on ( 'update' , syncTimesFromRegion )
109+ region . value . on ( 'update-end' , syncTimesFromRegion )
69110 } )
70111
71112 ws . value . on ( 'click' , ( e ) => {
@@ -76,12 +117,14 @@ export default defineComponent({
76117 return
77118 }
78119 region . value ! . setOptions ( { start : time } )
120+ syncTimesFromRegion ( )
79121 } else if ( shift . value ) {
80122 if ( time <= region . value ! . start ) {
81123 addToast ( { message : t ( 'music.edit.audioPreviewEndLtStart' ) , type : 'warning' } )
82124 return
83125 }
84126 region . value ! . setOptions ( { end : time , start : region . value ! . start } )
127+ syncTimesFromRegion ( )
85128 }
86129 } )
87130
@@ -110,6 +153,10 @@ export default defineComponent({
110153
111154 const playIcon = computed ( ( ) => isPlaying . value ? 'i-mdi-pause' : 'i-mdi-play' )
112155
156+ expose ( {
157+ save, load
158+ } )
159+
113160 return ( ) => < div class = "relative" >
114161 { dataLoad . value && < div class = "absolute inset-0 flex items-center justify-center bg-black/10 z-10" > < div class = "i-mdi-loading animate-spin text-2xl" /> </ div > }
115162 < div class = "flex flex-col gap-3" >
@@ -133,13 +180,15 @@ export default defineComponent({
133180 { t ( 'music.edit.audioPreviewSelectRegion' ) }
134181 </ Button >
135182 </ div >
136- < div class = "flex gap-2 justify-end" >
137- < Button variant = "secondary" danger onClick = { props . closeModel as any } disabled = { load . value } >
138- { t ( 'common.dismiss' ) }
139- </ Button >
140- < Button variant = "secondary" onClick = { save } ing = { load . value } >
141- { t ( 'common.save' ) }
142- </ Button >
183+ < div class = "flex gap-4 items-center" >
184+ < div class = "flex flex-col gap-1 w-0 grow" >
185+ < div class = "ml-1 text-sm" > { t ( 'music.edit.audioPreviewStart' ) } </ div >
186+ < NumberInput v-model :value = { startTime . value } min = { 0 } max = { duration . value } step = { 0.001 } decimal = { 3 } onChange = { onStartTimeChange } />
187+ </ div >
188+ < div class = "flex flex-col gap-1 w-0 grow" >
189+ < div class = "ml-1 text-sm" > { t ( 'music.edit.audioPreviewEnd' ) } </ div >
190+ < NumberInput v-model :value = { endTime . value } min = { 0 } max = { duration . value } step = { 0.001 } decimal = { 3 } onChange = { onEndTimeChange } />
191+ </ div >
143192 </ div >
144193 </ div >
145194 </ div > ;
0 commit comments