feat(mobile): align employee practice screens for release
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<view class="page practice-page employee-page-shell">
|
||||
<view class="practice-head">
|
||||
<text class="page-title">训练前预习</text>
|
||||
<text class="page-subtitle">先掌握要点,再开始场景练习</text>
|
||||
<text class="page-title">{{ practicePageTitle }}</text>
|
||||
<text class="page-subtitle">{{ practicePageSubtitle }}</text>
|
||||
</view>
|
||||
|
||||
<view v-if="message" class="status-message">{{ message }}</view>
|
||||
@@ -15,7 +15,7 @@
|
||||
<button class="primary-button login-button" @click="goLogin">去登录</button>
|
||||
</view>
|
||||
|
||||
<view v-if="loggedIn" class="panel scenario-card employee-card">
|
||||
<view v-if="loggedIn && practiceView === 'prep'" class="panel scenario-card employee-card">
|
||||
<view class="scenario-headline">
|
||||
<text class="scenario-title">{{ activeScenarioName }}</text>
|
||||
<text class="scenario-desc">{{ positionLabel }} · 业主服务场景</text>
|
||||
@@ -53,7 +53,7 @@
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<view v-if="loggedIn" class="panel assignment-panel recent-panel">
|
||||
<view v-if="loggedIn && practiceView === 'prep'" class="panel assignment-panel recent-panel">
|
||||
<view class="assignment-head">
|
||||
<text class="panel-title">最近练过</text>
|
||||
<text class="recent-link">全部 ›</text>
|
||||
@@ -74,7 +74,7 @@
|
||||
<view v-else class="empty">暂无最近练习</view>
|
||||
</view>
|
||||
|
||||
<view v-if="loggedIn" class="panel assignment-panel daily-panel">
|
||||
<view v-if="loggedIn && practiceView === 'prep'" class="panel assignment-panel daily-panel">
|
||||
<view class="assignment-head">
|
||||
<view><text class="panel-title">每日三题</text><text class="daily-subtitle">今日安排</text></view>
|
||||
<text class="daily-action-label">去作答 ›</text>
|
||||
@@ -123,13 +123,13 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="loggedIn" class="capability-strip">
|
||||
<view v-if="loggedIn && practiceView === 'prep'" class="capability-strip">
|
||||
<view><text>情绪</text><text>识别与安抚</text></view>
|
||||
<view><text>信任</text><text>建立与维护</text></view>
|
||||
<view><text>红线纠偏</text><text>识别与规避</text></view>
|
||||
</view>
|
||||
|
||||
<view v-if="turns.length" class="panel dialogue-card employee-card">
|
||||
<view v-if="practiceView === 'active' && turns.length" class="panel dialogue-card employee-card">
|
||||
<view class="dialogue-head">
|
||||
<view class="dialogue-heading-block">
|
||||
<text class="panel-title">对话</text>
|
||||
@@ -197,7 +197,7 @@
|
||||
<text v-if="draftAudioUrl" class="voice-state">已附带语音回放</text>
|
||||
</view>
|
||||
|
||||
<view v-if="helpAnswer || helpRefs.length" class="panel help-card employee-card">
|
||||
<view v-if="practiceView === 'active' && (helpAnswer || helpRefs.length)" class="panel help-card employee-card">
|
||||
<text class="panel-title">SOP 提示</text>
|
||||
<text v-if="helpAnswer" class="help-answer">{{ helpAnswer }}</text>
|
||||
<view v-if="helpRefs.length" class="help-refs">
|
||||
@@ -248,6 +248,11 @@
|
||||
</button>
|
||||
<text v-else class="satisfaction-thanks">感谢反馈,已记录本次训练体验。</text>
|
||||
</view>
|
||||
|
||||
<view v-if="result" class="result-actions">
|
||||
<button class="secondary-button" @click="openPracticeHistory">查看训练记录</button>
|
||||
<button class="primary-button" @click="start(selectedScenarioId)">再练一次</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@@ -327,6 +332,19 @@ const draftAudioOssId = ref<number | undefined>();
|
||||
const mediaRecorder = ref<MediaRecorder | null>(null);
|
||||
const mediaStream = ref<MediaStream | null>(null);
|
||||
const busy = computed(() => status.value === 'starting' || status.value === 'submitting');
|
||||
const practiceView = computed<'prep' | 'active' | 'result'>(() => {
|
||||
if (result.value) return 'result';
|
||||
return status.value === 'active' || status.value === 'submitting' ? 'active' : 'prep';
|
||||
});
|
||||
const practicePageTitle = computed(() => {
|
||||
if (practiceView.value === 'result') return '训练完成';
|
||||
return practiceView.value === 'active' ? '场景对练' : '训练前预习';
|
||||
});
|
||||
const practicePageSubtitle = computed(() => {
|
||||
if (practiceView.value === 'result') return `${activeScenarioName.value} · ${Math.max(roundIndex.value, 1)} 轮对话`;
|
||||
if (practiceView.value === 'active') return `${activeScenarioName.value} · 第 ${Math.min(roundIndex.value + 1, 3)}/3 轮`;
|
||||
return '先掌握要点,再开始场景练习';
|
||||
});
|
||||
const canStartPractice = computed(() => status.value === 'idle' || status.value === 'finished');
|
||||
const hasTraineeReply = computed(() => turns.value.some((item) => item.role === 'trainee'));
|
||||
const canFinishPractice = computed(() => hasTraineeReply.value && !redFlagActive.value);
|
||||
@@ -643,6 +661,10 @@ const openRecentItem = (item: RecentPracticeItem) => {
|
||||
uni.navigateTo({ url: '/pages/user/profile/index' });
|
||||
};
|
||||
|
||||
const openPracticeHistory = () => {
|
||||
uni.navigateTo({ url: '/pages/user/profile/index' });
|
||||
};
|
||||
|
||||
const consumePendingAssignment = () => {
|
||||
const raw = uni.getStorageSync(pendingAssignmentKey);
|
||||
if (!raw) return;
|
||||
@@ -1244,7 +1266,7 @@ onShow(() => {
|
||||
}
|
||||
|
||||
.dialogue-card {
|
||||
border-color: #dbe7ff;
|
||||
border-color: #f3dadd;
|
||||
box-shadow: var(--employee-card-shadow);
|
||||
}
|
||||
|
||||
@@ -1306,11 +1328,11 @@ onShow(() => {
|
||||
}
|
||||
|
||||
.emotion-fill {
|
||||
background: #6f9cff;
|
||||
background: #e60012;
|
||||
}
|
||||
|
||||
.trust-fill {
|
||||
background: #2f6bff;
|
||||
background: #8c929a;
|
||||
}
|
||||
|
||||
.red-flag {
|
||||
@@ -1348,19 +1370,19 @@ onShow(() => {
|
||||
}
|
||||
|
||||
.turn-row.trainee {
|
||||
border-left-color: #2f6bff;
|
||||
background: #eef4ff;
|
||||
border-left-color: #e60012;
|
||||
background: #fff0f1;
|
||||
}
|
||||
|
||||
.turn-row.coach {
|
||||
border-left-color: #8bb1ff;
|
||||
background: #f4f7ff;
|
||||
border-left-color: #ef8992;
|
||||
background: #fff7f7;
|
||||
}
|
||||
|
||||
.reply-actions button.recording-active {
|
||||
border-color: #2f6bff;
|
||||
background: #eef4ff;
|
||||
color: #2f6bff;
|
||||
border-color: #e60012;
|
||||
background: #fff0f1;
|
||||
color: #e60012;
|
||||
}
|
||||
|
||||
.turn-role {
|
||||
@@ -1384,9 +1406,9 @@ onShow(() => {
|
||||
|
||||
.style-chip {
|
||||
width: auto;
|
||||
border-color: #c9d9ff;
|
||||
border-color: #f0c7cb;
|
||||
background: #ffffff;
|
||||
color: #2f6bff;
|
||||
color: #e60012;
|
||||
}
|
||||
|
||||
.reply-card {
|
||||
@@ -1396,7 +1418,7 @@ onShow(() => {
|
||||
|
||||
.practice-textarea {
|
||||
min-height: 124px;
|
||||
border-color: #dbe7ff;
|
||||
border-color: #f3dadd;
|
||||
border-radius: 18px;
|
||||
background: #f9fbff;
|
||||
}
|
||||
@@ -1416,8 +1438,8 @@ onShow(() => {
|
||||
.help-ref {
|
||||
padding: 5px 8px;
|
||||
border-radius: 5px;
|
||||
background: #eef4ff;
|
||||
color: #2f6bff;
|
||||
background: #fff0f1;
|
||||
color: #e60012;
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
}
|
||||
@@ -1429,7 +1451,7 @@ onShow(() => {
|
||||
}
|
||||
|
||||
.result-score {
|
||||
color: #2f6bff;
|
||||
color: #e60012;
|
||||
font-size: 44px;
|
||||
font-weight: 900;
|
||||
line-height: 1;
|
||||
@@ -1447,13 +1469,13 @@ onShow(() => {
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
padding: 12px;
|
||||
border-left: 3px solid #2f6bff;
|
||||
border-left: 3px solid #e60012;
|
||||
border-radius: 6px;
|
||||
background: #eef4ff;
|
||||
background: #fff0f1;
|
||||
}
|
||||
|
||||
.mentor-rewrite text:first-child {
|
||||
color: #2f6bff;
|
||||
color: #e60012;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
@@ -1481,9 +1503,9 @@ onShow(() => {
|
||||
}
|
||||
|
||||
.satisfaction-option.selected {
|
||||
border-color: #2f6bff;
|
||||
background: #eef4ff;
|
||||
color: #2f6bff;
|
||||
border-color: #e60012;
|
||||
background: #fff0f1;
|
||||
color: #e60012;
|
||||
}
|
||||
|
||||
.satisfaction-textarea {
|
||||
@@ -1496,6 +1518,18 @@ onShow(() => {
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.result-actions {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.result-actions button {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* 练页入口态:与员工端蓝白轻卡片体系保持一致 */
|
||||
.practice-page {
|
||||
padding: 0 16px 108px;
|
||||
@@ -1504,16 +1538,16 @@ onShow(() => {
|
||||
|
||||
.practice-head {
|
||||
margin: 0 -16px;
|
||||
padding: 30px 20px 24px;
|
||||
border-bottom: 1px solid var(--employee-line);
|
||||
background: linear-gradient(180deg, #eef4ff 0%, #f5f8fd 100%);
|
||||
text-align: left;
|
||||
padding: 28px 20px 16px;
|
||||
border-bottom: 0;
|
||||
background: #f7f7f7;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.practice-head::after,
|
||||
.practice-eyebrow,
|
||||
.practice-tip {
|
||||
display: block;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.practice-head .page-title {
|
||||
@@ -1665,9 +1699,9 @@ onShow(() => {
|
||||
}
|
||||
|
||||
.scenario-card .primary-button {
|
||||
margin-top: 18px;
|
||||
background: linear-gradient(135deg, #2f6bff, #5a8cff);
|
||||
box-shadow: 0 8px 16px rgba(47, 107, 255, 0.18);
|
||||
margin-top: 2px;
|
||||
background: var(--employee-primary);
|
||||
box-shadow: 0 8px 16px rgba(230, 0, 18, 0.18);
|
||||
}
|
||||
|
||||
.login-panel {
|
||||
@@ -1675,12 +1709,12 @@ onShow(() => {
|
||||
}
|
||||
|
||||
.assignment-row {
|
||||
border-color: #dbe7ff;
|
||||
border-color: #f3dadd;
|
||||
}
|
||||
|
||||
.assignment-row.highlighted {
|
||||
border-color: #9bbaff;
|
||||
background: #f4f7ff;
|
||||
border-color: #ef8992;
|
||||
background: #fff7f7;
|
||||
}
|
||||
|
||||
.assignment-badge,
|
||||
@@ -1803,8 +1837,8 @@ onShow(() => {
|
||||
}
|
||||
|
||||
.daily-row .small-button {
|
||||
background: linear-gradient(135deg, #2f6bff, #5a8cff);
|
||||
box-shadow: 0 6px 12px rgba(47, 107, 255, 0.16);
|
||||
background: linear-gradient(135deg, #ed1f28, #ff4b4d);
|
||||
box-shadow: 0 6px 12px rgba(230, 0, 18, 0.16);
|
||||
}
|
||||
|
||||
.capability-strip {
|
||||
@@ -1843,7 +1877,7 @@ onShow(() => {
|
||||
|
||||
.practice-head .page-title {
|
||||
color: #1f2024;
|
||||
font-size: 29px;
|
||||
font-size: 30px;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
@@ -1852,36 +1886,43 @@ onShow(() => {
|
||||
}
|
||||
|
||||
.scenario-card {
|
||||
padding: 20px 16px 16px;
|
||||
border-radius: 24px;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 10px 26px rgba(24, 28, 36, 0.06);
|
||||
padding: 0;
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.scenario-headline {
|
||||
max-width: none;
|
||||
min-height: 42px;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 0 14px;
|
||||
border: 1px solid #ffd1d5;
|
||||
border-radius: 22px;
|
||||
background: #fff7f7;
|
||||
}
|
||||
|
||||
.scenario-title {
|
||||
color: #25282d;
|
||||
font-size: 27px;
|
||||
color: var(--employee-primary);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.scenario-desc {
|
||||
color: #8a8d93;
|
||||
font-size: 14px;
|
||||
color: var(--employee-primary);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.scenario-persona {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-top: 2px;
|
||||
margin-top: 14px;
|
||||
padding: 14px;
|
||||
border: 1px solid #ffd9dc;
|
||||
border-radius: 16px;
|
||||
background: #fff8f8;
|
||||
border: 0;
|
||||
border-radius: 20px;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.persona-avatar {
|
||||
@@ -1930,15 +1971,15 @@ onShow(() => {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
margin-top: 2px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.prep-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 16px;
|
||||
border: 1px solid #f0f1f3;
|
||||
gap: 7px;
|
||||
padding: 15px 16px;
|
||||
border: 0;
|
||||
border-radius: 20px;
|
||||
background: #ffffff;
|
||||
}
|
||||
@@ -1950,8 +1991,7 @@ onShow(() => {
|
||||
}
|
||||
|
||||
.prep-section-redline {
|
||||
border-color: #ffe0e2;
|
||||
background: #fffafa;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.prep-section-redline .prep-section-title,
|
||||
@@ -1964,7 +2004,7 @@ onShow(() => {
|
||||
margin-top: 0;
|
||||
color: #858990;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
line-height: 1.42;
|
||||
}
|
||||
|
||||
.prep-mark {
|
||||
|
||||
Reference in New Issue
Block a user