fix(followup-audio): preserve roles chosen before first audio file
This commit is contained in:
@@ -59,6 +59,7 @@
|
||||
<section v-if="hasStereo" class="channel-role-box" aria-label="声道角色人工标注">
|
||||
<label>声道角色(人工确认)<el-select :model-value="channelRoles" :disabled="!canEditReview || busy" @update:model-value="setChannelRoles"><el-option label="角色待确认" value="unconfirmed" /><el-option label="左客服端 / 右患者或家属端" value="left_service" /><el-option label="右客服端 / 左患者或家属端" value="right_service" /></el-select></label>
|
||||
<p class="muted">声道不等于已核实的说话人身份。角色标注不自动重新提炼;变更后保存草稿会取消全部采纳,须重新核对。</p>
|
||||
<p v-if="savedChannelRoles !== 'unconfirmed' && !roleDirty" class="muted">当前角色已确认,无需再次选择;只有左右标错时才需修改。</p>
|
||||
<p v-if="roleDirty" class="review-error" role="status">角色标注尚未保存,请先保存草稿,再逐项核对。</p>
|
||||
</section>
|
||||
<FollowupAudioReview :key="selectedId" ref="reviewRef" v-model:items="reviewItems" :fields="detail.fields || capabilities.fields" :disabled="!canEditReview || busy" :can-daily="capabilities.can_daily" :applied="detail.status === 'applied'" :uncertainties="detail.uncertainties" :snapshot-items="detail.items" :channel-roles="savedChannelRoles" @seek="loadDialogue" />
|
||||
@@ -292,7 +293,11 @@ async function backToList() {
|
||||
stop(); resetRoles(); selectedId.value = 0; detail.value = null; reviewItems.value = []; snapshot.value = '[]'; detailSections.value = []; errorMessage.value = ''
|
||||
await refresh()
|
||||
}
|
||||
function openUpload() { if (canUpload.value && !busy.value && !createUncertain.value) uploadVisible.value = true }
|
||||
function openUpload() {
|
||||
if (!canUpload.value || busy.value || createUncertain.value) return
|
||||
if (!file.value) uploadChannelRoles.value = 'unconfirmed'
|
||||
uploadVisible.value = true
|
||||
}
|
||||
function closeUpload(done?: () => void) { if (uploading.value) return; uploadVisible.value = false; done?.() }
|
||||
function statusType(status: string) {
|
||||
if (status === 'failed' || status === 'needs_reconciliation') return 'danger'
|
||||
@@ -300,7 +305,16 @@ function statusType(status: string) {
|
||||
if (status === 'review') return 'warning'
|
||||
return 'info'
|
||||
}
|
||||
function chooseFile(event: Event) { uploadChannelRoles.value = 'unconfirmed'; file.value = (event.target as HTMLInputElement).files?.[0] || null; uploadProgress.value = 0; uploadStage.value = '' }
|
||||
function chooseFile(event: Event) {
|
||||
const nextFile = (event.target as HTMLInputElement).files?.[0] || null
|
||||
// Cancelling the picker or selecting the first file must not discard a role chosen earlier.
|
||||
if (!nextFile) return
|
||||
if (file.value && nextFile !== file.value) {
|
||||
uploadChannelRoles.value = 'unconfirmed'
|
||||
ElMessage.warning('录音已更换,请重新核对本次录音的声道角色')
|
||||
}
|
||||
file.value = nextFile; uploadProgress.value = 0; uploadStage.value = ''
|
||||
}
|
||||
async function submitUpload() {
|
||||
if (busy.value || createUncertain.value) return
|
||||
const problem = uploadIssue(file.value, recordedAt.value, modelKey.value, props.capabilities, props.viewOnly)
|
||||
|
||||
@@ -1512,10 +1512,44 @@ test('upload sends explicitly selected pre-extraction roles and clears them on p
|
||||
})
|
||||
|
||||
|
||||
test('a new recording never inherits the previous file role assignment', async () => {
|
||||
test('a replacement recording never inherits the previous file role assignment', async () => {
|
||||
const f = setupPanel(); await settle()
|
||||
f.panel.file.value = new File(['previous'], 'previous.wav')
|
||||
f.panel.uploadChannelRoles.value = 'left_service'
|
||||
f.panel.chooseFile({ target: { files: [new File(['synthetic'], 'new.wav')] } })
|
||||
assert.equal(f.panel.uploadChannelRoles.value, 'unconfirmed')
|
||||
f.dispose()
|
||||
})
|
||||
|
||||
for (const order of ['role-first', 'file-first']) test(`upload ${order} preserves roles through create and initial review without reselection`, async () => {
|
||||
let createdRole = 'unconfirmed'
|
||||
const f = setupPanel({
|
||||
followupAudioCreate: async payload => { createdRole = payload.channel_roles; return { task_id: 2, status: 'queued' } },
|
||||
followupAudioDetail: async id => stereoTask({ id, channel_roles: createdRole })
|
||||
})
|
||||
await settle(); f.panel.openUpload()
|
||||
if (order === 'role-first') f.panel.uploadChannelRoles.value = 'left_service'
|
||||
f.panel.chooseFile({ target: { files: [new File(['synthetic'], 'first.wav')] } })
|
||||
if (order === 'file-first') f.panel.uploadChannelRoles.value = 'left_service'
|
||||
f.panel.recordedAt.value = '2026-09-29 09:00:00'
|
||||
await f.panel.submitUpload()
|
||||
assert.equal(createdRole, 'left_service')
|
||||
assert.equal(f.panel.channelRoles.value, 'left_service')
|
||||
assert.equal(f.panel.savedChannelRoles.value, 'left_service')
|
||||
assert.equal(f.panel.roleDirty.value, false)
|
||||
assert.doesNotMatch(f.panel.applyIssue.value, /声道角色/)
|
||||
f.dispose()
|
||||
})
|
||||
|
||||
|
||||
test('cancelled file selection keeps current file and roles; new upload starts unconfirmed', async () => {
|
||||
const f = setupPanel(); await settle(); f.panel.openUpload()
|
||||
const recording = new File(['synthetic'], 'existing.wav')
|
||||
f.panel.file.value = recording; f.panel.uploadChannelRoles.value = 'left_service'
|
||||
f.panel.chooseFile({ target: { files: [] } })
|
||||
assert.equal(f.panel.file.value, recording)
|
||||
assert.equal(f.panel.uploadChannelRoles.value, 'left_service')
|
||||
f.panel.file.value = null; f.panel.closeUpload(); f.panel.openUpload()
|
||||
assert.equal(f.panel.uploadChannelRoles.value, 'unconfirmed')
|
||||
f.dispose()
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user