Files
teaching-feedback-assistant/pages/feedback/index.wxml
2026-07-22 19:13:55 +08:00

169 lines
8.4 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<view class="page-shell feedback-page">
<text class="eyebrow">课堂反馈</text>
<view class="page-heading">
<text class="page-title">反馈生成</text>
</view>
<text class="page-subtitle">填写课堂表现并保存到教学反馈记录。</text>
<view wx:if="{{errorMessage}}" class="status-banner">
<view class="status-copy">
<text>{{errorMessage}}</text>
<view wx:if="{{errorRequestId}}" class="status-request-id">
<text>请求编号:{{errorRequestId}}</text>
<text class="status-action" bindtap="copyRequestId">复制</text>
</view>
</view>
<text class="status-action" bindtap="retryLoad">重试</text>
</view>
<view class="feedback-form surface">
<picker class="feedback-field" mode="selector" range="{{profileOptions}}" value="{{selectedProfileIndex}}" disabled="{{loading || recording || voiceBusy}}" bindchange="onProfileChange">
<text class="form-label">学生档案</text>
<text class="form-value">{{loading ? '正在加载...' : profileOptions[selectedProfileIndex]}}</text>
</picker>
<picker class="feedback-field" mode="date" value="{{draft.feedbackDate}}" disabled="{{recording || voiceBusy}}" bindchange="onDateChange">
<text class="form-label">反馈日期</text>
<text class="form-value">{{draft.feedbackDate}}</text>
</picker>
<view class="content-field">
<view class="content-label-row">
<text class="form-label">反馈内容</text>
<view class="content-actions">
<text class="content-count">{{contentLength}} / 2000</text>
<button
wx:if="{{contentLength > 0}}"
class="content-clear-button"
disabled="{{recording || voiceBusy}}"
bindtap="clearFeedbackContent"
>清空</button>
</view>
</view>
<textarea
class="feedback-textarea"
value="{{draft.content}}"
maxlength="2000"
disabled="{{recording || voiceBusy}}"
placeholder="记录课堂完成情况、进步与后续建议"
bindinput="onContentInput"
/>
<view class="voice-input-row">
<button
class="voice-button {{recording ? 'voice-button-recording' : ''}}"
disabled="{{saving || voiceBusy}}"
bindtap="toggleRecording"
aria-label="{{recording ? '结束录音' : '开始录音'}}"
>
<text class="voice-button-icon">{{recording ? '■' : '●'}}</text>
<text>{{recording ? '结束录音' : '语音录入'}}</text>
</button>
<view class="voice-state {{voiceActionable ? 'voice-state-actionable' : ''}}" bindtap="toggleVoiceDetails">
<text class="voice-status">{{voiceStatus}}</text>
<text wx:if="{{recording}}" class="recording-time">{{recordingTime}}</text>
<view wx:elif="{{voiceActionable}}" class="voice-state-arrow {{voiceDetailsExpanded ? 'voice-state-arrow-expanded' : ''}}"></view>
</view>
<view wx:if="{{voiceDetailsExpanded}}" class="voice-record-list">
<view class="voice-record-list-heading">
<text>语音记录</text>
<view class="voice-summary-selection-heading">
<text>已选 {{selectedSummaryLessonCount}} / {{eligibleSummaryLessonCount}}</text>
<button
wx:if="{{eligibleSummaryLessonCount > 0}}"
class="voice-record-text-button"
disabled="{{recording || voiceBusy || summaryBusy || (summaryRun && (summaryRun.status === 'queued' || summaryRun.status === 'processing'))}}"
bindtap="toggleAllSummaryLessons"
>{{allSummaryLessonsSelected ? '取消全选' : '全选'}}</button>
</view>
</view>
<checkbox-group bindchange="onSummarySelectionChange">
<view
wx:for="{{voiceLessonViews}}"
wx:key="id"
wx:for-item="lesson"
class="voice-record-item"
>
<view class="voice-record-header">
<view class="voice-record-title-row">
<checkbox
wx:if="{{lesson.summaryEligible}}"
class="voice-summary-checkbox"
value="{{lesson.id}}"
checked="{{lesson.summarySelected}}"
disabled="{{recording || voiceBusy || summaryBusy || (summaryRun && (summaryRun.status === 'queued' || summaryRun.status === 'processing'))}}"
color="#007aff"
/>
<view>
<text class="voice-record-title">第{{lesson.sequence}}节</text>
<text class="voice-record-duration">{{lesson.durationText}} · {{lesson.transcriptLength}}字</text>
</view>
</view>
<text class="voice-record-status">{{lesson.statusText}}</text>
</view>
<text
wx:if="{{lesson.transcript}}"
selectable
class="voice-record-transcript {{expandedVoiceLessonId === lesson.id ? 'voice-record-transcript-expanded' : ''}}"
>{{lesson.transcript}}</text>
<text wx:else class="voice-record-empty">{{lesson.emptyText}}</text>
<view wx:if="{{lesson.transcript}}" class="voice-record-actions">
<button
wx:if="{{lesson.transcriptExpandable}}"
class="voice-record-text-button"
data-lesson-id="{{lesson.id}}"
bindtap="toggleVoiceLessonTranscript"
>{{expandedVoiceLessonId === lesson.id ? '收起' : '展开'}}</button>
<button
class="voice-record-add-button"
data-lesson-id="{{lesson.id}}"
disabled="{{!lesson.canAdd || voiceBusy}}"
bindtap="addVoiceLessonToDraft"
>{{lesson.addActionText}}</button>
</view>
</view>
</checkbox-group>
<view wx:if="{{eligibleSummaryLessonCount > 0}}" class="voice-summary-controls">
<button
class="voice-summary-button"
loading="{{summaryBusy}}"
disabled="{{recording || voiceBusy || summaryBusy || selectedSummaryLessonCount === 0 || (summaryRun && (summaryRun.status === 'queued' || summaryRun.status === 'processing'))}}"
bindtap="generateFeedbackSummary"
>{{summaryActionText}}{{selectedSummaryLessonCount}}节)</button>
</view>
<view wx:if="{{summaryRun}}" class="voice-summary-preview {{summaryRun.status === 'failed' ? 'voice-summary-preview-failed' : ''}}">
<view class="voice-summary-preview-heading">
<text>总结预览</text>
<text>{{summaryStatusText}}</text>
</view>
<text wx:if="{{summaryRun.content}}" selectable class="voice-summary-preview-content">{{summaryRun.content}}</text>
<text wx:elif="{{summaryRun.status === 'queued' || summaryRun.status === 'processing'}}" class="voice-summary-preview-note">已选择 {{summaryRun.lessonIds.length}} 节录音</text>
<button
wx:if="{{summaryRun.status === 'ready'}}"
class="voice-summary-apply-button"
loading="{{summaryBusy}}"
disabled="{{summaryBusy}}"
bindtap="applyGeneratedSummary"
>替换输入框</button>
</view>
</view>
<view wx:if="{{session && session.failedSegmentCount > 0}}" class="voice-failure-recovery">
<text class="voice-failure-hint">{{voiceFailureHint}}</text>
<button
class="voice-discard-button"
disabled="{{saving || summaryBusy || recording || voiceBusy}}"
bindtap="discardFailedSegments"
>放弃失败录音</button>
</view>
</view>
</view>
<button class="save-feedback primary-button" loading="{{saving}}" disabled="{{saving || summaryBusy || recording || voiceBusy || (summaryRun && (summaryRun.status === 'queued' || summaryRun.status === 'processing')) || (session && session.processingSegmentCount > 0 && session.failedSegmentCount === 0)}}" bindtap="saveFeedback">{{primaryActionText}}</button>
</view>
<view wx:if="{{!loading && profiles.length === 0}}" class="profiles-hint">
<text>当前没有学生档案,反馈仍可不关联学生直接保存。</text>
<text class="status-action" bindtap="goStudents">新建档案</text>
</view>
</view>