|  | @@ -54,15 +54,13 @@
 | 
	
		
			
				|  |  |        <el-form-item
 | 
	
		
			
				|  |  |          prop="sysMusicScore.isOpenMetronome"
 | 
	
		
			
				|  |  |          label="节拍器"
 | 
	
		
			
				|  |  | -        :rules="[{required: true, message: '请选择节拍器'}]"
 | 
	
		
			
				|  |  | +        :rules="[{ required: true, message: '请选择节拍器' }]"
 | 
	
		
			
				|  |  |        >
 | 
	
		
			
				|  |  |          <template slot="label">
 | 
	
		
			
				|  |  |            <span>
 | 
	
		
			
				|  |  |              节拍器
 | 
	
		
			
				|  |  |              <el-tooltip placement="top" popper-class="mTooltip">
 | 
	
		
			
				|  |  | -              <div slot="content">
 | 
	
		
			
				|  |  | -                是否播放系统自带节拍器
 | 
	
		
			
				|  |  | -              </div>
 | 
	
		
			
				|  |  | +              <div slot="content">是否播放系统自带节拍器</div>
 | 
	
		
			
				|  |  |                <i
 | 
	
		
			
				|  |  |                  class="el-icon-question"
 | 
	
		
			
				|  |  |                  style="font-size: 18px; color: #f56c6c"
 | 
	
	
		
			
				|  | @@ -81,26 +79,16 @@
 | 
	
		
			
				|  |  |              v-for="item in memberRankList"
 | 
	
		
			
				|  |  |              :key="item.id"
 | 
	
		
			
				|  |  |            ></el-option> -->
 | 
	
		
			
				|  |  | -          <el-option
 | 
	
		
			
				|  |  | -            :value="0"
 | 
	
		
			
				|  |  | -            label="不播放"
 | 
	
		
			
				|  |  | -          ></el-option>
 | 
	
		
			
				|  |  | -          <el-option
 | 
	
		
			
				|  |  | -            :value="1"
 | 
	
		
			
				|  |  | -            label="播放"
 | 
	
		
			
				|  |  | -          ></el-option>
 | 
	
		
			
				|  |  | +          <el-option :value="0" label="不播放"></el-option>
 | 
	
		
			
				|  |  | +          <el-option :value="1" label="播放"></el-option>
 | 
	
		
			
				|  |  |          </el-select>
 | 
	
		
			
				|  |  |        </el-form-item>
 | 
	
		
			
				|  |  | -      <el-form-item
 | 
	
		
			
				|  |  | -        label="重复节拍时长"
 | 
	
		
			
				|  |  | -      >
 | 
	
		
			
				|  |  | +      <el-form-item label="重复节拍时长">
 | 
	
		
			
				|  |  |          <template slot="label">
 | 
	
		
			
				|  |  |            <span>
 | 
	
		
			
				|  |  |              重复节拍时长
 | 
	
		
			
				|  |  |              <el-tooltip placement="top" popper-class="mTooltip">
 | 
	
		
			
				|  |  | -              <div slot="content">
 | 
	
		
			
				|  |  | -                2/4拍类似的节拍器是否重复时长
 | 
	
		
			
				|  |  | -              </div>
 | 
	
		
			
				|  |  | +              <div slot="content">2/4拍类似的节拍器是否重复时长</div>
 | 
	
		
			
				|  |  |                <i
 | 
	
		
			
				|  |  |                  class="el-icon-question"
 | 
	
		
			
				|  |  |                  style="font-size: 18px; color: #f56c6c"
 | 
	
	
		
			
				|  | @@ -113,14 +101,8 @@
 | 
	
		
			
				|  |  |            v-model="form.repeatedBeats"
 | 
	
		
			
				|  |  |            placeholder="请选择是否重复节拍器时长"
 | 
	
		
			
				|  |  |          >
 | 
	
		
			
				|  |  | -          <el-option
 | 
	
		
			
				|  |  | -            :value="0"
 | 
	
		
			
				|  |  | -            label="不重复"
 | 
	
		
			
				|  |  | -          ></el-option>
 | 
	
		
			
				|  |  | -          <el-option
 | 
	
		
			
				|  |  | -            :value="1"
 | 
	
		
			
				|  |  | -            label="重复"
 | 
	
		
			
				|  |  | -          ></el-option>
 | 
	
		
			
				|  |  | +          <el-option :value="0" label="不重复"></el-option>
 | 
	
		
			
				|  |  | +          <el-option :value="1" label="重复"></el-option>
 | 
	
		
			
				|  |  |          </el-select>
 | 
	
		
			
				|  |  |        </el-form-item>
 | 
	
		
			
				|  |  |        <!-- <el-form-item
 | 
	
	
		
			
				|  | @@ -175,19 +157,77 @@
 | 
	
		
			
				|  |  |            <el-radio :label="0">否</el-radio>
 | 
	
		
			
				|  |  |          </el-radio-group>
 | 
	
		
			
				|  |  |        </el-form-item>
 | 
	
		
			
				|  |  | +      <el-form-item :prop="`sysMusicScore.subjectId`" label="声部">
 | 
	
		
			
				|  |  | +        <!-- :rules="[{required: true, message: '请选择声部'}]" -->
 | 
	
		
			
				|  |  | +        <el-select
 | 
	
		
			
				|  |  | +          style="width: 100% !important"
 | 
	
		
			
				|  |  | +          v-model="form.sysMusicScore.subjectId"
 | 
	
		
			
				|  |  | +          clearable
 | 
	
		
			
				|  |  | +          placeholder="请选择声部"
 | 
	
		
			
				|  |  | +        >
 | 
	
		
			
				|  |  | +          <el-option
 | 
	
		
			
				|  |  | +            v-for="item in selects.subjects"
 | 
	
		
			
				|  |  | +            :value="item.id"
 | 
	
		
			
				|  |  | +            :label="item.name"
 | 
	
		
			
				|  |  | +            :key="item.id"
 | 
	
		
			
				|  |  | +            :disabled="hasSubjectId(item.id)"
 | 
	
		
			
				|  |  | +          ></el-option>
 | 
	
		
			
				|  |  | +        </el-select>
 | 
	
		
			
				|  |  | +      </el-form-item>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      <el-form-item
 | 
	
		
			
				|  |  | +        :prop="`sysMusicScore.isShowFingering`"
 | 
	
		
			
				|  |  | +        label="指法展示"
 | 
	
		
			
				|  |  | +        :rules="[{ required: true, message: '请选择是否展示指法' }]"
 | 
	
		
			
				|  |  | +      >
 | 
	
		
			
				|  |  | +        <el-select
 | 
	
		
			
				|  |  | +          style="width: 100% !important"
 | 
	
		
			
				|  |  | +          v-model="form.sysMusicScore.isShowFingering"
 | 
	
		
			
				|  |  | +          placeholder="请选择是否展示指法"
 | 
	
		
			
				|  |  | +        >
 | 
	
		
			
				|  |  | +          <el-option :value="true" label="是"></el-option>
 | 
	
		
			
				|  |  | +          <el-option :value="false" label="否"></el-option>
 | 
	
		
			
				|  |  | +        </el-select>
 | 
	
		
			
				|  |  | +      </el-form-item>
 | 
	
		
			
				|  |  |        <el-form-item
 | 
	
		
			
				|  |  |          prop="sysMusicScore.order"
 | 
	
		
			
				|  |  |          label="排序"
 | 
	
		
			
				|  |  | -        :rules="[{ required: true, message: '请输入排序' }, {
 | 
	
		
			
				|  |  | -              pattern: /^([1-9]\d*|[0]{1,1})$/,
 | 
	
		
			
				|  |  | -              message: '请输入正确的排序',
 | 
	
		
			
				|  |  | -              trigger: 'blur',
 | 
	
		
			
				|  |  | -            },]"
 | 
	
		
			
				|  |  | +        :rules="[
 | 
	
		
			
				|  |  | +          { required: true, message: '请输入排序' },
 | 
	
		
			
				|  |  | +          {
 | 
	
		
			
				|  |  | +            pattern: /^([1-9]\d*|[0]{1,1})$/,
 | 
	
		
			
				|  |  | +            message: '请输入正确的排序',
 | 
	
		
			
				|  |  | +            trigger: 'blur',
 | 
	
		
			
				|  |  | +          },
 | 
	
		
			
				|  |  | +        ]"
 | 
	
		
			
				|  |  |        >
 | 
	
		
			
				|  |  |          <el-input placeholder="请输入排序" v-model="form.sysMusicScore.order" />
 | 
	
		
			
				|  |  |        </el-form-item>
 | 
	
		
			
				|  |  |        <el-form-item
 | 
	
		
			
				|  |  | -      v-if="form.sysMusicScore.isOpenMetronome ===1"
 | 
	
		
			
				|  |  | +        label="MusicXML"
 | 
	
		
			
				|  |  | +        :prop="`sysMusicScore.xmlUrl`"
 | 
	
		
			
				|  |  | +        :rules="[{ required: true, message: '请选择MusicXML文件' }]"
 | 
	
		
			
				|  |  | +      >
 | 
	
		
			
				|  |  | +        <singe-file-upload
 | 
	
		
			
				|  |  | +          tips="仅支持上传 xml 格式文件"
 | 
	
		
			
				|  |  | +          accept=".xml"
 | 
	
		
			
				|  |  | +          v-model="form.sysMusicScore.xmlUrl"
 | 
	
		
			
				|  |  | +          @inputFile="inputFile"
 | 
	
		
			
				|  |  | +          bucket_name="cloud-coach"
 | 
	
		
			
				|  |  | +        />
 | 
	
		
			
				|  |  | +      </el-form-item>
 | 
	
		
			
				|  |  | +      <el-form-item
 | 
	
		
			
				|  |  | +        prop="sysMusicScore.speed"
 | 
	
		
			
				|  |  | +        label="速度"
 | 
	
		
			
				|  |  | +        :rules="[{ required: true, message: '请输入速度' }]"
 | 
	
		
			
				|  |  | +      >
 | 
	
		
			
				|  |  | +        <el-input
 | 
	
		
			
				|  |  | +          placeholder="请输入速度"
 | 
	
		
			
				|  |  | +          v-model="form.sysMusicScore.speed"
 | 
	
		
			
				|  |  | +        />
 | 
	
		
			
				|  |  | +      </el-form-item>
 | 
	
		
			
				|  |  | +      <el-form-item
 | 
	
		
			
				|  |  | +        v-if="form.sysMusicScore.isOpenMetronome === 1"
 | 
	
		
			
				|  |  |          label="伴奏(不含节拍器)"
 | 
	
		
			
				|  |  |          prop="sysMusicScore.url"
 | 
	
		
			
				|  |  |        >
 | 
	
	
		
			
				|  | @@ -199,7 +239,7 @@
 | 
	
		
			
				|  |  |          />
 | 
	
		
			
				|  |  |        </el-form-item>
 | 
	
		
			
				|  |  |        <el-form-item
 | 
	
		
			
				|  |  | -      v-else
 | 
	
		
			
				|  |  | +        v-else
 | 
	
		
			
				|  |  |          label="伴奏(含节拍器)"
 | 
	
		
			
				|  |  |          prop="sysMusicScore.metronomeUrl"
 | 
	
		
			
				|  |  |        >
 | 
	
	
		
			
				|  | @@ -207,45 +247,58 @@
 | 
	
		
			
				|  |  |            tips="仅支持上传 mp3/aac 格式音频文件"
 | 
	
		
			
				|  |  |            accept=".mp3, .aac"
 | 
	
		
			
				|  |  |            v-model="form.sysMusicScore.metronomeUrl"
 | 
	
		
			
				|  |  | -            bucket_name="cloud-coach"
 | 
	
		
			
				|  |  | +          bucket_name="cloud-coach"
 | 
	
		
			
				|  |  |          />
 | 
	
		
			
				|  |  |        </el-form-item>
 | 
	
		
			
				|  |  | -      <el-form-item
 | 
	
		
			
				|  |  | -        label="MIDI"
 | 
	
		
			
				|  |  | -        prop="sysMusicScore.midiUrl"
 | 
	
		
			
				|  |  | -      >
 | 
	
		
			
				|  |  | +      <el-form-item label="MIDI" prop="sysMusicScore.midiUrl">
 | 
	
		
			
				|  |  |          <singe-file-upload
 | 
	
		
			
				|  |  |            tips="仅支持上传 mid 格式音频文件"
 | 
	
		
			
				|  |  |            accept=".mid"
 | 
	
		
			
				|  |  |            v-model="form.sysMusicScore.midiUrl"
 | 
	
		
			
				|  |  | -            bucket_name="cloud-coach"
 | 
	
		
			
				|  |  | +          bucket_name="cloud-coach"
 | 
	
		
			
				|  |  |          />
 | 
	
		
			
				|  |  |        </el-form-item>
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |        <div v-if="gradual && gradual.length">
 | 
	
		
			
				|  |  | -        <el-alert :closable="false" style="margin-bottom: 20px;">识别到共{{gradual.length}}处渐变速度,请输入Dorico对应小节时间信息</el-alert>
 | 
	
		
			
				|  |  | -        <div v-for="item in gradual">
 | 
	
		
			
				|  |  | +        <el-alert :closable="false" style="margin-bottom: 20px"
 | 
	
		
			
				|  |  | +          >识别到共{{
 | 
	
		
			
				|  |  | +            gradual.length
 | 
	
		
			
				|  |  | +          }}处渐变速度,请输入Dorico对应小节时间信息</el-alert
 | 
	
		
			
				|  |  | +        >
 | 
	
		
			
				|  |  | +        <div v-for="(item,index) in gradual" :key="index">
 | 
	
		
			
				|  |  |            <el-form-item
 | 
	
		
			
				|  |  |              :label="item[0].measureIndex + 2 + ' 小节'"
 | 
	
		
			
				|  |  | -            :rules="[{required: true, message: '请输入合奏曲目时间'}, {
 | 
	
		
			
				|  |  | -              pattern: /^((\d{2}):?){2,3}$/,
 | 
	
		
			
				|  |  | -              message: '请输入正确的曲目时间',
 | 
	
		
			
				|  |  | -              trigger: 'blur',
 | 
	
		
			
				|  |  | -            }]"
 | 
	
		
			
				|  |  | +            :rules="[
 | 
	
		
			
				|  |  | +              { required: true, message: '请输入合奏曲目时间' },
 | 
	
		
			
				|  |  | +              {
 | 
	
		
			
				|  |  | +                pattern: /^((\d{2}):?){2,3}$/,
 | 
	
		
			
				|  |  | +                message: '请输入正确的曲目时间',
 | 
	
		
			
				|  |  | +                trigger: 'blur',
 | 
	
		
			
				|  |  | +              },
 | 
	
		
			
				|  |  | +            ]"
 | 
	
		
			
				|  |  |              :prop="`graduals.${item[0].measureIndex}`"
 | 
	
		
			
				|  |  |            >
 | 
	
		
			
				|  |  | -            <el-input placeholder="00:00:00" v-model="form.graduals[item[0].measureIndex]"></el-input>
 | 
	
		
			
				|  |  | +            <el-input
 | 
	
		
			
				|  |  | +              placeholder="00:00:00"
 | 
	
		
			
				|  |  | +              v-model="form.graduals[item[0].measureIndex]"
 | 
	
		
			
				|  |  | +            ></el-input>
 | 
	
		
			
				|  |  |            </el-form-item>
 | 
	
		
			
				|  |  |            <el-form-item
 | 
	
		
			
				|  |  |              :label="item[1].measureIndex + 2 + ' 小节'"
 | 
	
		
			
				|  |  | -            :rules="[{required: true, message: '请输入合奏曲目时间'}, {
 | 
	
		
			
				|  |  | -              pattern: /^((\d{2}):?){2,3}$/,
 | 
	
		
			
				|  |  | -              message: '请输入正确的曲目时间',
 | 
	
		
			
				|  |  | -              trigger: 'blur',
 | 
	
		
			
				|  |  | -            }]"
 | 
	
		
			
				|  |  | +            :rules="[
 | 
	
		
			
				|  |  | +              { required: true, message: '请输入合奏曲目时间' },
 | 
	
		
			
				|  |  | +              {
 | 
	
		
			
				|  |  | +                pattern: /^((\d{2}):?){2,3}$/,
 | 
	
		
			
				|  |  | +                message: '请输入正确的曲目时间',
 | 
	
		
			
				|  |  | +                trigger: 'blur',
 | 
	
		
			
				|  |  | +              },
 | 
	
		
			
				|  |  | +            ]"
 | 
	
		
			
				|  |  |              :prop="`graduals.${item[1].measureIndex}`"
 | 
	
		
			
				|  |  |            >
 | 
	
		
			
				|  |  | -            <el-input placeholder="00:00:00" v-model="form.graduals[item[1].measureIndex]"></el-input>
 | 
	
		
			
				|  |  | +            <el-input
 | 
	
		
			
				|  |  | +              placeholder="00:00:00"
 | 
	
		
			
				|  |  | +              v-model="form.graduals[item[1].measureIndex]"
 | 
	
		
			
				|  |  | +            ></el-input>
 | 
	
		
			
				|  |  |            </el-form-item>
 | 
	
		
			
				|  |  |          </div>
 | 
	
		
			
				|  |  |        </div>
 | 
	
	
		
			
				|  | @@ -256,73 +309,44 @@
 | 
	
		
			
				|  |  |          :key="index"
 | 
	
		
			
				|  |  |        >
 | 
	
		
			
				|  |  |          <el-row>
 | 
	
		
			
				|  |  | -          <el-col :span="12">
 | 
	
		
			
				|  |  | +          <!-- <el-col :span="12">
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +          </el-col> -->
 | 
	
		
			
				|  |  | +          <!-- <el-col :span="12">
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +          </el-col> -->
 | 
	
		
			
				|  |  | +          <el-col :span="12" v-if="partListNames.length > 1">
 | 
	
		
			
				|  |  |              <el-form-item
 | 
	
		
			
				|  |  | -              :prop="`sysMusicScoreAccompaniments.${index}.subjectId`"
 | 
	
		
			
				|  |  | -              label="声部"
 | 
	
		
			
				|  |  | +              label="所属轨道"
 | 
	
		
			
				|  |  | +              :prop="`sysMusicScoreAccompaniments.${index}.track`"
 | 
	
		
			
				|  |  | +              :rules="[{ required: true, message: '请选择所属轨道' }]"
 | 
	
		
			
				|  |  |              >
 | 
	
		
			
				|  |  | -              <!-- :rules="[{required: true, message: '请选择声部'}]" -->
 | 
	
		
			
				|  |  |                <el-select
 | 
	
		
			
				|  |  |                  style="width: 100% !important"
 | 
	
		
			
				|  |  | -                v-model="song.subjectId"
 | 
	
		
			
				|  |  | +                v-model="song.track"
 | 
	
		
			
				|  |  |                  clearable
 | 
	
		
			
				|  |  | -                placeholder="请选择声部"
 | 
	
		
			
				|  |  | +                placeholder="请选择轨道"
 | 
	
		
			
				|  |  |                >
 | 
	
		
			
				|  |  |                  <el-option
 | 
	
		
			
				|  |  | -                  v-for="item in selects.subjects"
 | 
	
		
			
				|  |  | -                  :value="item.id"
 | 
	
		
			
				|  |  | -                  :label="item.name"
 | 
	
		
			
				|  |  | -                  :key="item.id"
 | 
	
		
			
				|  |  | -                  :disabled="hasSubjectId(item.id)"
 | 
	
		
			
				|  |  | +                  v-for="item in partListNames"
 | 
	
		
			
				|  |  | +                  :value="item"
 | 
	
		
			
				|  |  | +                  :label="item"
 | 
	
		
			
				|  |  | +                  :key="item"
 | 
	
		
			
				|  |  | +                  :disabled="hasPartName(item)"
 | 
	
		
			
				|  |  |                  ></el-option>
 | 
	
		
			
				|  |  |                </el-select>
 | 
	
		
			
				|  |  |              </el-form-item>
 | 
	
		
			
				|  |  |            </el-col>
 | 
	
		
			
				|  |  | -          <el-col :span="12">
 | 
	
		
			
				|  |  | -            <el-form-item
 | 
	
		
			
				|  |  | -              :prop="`sysMusicScoreAccompaniments.${index}.speed`"
 | 
	
		
			
				|  |  | -              label="速度"
 | 
	
		
			
				|  |  | -              :rules="[{ required: true, message: '请输入速度' }]"
 | 
	
		
			
				|  |  | -            >
 | 
	
		
			
				|  |  | -              <el-input
 | 
	
		
			
				|  |  | -                type="number"
 | 
	
		
			
				|  |  | -                placeholder="请输入速度"
 | 
	
		
			
				|  |  | -                v-model="song.speed"
 | 
	
		
			
				|  |  | -              />
 | 
	
		
			
				|  |  | -            </el-form-item>
 | 
	
		
			
				|  |  | -          </el-col>
 | 
	
		
			
				|  |  | -          <el-col :span="12">
 | 
	
		
			
				|  |  | -            <el-form-item
 | 
	
		
			
				|  |  | -              :prop="`sysMusicScoreAccompaniments.${index}.isShowFingering`"
 | 
	
		
			
				|  |  | -              label="指法展示"
 | 
	
		
			
				|  |  | -              :rules="[{ required: true, message: '请选择是否展示指法' }]"
 | 
	
		
			
				|  |  | -            >
 | 
	
		
			
				|  |  | -              <el-select
 | 
	
		
			
				|  |  | -                style="width: 100% !important"
 | 
	
		
			
				|  |  | -                v-model="song.isShowFingering"
 | 
	
		
			
				|  |  | -                placeholder="请选择是否展示指法"
 | 
	
		
			
				|  |  | -              >
 | 
	
		
			
				|  |  | -                <el-option :value="true" label="是"></el-option>
 | 
	
		
			
				|  |  | -                <el-option :value="false" label="否"></el-option>
 | 
	
		
			
				|  |  | -              </el-select>
 | 
	
		
			
				|  |  | -            </el-form-item>
 | 
	
		
			
				|  |  | -          </el-col>
 | 
	
		
			
				|  |  | -          <el-col :span="12">
 | 
	
		
			
				|  |  | -            <el-form-item
 | 
	
		
			
				|  |  | -              :prop="`sysMusicScoreAccompaniments.${index}.memo`"
 | 
	
		
			
				|  |  | -              label="描述"
 | 
	
		
			
				|  |  | -            >
 | 
	
		
			
				|  |  | -              <el-input placeholder="请输入描述" v-model="song.memo" />
 | 
	
		
			
				|  |  | -            </el-form-item>
 | 
	
		
			
				|  |  | -          </el-col>
 | 
	
		
			
				|  |  | -          <el-col :span="12"  v-if="form.sysMusicScore.isOpenMetronome ===1">
 | 
	
		
			
				|  |  | -            <el-form-item
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +          <el-col :span="12" v-if="form.sysMusicScore.isOpenMetronome === 1">
 | 
	
		
			
				|  |  | +            <el-form-item
 | 
	
		
			
				|  |  |                label="原音(不含节拍器)"
 | 
	
		
			
				|  |  |                :prop="`sysMusicScoreAccompaniments.${index}.mp3Url`"
 | 
	
		
			
				|  |  |                :rules="[
 | 
	
		
			
				|  |  |                  {
 | 
	
		
			
				|  |  | -                  required: form.sysMusicScore.isOpenMetronome ? form.sysMusicScore.playMode === 'MP3' : false,
 | 
	
		
			
				|  |  | +                  required: form.sysMusicScore.isOpenMetronome
 | 
	
		
			
				|  |  | +                    ? form.sysMusicScore.playMode === 'MP3'
 | 
	
		
			
				|  |  | +                    : false,
 | 
	
		
			
				|  |  |                    message: '请上传原音(不含节拍器)',
 | 
	
		
			
				|  |  |                  },
 | 
	
		
			
				|  |  |                ]"
 | 
	
	
		
			
				|  | @@ -331,7 +355,7 @@
 | 
	
		
			
				|  |  |                  tips="仅支持上传 mp3/aac 格式音频文件"
 | 
	
		
			
				|  |  |                  accept=".mp3, .aac"
 | 
	
		
			
				|  |  |                  v-model="song.mp3Url"
 | 
	
		
			
				|  |  | -                  bucket_name="cloud-coach"
 | 
	
		
			
				|  |  | +                bucket_name="cloud-coach"
 | 
	
		
			
				|  |  |                />
 | 
	
		
			
				|  |  |              </el-form-item>
 | 
	
		
			
				|  |  |            </el-col>
 | 
	
	
		
			
				|  | @@ -341,7 +365,9 @@
 | 
	
		
			
				|  |  |                :prop="`sysMusicScoreAccompaniments.${index}.metronomeMp3Url`"
 | 
	
		
			
				|  |  |                :rules="[
 | 
	
		
			
				|  |  |                  {
 | 
	
		
			
				|  |  | -                  required: form.sysMusicScore.isOpenMetronome ? false : form.sysMusicScore.playMode === 'MP3',
 | 
	
		
			
				|  |  | +                  required: form.sysMusicScore.isOpenMetronome
 | 
	
		
			
				|  |  | +                    ? false
 | 
	
		
			
				|  |  | +                    : form.sysMusicScore.playMode === 'MP3',
 | 
	
		
			
				|  |  |                    message: '原音(含节拍器)',
 | 
	
		
			
				|  |  |                  },
 | 
	
		
			
				|  |  |                ]"
 | 
	
	
		
			
				|  | @@ -350,49 +376,28 @@
 | 
	
		
			
				|  |  |                  tips="仅支持上传 mp3/aac 格式音频文件"
 | 
	
		
			
				|  |  |                  accept=".mp3, .aac"
 | 
	
		
			
				|  |  |                  v-model="song.metronomeMp3Url"
 | 
	
		
			
				|  |  | -                  bucket_name="cloud-coach"
 | 
	
		
			
				|  |  | +                bucket_name="cloud-coach"
 | 
	
		
			
				|  |  |                />
 | 
	
		
			
				|  |  |              </el-form-item>
 | 
	
		
			
				|  |  |            </el-col>
 | 
	
		
			
				|  |  |          </el-row>
 | 
	
		
			
				|  |  |          <el-row>
 | 
	
		
			
				|  |  | -          <el-col :span="12">
 | 
	
		
			
				|  |  | +          <el-col :span="24">
 | 
	
		
			
				|  |  |              <el-form-item
 | 
	
		
			
				|  |  | -              label="MusicXML"
 | 
	
		
			
				|  |  | -              :prop="`sysMusicScoreAccompaniments.${index}.xmlUrl`"
 | 
	
		
			
				|  |  | -              :rules="[{ required: true, message: '请选择MusicXML文件' }]"
 | 
	
		
			
				|  |  | +              :prop="`sysMusicScoreAccompaniments.${index}.memo`"
 | 
	
		
			
				|  |  | +              label="描述"
 | 
	
		
			
				|  |  |              >
 | 
	
		
			
				|  |  | -              <singe-file-upload
 | 
	
		
			
				|  |  | -                tips="仅支持上传 xml 格式文件"
 | 
	
		
			
				|  |  | -                accept=".xml"
 | 
	
		
			
				|  |  | -                v-model="song.xmlUrl"
 | 
	
		
			
				|  |  | -                @inputFile="inputFile"
 | 
	
		
			
				|  |  | -                  bucket_name="cloud-coach"
 | 
	
		
			
				|  |  | +              <el-input
 | 
	
		
			
				|  |  | +                type="textarea"
 | 
	
		
			
				|  |  | +                :rows="2"
 | 
	
		
			
				|  |  | +                placeholder="请输入描述"
 | 
	
		
			
				|  |  | +                v-model="song.memo"
 | 
	
		
			
				|  |  |                />
 | 
	
		
			
				|  |  |              </el-form-item>
 | 
	
		
			
				|  |  |            </el-col>
 | 
	
		
			
				|  |  | -          <el-col :span="12" v-if="partListNames.length > 1">
 | 
	
		
			
				|  |  | -            <el-form-item
 | 
	
		
			
				|  |  | -              label="所属轨道"
 | 
	
		
			
				|  |  | -              :prop="`sysMusicScoreAccompaniments.${index}.track`"
 | 
	
		
			
				|  |  | -              :rules="[{ required: true, message: '请选择所属轨道' }]"
 | 
	
		
			
				|  |  | -            >
 | 
	
		
			
				|  |  | -              <el-select
 | 
	
		
			
				|  |  | -                style="width: 100% !important"
 | 
	
		
			
				|  |  | -                v-model="song.track"
 | 
	
		
			
				|  |  | -                clearable
 | 
	
		
			
				|  |  | -                placeholder="请选择轨道"
 | 
	
		
			
				|  |  | -              >
 | 
	
		
			
				|  |  | -                <el-option
 | 
	
		
			
				|  |  | -                  v-for="item in partListNames"
 | 
	
		
			
				|  |  | -                  :value="item"
 | 
	
		
			
				|  |  | -                  :label="item"
 | 
	
		
			
				|  |  | -                  :key="item"
 | 
	
		
			
				|  |  | -                  :disabled="hasPartName(item)"
 | 
	
		
			
				|  |  | -                ></el-option>
 | 
	
		
			
				|  |  | -              </el-select>
 | 
	
		
			
				|  |  | -            </el-form-item>
 | 
	
		
			
				|  |  | -          </el-col>
 | 
	
		
			
				|  |  | +          <!-- <el-col :span="12">
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +          </el-col> -->
 | 
	
		
			
				|  |  |          </el-row>
 | 
	
		
			
				|  |  |          <el-button
 | 
	
		
			
				|  |  |            class="file-remove"
 | 
	
	
		
			
				|  | @@ -417,18 +422,18 @@
 | 
	
		
			
				|  |  |    </div>
 | 
	
		
			
				|  |  |  </template>
 | 
	
		
			
				|  |  |  <script>
 | 
	
		
			
				|  |  | -import axios from 'axios'
 | 
	
		
			
				|  |  | +import axios from "axios";
 | 
	
		
			
				|  |  |  import { Add, Update, queryPageSysExam, queryTree } from "../api";
 | 
	
		
			
				|  |  |  import { getAllmemberRank } from "@/views/resetTeaming/api";
 | 
	
		
			
				|  |  |  const initailExtConfigJson = {
 | 
	
		
			
				|  |  | -  repeatedBeats: 0
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | +  repeatedBeats: 0,
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  export default {
 | 
	
		
			
				|  |  |    props: ["detail", "type"],
 | 
	
		
			
				|  |  |    data() {
 | 
	
		
			
				|  |  |      return {
 | 
	
		
			
				|  |  |        gradual: null,
 | 
	
		
			
				|  |  | -      xmlFirstSpeed: '',
 | 
	
		
			
				|  |  | +      xmlFirstSpeed: "",
 | 
	
		
			
				|  |  |        partListNames: [],
 | 
	
		
			
				|  |  |        tree: [],
 | 
	
		
			
				|  |  |        extConfigJson: {},
 | 
	
	
		
			
				|  | @@ -451,7 +456,11 @@ export default {
 | 
	
		
			
				|  |  |            renderFrom: "",
 | 
	
		
			
				|  |  |            playMode: "MP3",
 | 
	
		
			
				|  |  |            enableEvaluation: 1,
 | 
	
		
			
				|  |  | -          extConfigJson: "{}"
 | 
	
		
			
				|  |  | +          extConfigJson: "{}",
 | 
	
		
			
				|  |  | +          subjectId:null,
 | 
	
		
			
				|  |  | +          speed:'',
 | 
	
		
			
				|  |  | +          xmlUrl:null,
 | 
	
		
			
				|  |  | +          isShowFingering:null,
 | 
	
		
			
				|  |  |          },
 | 
	
		
			
				|  |  |          sysMusicScoreAccompaniments: [
 | 
	
		
			
				|  |  |            {
 | 
	
	
		
			
				|  | @@ -461,7 +470,7 @@ export default {
 | 
	
		
			
				|  |  |              xmlUrl: "",
 | 
	
		
			
				|  |  |              isShowFingering: true,
 | 
	
		
			
				|  |  |              mome: "",
 | 
	
		
			
				|  |  | -            track: ""
 | 
	
		
			
				|  |  | +            track: "",
 | 
	
		
			
				|  |  |            },
 | 
	
		
			
				|  |  |          ],
 | 
	
		
			
				|  |  |          delExamSongAccompanimentIds: [],
 | 
	
	
		
			
				|  | @@ -481,12 +490,12 @@ export default {
 | 
	
		
			
				|  |  |        try {
 | 
	
		
			
				|  |  |          this.extConfigJson = JSON.parse(this.detail.extConfigJson);
 | 
	
		
			
				|  |  |        } catch (error) {
 | 
	
		
			
				|  |  | -        this.extConfigJson = {...initailExtConfigJson}
 | 
	
		
			
				|  |  | +        this.extConfigJson = { ...initailExtConfigJson };
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |        this.form.repeatedBeats = this.extConfigJson.repeatedBeats;
 | 
	
		
			
				|  |  |        this.form.graduals = this.extConfigJson.gradualTimes || {};
 | 
	
		
			
				|  |  |        this.$set(this.form, "sysMusicScore", {
 | 
	
		
			
				|  |  | -        isOpenMetronome:Number(this.detail.isOpenMetronome),
 | 
	
		
			
				|  |  | +        isOpenMetronome: Number(this.detail.isOpenMetronome),
 | 
	
		
			
				|  |  |          name: this.detail.name,
 | 
	
		
			
				|  |  |          url: this.detail.url,
 | 
	
		
			
				|  |  |          midiUrl: this.detail.midiUrl,
 | 
	
	
		
			
				|  | @@ -506,9 +515,11 @@ export default {
 | 
	
		
			
				|  |  |        } else {
 | 
	
		
			
				|  |  |          this.form.rankIdType = 0;
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | -      const xmlres = await axios(this.detail.xmlUrl)
 | 
	
		
			
				|  |  | -      this.gradual = getGradualLengthByXml(xmlres.data).filter(item => item.length === 2)
 | 
	
		
			
				|  |  | -      this.partListNames = this.getPartListNames(xmlres.data)
 | 
	
		
			
				|  |  | +      const xmlres = await axios(this.detail.xmlUrl);
 | 
	
		
			
				|  |  | +      this.gradual = getGradualLengthByXml(xmlres.data).filter(
 | 
	
		
			
				|  |  | +        (item) => item.length === 2
 | 
	
		
			
				|  |  | +      );
 | 
	
		
			
				|  |  | +      this.partListNames = this.getPartListNames(xmlres.data);
 | 
	
		
			
				|  |  |        this.FeatchDetailList();
 | 
	
		
			
				|  |  |      } else {
 | 
	
		
			
				|  |  |        // 新增条件下默认设置为收费
 | 
	
	
		
			
				|  | @@ -518,40 +529,65 @@ export default {
 | 
	
		
			
				|  |  |    },
 | 
	
		
			
				|  |  |    methods: {
 | 
	
		
			
				|  |  |      getPartListNames(xml) {
 | 
	
		
			
				|  |  | -      if (!xml) return []
 | 
	
		
			
				|  |  | -      const xmlParse = new DOMParser().parseFromString(xml, 'text/xml')
 | 
	
		
			
				|  |  | -      const partList = xmlParse.getElementsByTagName('part-list')?.[0]?.getElementsByTagName('score-part') || []
 | 
	
		
			
				|  |  | -      const partListNames = Array.from(partList).map(item => item.getElementsByTagName('part-name')?.[0].textContent || '')
 | 
	
		
			
				|  |  | -      this.xmlFirstSpeed = xmlParse.getElementsByTagName('per-minute')?.[0]?.textContent || ''
 | 
	
		
			
				|  |  | -      return partListNames.filter(text => text.toLocaleUpperCase() !== 'COMMON')
 | 
	
		
			
				|  |  | +      if (!xml) return [];
 | 
	
		
			
				|  |  | +      const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
 | 
	
		
			
				|  |  | +      const partList =
 | 
	
		
			
				|  |  | +        xmlParse
 | 
	
		
			
				|  |  | +          .getElementsByTagName("part-list")?.[0]
 | 
	
		
			
				|  |  | +          ?.getElementsByTagName("score-part") || [];
 | 
	
		
			
				|  |  | +      const partListNames = Array.from(partList).map(
 | 
	
		
			
				|  |  | +        (item) => item.getElementsByTagName("part-name")?.[0].textContent || ""
 | 
	
		
			
				|  |  | +      );
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      this.xmlFirstSpeed =
 | 
	
		
			
				|  |  | +        xmlParse.getElementsByTagName("per-minute")?.[0]?.textContent || "";
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      // this.form.sysMusicScore.speed = this.xmlFirstSpeed;
 | 
	
		
			
				|  |  | +      this.$set(this.form.sysMusicScore,'speed',this.xmlFirstSpeed)
 | 
	
		
			
				|  |  | +      return partListNames.filter(
 | 
	
		
			
				|  |  | +        (text) => text.toLocaleUpperCase() !== "COMMON"
 | 
	
		
			
				|  |  | +      );
 | 
	
		
			
				|  |  |      },
 | 
	
		
			
				|  |  |      inputFile(file) {
 | 
	
		
			
				|  |  | -      const xmlRead = new FileReader()
 | 
	
		
			
				|  |  | -      xmlRead.onload = res => {
 | 
	
		
			
				|  |  | -        this.partListNames = this.getPartListNames(res.target.result)
 | 
	
		
			
				|  |  | -        this.gradual = getGradualLengthByXml(res.target.result).filter(item => item.length === 2)
 | 
	
		
			
				|  |  | +      const xmlRead = new FileReader();
 | 
	
		
			
				|  |  | +      xmlRead.onload = (res) => {
 | 
	
		
			
				|  |  | +        this.partListNames = this.getPartListNames(res.target.result);
 | 
	
		
			
				|  |  | +        this.gradual = getGradualLengthByXml(res.target.result).filter(
 | 
	
		
			
				|  |  | +          (item) => item.length === 2
 | 
	
		
			
				|  |  | +        );
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          for (let j = 0; j < this.form.sysMusicScoreAccompaniments.length; j++) {
 | 
	
		
			
				|  |  | -          this.form.sysMusicScoreAccompaniments[j].track =  this.partListNames[j]
 | 
	
		
			
				|  |  | +          this.form.sysMusicScoreAccompaniments[j].track =
 | 
	
		
			
				|  |  | +            this.partListNames[j];
 | 
	
		
			
				|  |  |            if (!this.form.sysMusicScoreAccompaniments[j].speed) {
 | 
	
		
			
				|  |  | -            this.form.sysMusicScoreAccompaniments[j].speed = this.xmlFirstSpeed
 | 
	
		
			
				|  |  | +            this.form.sysMusicScoreAccompaniments[j].speed = this.xmlFirstSpeed;
 | 
	
		
			
				|  |  |            }
 | 
	
		
			
				|  |  | -          this.$set(this.form, 'sysMusicScoreAccompaniments', this.form.sysMusicScoreAccompaniments)
 | 
	
		
			
				|  |  | +          this.$set(
 | 
	
		
			
				|  |  | +            this.form,
 | 
	
		
			
				|  |  | +            "sysMusicScoreAccompaniments",
 | 
	
		
			
				|  |  | +            this.form.sysMusicScoreAccompaniments
 | 
	
		
			
				|  |  | +          );
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | -        for (let index = this.form.sysMusicScoreAccompaniments.length; index < this.partListNames.length; index++) {
 | 
	
		
			
				|  |  | -          const part = this.partListNames[index]
 | 
	
		
			
				|  |  | +        for (
 | 
	
		
			
				|  |  | +          let index = this.form.sysMusicScoreAccompaniments.length;
 | 
	
		
			
				|  |  | +          index < this.partListNames.length;
 | 
	
		
			
				|  |  | +          index++
 | 
	
		
			
				|  |  | +        ) {
 | 
	
		
			
				|  |  | +          const part = this.partListNames[index];
 | 
	
		
			
				|  |  |            const sysData = {
 | 
	
		
			
				|  |  |              ...this.form.sysMusicScoreAccompaniments[0],
 | 
	
		
			
				|  |  |              metronomeMp3Url: "",
 | 
	
		
			
				|  |  |              mp3Url: "",
 | 
	
		
			
				|  |  |              track: part,
 | 
	
		
			
				|  |  | -          }
 | 
	
		
			
				|  |  | +          };
 | 
	
		
			
				|  |  |            if (!sysData.speed) {
 | 
	
		
			
				|  |  | -            sysData.speed = this.xmlFirstSpeed
 | 
	
		
			
				|  |  | +            sysData.speed = this.xmlFirstSpeed;
 | 
	
		
			
				|  |  |            }
 | 
	
		
			
				|  |  | -          this.createSys(sysData)
 | 
	
		
			
				|  |  | +          this.createSys(sysData);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -      xmlRead.readAsText(file.raw)
 | 
	
		
			
				|  |  | +      };
 | 
	
		
			
				|  |  | +      xmlRead.readAsText(file.raw);
 | 
	
		
			
				|  |  |      },
 | 
	
		
			
				|  |  |      rankChange(value) {
 | 
	
		
			
				|  |  |        if (value) {
 | 
	
	
		
			
				|  | @@ -595,15 +631,18 @@ export default {
 | 
	
		
			
				|  |  |        try {
 | 
	
		
			
				|  |  |          const res = await queryTree();
 | 
	
		
			
				|  |  |          this.tree = res.data;
 | 
	
		
			
				|  |  | -        this.formatTree(this.tree)
 | 
	
		
			
				|  |  | +        this.formatTree(this.tree);
 | 
	
		
			
				|  |  |        } catch (error) {}
 | 
	
		
			
				|  |  |      },
 | 
	
		
			
				|  |  | -    formatTree(data){
 | 
	
		
			
				|  |  | -      for(let i of data) {
 | 
	
		
			
				|  |  | -        if(i.sysMusicScoreCategoriesList && i.sysMusicScoreCategoriesList.length > 0) {
 | 
	
		
			
				|  |  | -          this.formatTree(i.sysMusicScoreCategoriesList, i)
 | 
	
		
			
				|  |  | +    formatTree(data) {
 | 
	
		
			
				|  |  | +      for (let i of data) {
 | 
	
		
			
				|  |  | +        if (
 | 
	
		
			
				|  |  | +          i.sysMusicScoreCategoriesList &&
 | 
	
		
			
				|  |  | +          i.sysMusicScoreCategoriesList.length > 0
 | 
	
		
			
				|  |  | +        ) {
 | 
	
		
			
				|  |  | +          this.formatTree(i.sysMusicScoreCategoriesList, i);
 | 
	
		
			
				|  |  |          } else {
 | 
	
		
			
				|  |  | -          i.sysMusicScoreCategoriesList = null
 | 
	
		
			
				|  |  | +          i.sysMusicScoreCategoriesList = null;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |      },
 | 
	
	
		
			
				|  | @@ -618,17 +657,29 @@ export default {
 | 
	
		
			
				|  |  |              item.subjectId = null;
 | 
	
		
			
				|  |  |            }
 | 
	
		
			
				|  |  |          });
 | 
	
		
			
				|  |  | +      if(result.length >0){
 | 
	
		
			
				|  |  | +        console.log(result[0].speed)
 | 
	
		
			
				|  |  | +        this.$set(this.form.sysMusicScore, "subjectId", result[0].subjectId);
 | 
	
		
			
				|  |  | +        this.$set(this.form.sysMusicScore, "speed", result[0].speed);
 | 
	
		
			
				|  |  | +        this.$set(this.form.sysMusicScore, "xmlUrl", result[0].xmlUrl);
 | 
	
		
			
				|  |  | +        this.$set(this.form.sysMusicScore, "isShowFingering", result[0].isShowFingering);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |          this.$set(this.form, "sysMusicScoreAccompaniments", result);
 | 
	
		
			
				|  |  |        } catch (error) {}
 | 
	
		
			
				|  |  |      },
 | 
	
		
			
				|  |  |      createSys(initData) {
 | 
	
		
			
				|  |  | -      this.form.sysMusicScoreAccompaniments.push(Object.assign({
 | 
	
		
			
				|  |  | -        subjectId: "",
 | 
	
		
			
				|  |  | -        speed: "",
 | 
	
		
			
				|  |  | -        mp3Url: "",
 | 
	
		
			
				|  |  | -        xmlUrl: "",
 | 
	
		
			
				|  |  | -        track: "",
 | 
	
		
			
				|  |  | -      }, (initData || {})));
 | 
	
		
			
				|  |  | +      this.form.sysMusicScoreAccompaniments.push(
 | 
	
		
			
				|  |  | +        Object.assign(
 | 
	
		
			
				|  |  | +          {
 | 
	
		
			
				|  |  | +            subjectId: "",
 | 
	
		
			
				|  |  | +            speed: "",
 | 
	
		
			
				|  |  | +            mp3Url: "",
 | 
	
		
			
				|  |  | +            xmlUrl: "",
 | 
	
		
			
				|  |  | +            track: "",
 | 
	
		
			
				|  |  | +          },
 | 
	
		
			
				|  |  | +          initData || {}
 | 
	
		
			
				|  |  | +        )
 | 
	
		
			
				|  |  | +      );
 | 
	
		
			
				|  |  |      },
 | 
	
		
			
				|  |  |      async removeSys(index) {
 | 
	
		
			
				|  |  |        try {
 | 
	
	
		
			
				|  | @@ -661,6 +712,13 @@ export default {
 | 
	
		
			
				|  |  |      async submit() {
 | 
	
		
			
				|  |  |        this.$refs.form.validate(async (valid) => {
 | 
	
		
			
				|  |  |          if (valid) {
 | 
	
		
			
				|  |  | +          // 提交前平铺 速度,声部,XML,isShowFingering
 | 
	
		
			
				|  |  | +          this.form.sysMusicScoreAccompaniments.forEach(item=>{
 | 
	
		
			
				|  |  | +            item.speed = this.form.sysMusicScore.speed
 | 
	
		
			
				|  |  | +            item.subjectId = this.form.sysMusicScore.subjectId
 | 
	
		
			
				|  |  | +            item.spexmlUrled =  this.form.sysMusicScore.spexmlUrled
 | 
	
		
			
				|  |  | +            item.isShowFingering =  this.form.sysMusicScore.isShowFingering
 | 
	
		
			
				|  |  | +          })
 | 
	
		
			
				|  |  |            if (!this.detail) {
 | 
	
		
			
				|  |  |              await Add({
 | 
	
		
			
				|  |  |                ...this.form,
 | 
	
	
		
			
				|  | @@ -710,43 +768,46 @@ export default {
 | 
	
		
			
				|  |  |   * @param ele 指定元素
 | 
	
		
			
				|  |  |   * @param selectors 选择器
 | 
	
		
			
				|  |  |   */
 | 
	
		
			
				|  |  | - const getNextNote = (ele, selectors) => {
 | 
	
		
			
				|  |  | -  let index = 0
 | 
	
		
			
				|  |  | -  const parentEle = ele.closest(selectors)
 | 
	
		
			
				|  |  | -  let pointer = parentEle
 | 
	
		
			
				|  |  | -  const measure = parentEle?.closest('measure')
 | 
	
		
			
				|  |  | -  let siblingNote = null
 | 
	
		
			
				|  |  | +const getNextNote = (ele, selectors) => {
 | 
	
		
			
				|  |  | +  let index = 0;
 | 
	
		
			
				|  |  | +  const parentEle = ele.closest(selectors);
 | 
	
		
			
				|  |  | +  let pointer = parentEle;
 | 
	
		
			
				|  |  | +  const measure = parentEle?.closest("measure");
 | 
	
		
			
				|  |  | +  let siblingNote = null;
 | 
	
		
			
				|  |  |    // 查找到相邻的第一个note元素
 | 
	
		
			
				|  |  |    while (!siblingNote && index < (measure?.childNodes.length || 50)) {
 | 
	
		
			
				|  |  | -    index++
 | 
	
		
			
				|  |  | -    if (pointer?.nextElementSibling?.tagName === 'note') {
 | 
	
		
			
				|  |  | -      siblingNote = pointer?.nextElementSibling
 | 
	
		
			
				|  |  | +    index++;
 | 
	
		
			
				|  |  | +    if (pointer?.nextElementSibling?.tagName === "note") {
 | 
	
		
			
				|  |  | +      siblingNote = pointer?.nextElementSibling;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -    pointer = pointer?.nextElementSibling
 | 
	
		
			
				|  |  | +    pointer = pointer?.nextElementSibling;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -  return siblingNote
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | +  return siblingNote;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  export const onlyVisible = (xml, partIndex) => {
 | 
	
		
			
				|  |  | -  if (!xml) return ''
 | 
	
		
			
				|  |  | -  const xmlParse = new DOMParser().parseFromString(xml, 'text/xml')
 | 
	
		
			
				|  |  | -  const partList = xmlParse.getElementsByTagName('part-list')?.[0]?.getElementsByTagName('score-part') || []
 | 
	
		
			
				|  |  | +  if (!xml) return "";
 | 
	
		
			
				|  |  | +  const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
 | 
	
		
			
				|  |  | +  const partList =
 | 
	
		
			
				|  |  | +    xmlParse
 | 
	
		
			
				|  |  | +      .getElementsByTagName("part-list")?.[0]
 | 
	
		
			
				|  |  | +      ?.getElementsByTagName("score-part") || [];
 | 
	
		
			
				|  |  |    // const partListNames = Array.from(partList).map(item => item.getElementsByTagName('part-name')?.[0].textContent || '')
 | 
	
		
			
				|  |  | -  const parts = xmlParse.getElementsByTagName('part')
 | 
	
		
			
				|  |  | +  const parts = xmlParse.getElementsByTagName("part");
 | 
	
		
			
				|  |  |    // const firstTimeInfo = parts[0]?.getElementsByTagName('metronome')[0]?.parentElement?.parentElement?.cloneNode(true)
 | 
	
		
			
				|  |  |    // const firstMeasures = [...Array.from(parts[0]?.getElementsByTagName('measure') || [])]
 | 
	
		
			
				|  |  |    // const metronomes = [...Array.from(parts[0]?.getElementsByTagName('metronome') || [])]
 | 
	
		
			
				|  |  |    // const words = [...Array.from(parts[0]?.getElementsByTagName('words') || [])]
 | 
	
		
			
				|  |  |    // const codas = [...Array.from(parts[0]?.getElementsByTagName('coda') || [])]
 | 
	
		
			
				|  |  |    // const rehearsals = [...Array.from(parts[0]?.getElementsByTagName('rehearsal') || [])]
 | 
	
		
			
				|  |  | -  const visiblePartInfo = partList[partIndex]
 | 
	
		
			
				|  |  | +  const visiblePartInfo = partList[partIndex];
 | 
	
		
			
				|  |  |    // console.log(visiblePartInfo, partIndex)
 | 
	
		
			
				|  |  |    // state.partListNames = partListNames
 | 
	
		
			
				|  |  |    if (visiblePartInfo) {
 | 
	
		
			
				|  |  | -    const id = visiblePartInfo.getAttribute('id')
 | 
	
		
			
				|  |  | -    Array.from(parts).forEach(part => {
 | 
	
		
			
				|  |  | -      if (part && part.getAttribute('id') !== id) {
 | 
	
		
			
				|  |  | -        part.parentNode?.removeChild(part)
 | 
	
		
			
				|  |  | +    const id = visiblePartInfo.getAttribute("id");
 | 
	
		
			
				|  |  | +    Array.from(parts).forEach((part) => {
 | 
	
		
			
				|  |  | +      if (part && part.getAttribute("id") !== id) {
 | 
	
		
			
				|  |  | +        part.parentNode?.removeChild(part);
 | 
	
		
			
				|  |  |          // 不等于第一行才添加避免重复添加
 | 
	
		
			
				|  |  |        } else {
 | 
	
		
			
				|  |  |          // words.forEach(word => {
 | 
	
	
		
			
				|  | @@ -763,101 +824,101 @@ export const onlyVisible = (xml, partIndex) => {
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |        // 最后一个小节的结束线元素不在最后 调整
 | 
	
		
			
				|  |  | -      if (part && part.getAttribute('id') === id) {
 | 
	
		
			
				|  |  | -        const barlines = part.getElementsByTagName('barline')
 | 
	
		
			
				|  |  | -        const lastParent = barlines[barlines.length - 1]?.parentElement
 | 
	
		
			
				|  |  | -        if (lastParent?.lastElementChild?.tagName !== 'barline') {
 | 
	
		
			
				|  |  | -          const children = lastParent?.children || []
 | 
	
		
			
				|  |  | +      if (part && part.getAttribute("id") === id) {
 | 
	
		
			
				|  |  | +        const barlines = part.getElementsByTagName("barline");
 | 
	
		
			
				|  |  | +        const lastParent = barlines[barlines.length - 1]?.parentElement;
 | 
	
		
			
				|  |  | +        if (lastParent?.lastElementChild?.tagName !== "barline") {
 | 
	
		
			
				|  |  | +          const children = lastParent?.children || [];
 | 
	
		
			
				|  |  |            for (let el of children) {
 | 
	
		
			
				|  |  | -            if (el.tagName === 'barline') {
 | 
	
		
			
				|  |  | +            if (el.tagName === "barline") {
 | 
	
		
			
				|  |  |                // 将结束线元素放到最后
 | 
	
		
			
				|  |  | -              lastParent?.appendChild(el)
 | 
	
		
			
				|  |  | -              break
 | 
	
		
			
				|  |  | +              lastParent?.appendChild(el);
 | 
	
		
			
				|  |  | +              break;
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |            }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | -    })
 | 
	
		
			
				|  |  | -    Array.from(partList).forEach(part => {
 | 
	
		
			
				|  |  | -      if (part && part.getAttribute('id') !== id) {
 | 
	
		
			
				|  |  | -        part.parentNode?.removeChild(part)
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  | +    Array.from(partList).forEach((part) => {
 | 
	
		
			
				|  |  | +      if (part && part.getAttribute("id") !== id) {
 | 
	
		
			
				|  |  | +        part.parentNode?.removeChild(part);
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | -    })
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  |      // 处理装饰音问题
 | 
	
		
			
				|  |  | -    const notes = xmlParse.getElementsByTagName('note')
 | 
	
		
			
				|  |  | +    const notes = xmlParse.getElementsByTagName("note");
 | 
	
		
			
				|  |  |      const getNextvNoteDuration = (i) => {
 | 
	
		
			
				|  |  | -      let nextNote = notes[i + 1]
 | 
	
		
			
				|  |  | +      let nextNote = notes[i + 1];
 | 
	
		
			
				|  |  |        // 可能存在多个装饰音问题,取下一个非装饰音时值
 | 
	
		
			
				|  |  |        for (let index = i; index < notes.length; index++) {
 | 
	
		
			
				|  |  |          const note = notes[index];
 | 
	
		
			
				|  |  | -        if (!note.getElementsByTagName('grace')?.length) {
 | 
	
		
			
				|  |  | -          nextNote = note
 | 
	
		
			
				|  |  | -          break
 | 
	
		
			
				|  |  | +        if (!note.getElementsByTagName("grace")?.length) {
 | 
	
		
			
				|  |  | +          nextNote = note;
 | 
	
		
			
				|  |  | +          break;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | -      const nextNoteDuration = nextNote?.getElementsByTagName('duration')[0]
 | 
	
		
			
				|  |  | -      return nextNoteDuration
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +      const nextNoteDuration = nextNote?.getElementsByTagName("duration")[0];
 | 
	
		
			
				|  |  | +      return nextNoteDuration;
 | 
	
		
			
				|  |  | +    };
 | 
	
		
			
				|  |  |      Array.from(notes).forEach((note, i) => {
 | 
	
		
			
				|  |  | -      const graces = note.getElementsByTagName('grace')
 | 
	
		
			
				|  |  | +      const graces = note.getElementsByTagName("grace");
 | 
	
		
			
				|  |  |        if (graces && graces.length) {
 | 
	
		
			
				|  |  |          // if (i !== 0) {
 | 
	
		
			
				|  |  | -          note.appendChild(getNextvNoteDuration(i)?.cloneNode(true))
 | 
	
		
			
				|  |  | +        note.appendChild(getNextvNoteDuration(i)?.cloneNode(true));
 | 
	
		
			
				|  |  |          // }
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | -    })
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    // console.log(new XMLSerializer().serializeToString(xmlParse))
 | 
	
		
			
				|  |  | -  return new XMLSerializer().serializeToString(xmlParse)
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | +  return new XMLSerializer().serializeToString(xmlParse);
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  const speedInfo = {
 | 
	
		
			
				|  |  | -  'rall.': 1.333333333,
 | 
	
		
			
				|  |  | -  'poco rit.': 1.333333333,
 | 
	
		
			
				|  |  | -  'rit.': 1.333333333,
 | 
	
		
			
				|  |  | -  'molto rit.': 1.333333333,
 | 
	
		
			
				|  |  | -  'molto rall': 1.333333333,
 | 
	
		
			
				|  |  | +  "rall.": 1.333333333,
 | 
	
		
			
				|  |  | +  "poco rit.": 1.333333333,
 | 
	
		
			
				|  |  | +  "rit.": 1.333333333,
 | 
	
		
			
				|  |  | +  "molto rit.": 1.333333333,
 | 
	
		
			
				|  |  | +  "molto rall": 1.333333333,
 | 
	
		
			
				|  |  |    lentando: 1.333333333,
 | 
	
		
			
				|  |  |    allargando: 1.333333333,
 | 
	
		
			
				|  |  |    morendo: 1.333333333,
 | 
	
		
			
				|  |  | -  'accel.': 0.8,
 | 
	
		
			
				|  |  | +  "accel.": 0.8,
 | 
	
		
			
				|  |  |    calando: 2,
 | 
	
		
			
				|  |  | -  'poco accel.': 0.8,
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | +  "poco accel.": 0.8,
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /**
 | 
	
		
			
				|  |  |   * 按照xml进行减慢速度的计算
 | 
	
		
			
				|  |  |   * @param xml 始终按照第一分谱进行减慢速度的计算
 | 
	
		
			
				|  |  |   */
 | 
	
		
			
				|  |  | - export function getGradualLengthByXml (xml) {
 | 
	
		
			
				|  |  | -  const firstPartXml = onlyVisible(xml, 0)
 | 
	
		
			
				|  |  | -  const xmlParse = new DOMParser().parseFromString(firstPartXml, 'text/xml')
 | 
	
		
			
				|  |  | -  const measures = Array.from(xmlParse.querySelectorAll('measure'))
 | 
	
		
			
				|  |  | -  const notes = Array.from(xmlParse.querySelectorAll('note'))
 | 
	
		
			
				|  |  | -  const words = Array.from(xmlParse.querySelectorAll('words'))
 | 
	
		
			
				|  |  | -  const metronomes = Array.from(xmlParse.querySelectorAll('metronome'))
 | 
	
		
			
				|  |  | +export function getGradualLengthByXml(xml) {
 | 
	
		
			
				|  |  | +  const firstPartXml = onlyVisible(xml, 0);
 | 
	
		
			
				|  |  | +  const xmlParse = new DOMParser().parseFromString(firstPartXml, "text/xml");
 | 
	
		
			
				|  |  | +  const measures = Array.from(xmlParse.querySelectorAll("measure"));
 | 
	
		
			
				|  |  | +  const notes = Array.from(xmlParse.querySelectorAll("note"));
 | 
	
		
			
				|  |  | +  const words = Array.from(xmlParse.querySelectorAll("words"));
 | 
	
		
			
				|  |  | +  const metronomes = Array.from(xmlParse.querySelectorAll("metronome"));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  const eles = []
 | 
	
		
			
				|  |  | +  const eles = [];
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    for (const ele of [...words, ...metronomes]) {
 | 
	
		
			
				|  |  | -    const note = getNextNote(ele, 'direction')
 | 
	
		
			
				|  |  | +    const note = getNextNote(ele, "direction");
 | 
	
		
			
				|  |  |      // console.log(ele, note)
 | 
	
		
			
				|  |  |      if (note) {
 | 
	
		
			
				|  |  | -      const measure = note?.closest('measure')
 | 
	
		
			
				|  |  | -      const measureNotes = Array.from(measure.querySelectorAll('note'))
 | 
	
		
			
				|  |  | +      const measure = note?.closest("measure");
 | 
	
		
			
				|  |  | +      const measureNotes = Array.from(measure.querySelectorAll("note"));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |        const noteInMeasureIndex = Array.from(measure.childNodes)
 | 
	
		
			
				|  |  | -        .filter((item) => item.nodeName === 'note')
 | 
	
		
			
				|  |  | -        .findIndex((item) => item === note)
 | 
	
		
			
				|  |  | +        .filter((item) => item.nodeName === "note")
 | 
	
		
			
				|  |  | +        .findIndex((item) => item === note);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      let allDuration = 0
 | 
	
		
			
				|  |  | -      let leftDuration = 0
 | 
	
		
			
				|  |  | +      let allDuration = 0;
 | 
	
		
			
				|  |  | +      let leftDuration = 0;
 | 
	
		
			
				|  |  |        for (let i = 0; i < measureNotes.length; i++) {
 | 
	
		
			
				|  |  | -        const n = measureNotes[i]
 | 
	
		
			
				|  |  | -        const duration = +(n.querySelector('duration')?.textContent || '0')
 | 
	
		
			
				|  |  | -        allDuration += duration
 | 
	
		
			
				|  |  | +        const n = measureNotes[i];
 | 
	
		
			
				|  |  | +        const duration = +(n.querySelector("duration")?.textContent || "0");
 | 
	
		
			
				|  |  | +        allDuration += duration;
 | 
	
		
			
				|  |  |          if (i < noteInMeasureIndex) {
 | 
	
		
			
				|  |  | -          leftDuration = allDuration
 | 
	
		
			
				|  |  | +          leftDuration = allDuration;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |        eles.push({
 | 
	
	
		
			
				|  | @@ -869,16 +930,16 @@ const speedInfo = {
 | 
	
		
			
				|  |  |          type: ele.tagName,
 | 
	
		
			
				|  |  |          allDuration,
 | 
	
		
			
				|  |  |          leftDuration,
 | 
	
		
			
				|  |  | -      })
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  const gradualNotes = []
 | 
	
		
			
				|  |  | -  eles.sort((a, b) => a.index - b.index)
 | 
	
		
			
				|  |  | -  const keys = Object.keys(speedInfo).map(w => w.toLocaleLowerCase())
 | 
	
		
			
				|  |  | +  const gradualNotes = [];
 | 
	
		
			
				|  |  | +  eles.sort((a, b) => a.index - b.index);
 | 
	
		
			
				|  |  | +  const keys = Object.keys(speedInfo).map((w) => w.toLocaleLowerCase());
 | 
	
		
			
				|  |  |    for (const ele of eles) {
 | 
	
		
			
				|  |  | -    const textContent = ele.textContent?.toLocaleLowerCase().trim()
 | 
	
		
			
				|  |  | -    if (ele.type === 'words' && keys.includes(textContent)) {
 | 
	
		
			
				|  |  | +    const textContent = ele.textContent?.toLocaleLowerCase().trim();
 | 
	
		
			
				|  |  | +    if (ele.type === "words" && keys.includes(textContent)) {
 | 
	
		
			
				|  |  |        gradualNotes.push([
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |            start: ele.index,
 | 
	
	
		
			
				|  | @@ -888,13 +949,13 @@ const speedInfo = {
 | 
	
		
			
				|  |  |            leftDuration: ele.leftDuration,
 | 
	
		
			
				|  |  |            type: textContent,
 | 
	
		
			
				|  |  |          },
 | 
	
		
			
				|  |  | -      ])
 | 
	
		
			
				|  |  | +      ]);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      if (
 | 
	
		
			
				|  |  | -      ele.type === 'metronome' ||
 | 
	
		
			
				|  |  | -      (ele.type === 'words' && textContent === 'a tempo')
 | 
	
		
			
				|  |  | +      ele.type === "metronome" ||
 | 
	
		
			
				|  |  | +      (ele.type === "words" && textContent === "a tempo")
 | 
	
		
			
				|  |  |      ) {
 | 
	
		
			
				|  |  | -      const indexOf = gradualNotes.findIndex((item) => item.length === 1)
 | 
	
		
			
				|  |  | +      const indexOf = gradualNotes.findIndex((item) => item.length === 1);
 | 
	
		
			
				|  |  |        if (indexOf > -1 && ele.index > gradualNotes[indexOf]?.[0].start) {
 | 
	
		
			
				|  |  |          gradualNotes[indexOf][1] = {
 | 
	
		
			
				|  |  |            start: ele.index,
 | 
	
	
		
			
				|  | @@ -903,11 +964,11 @@ const speedInfo = {
 | 
	
		
			
				|  |  |            allDuration: ele.allDuration,
 | 
	
		
			
				|  |  |            leftDuration: ele.leftDuration,
 | 
	
		
			
				|  |  |            type: textContent,
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | +        };
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -  return gradualNotes
 | 
	
		
			
				|  |  | +  return gradualNotes;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  </script>
 | 
	
		
			
				|  |  |  <style lang="less" scoped>
 |