feat(aihr): on-demand summary card with infographic bottom sheet
P0-2 按甲方钦定交互落地(反馈#2:显式按钮+小窗+预期等待):新端点
POST /api/knowledge/summary-card 复用检索命中片段调 chat 模型
(temperature=0,30s超时)生成结构化卡{title,steps,objections,scripts,
reminders},无命中/无模型/失败均明确报错可重试,不兜假数据;检索
底层拆出 dbHits() 避免总结卡重复触发轻概要 LLM。移动端:概要卡下
「✨生成总结卡·约10秒生成」按钮→底部弹层(拖条+X+深遮罩),四区块
信息图排版(蓝序号四步法/橙块异议/绿引用话术/✓提醒)对照设计基准,
430px 两列 375px 单列;听一遍(独立 TTS 状态)+开始跟读(问练焊接);
SOP 面板配色统一蓝色系。「保存图片」需 html2canvas 依赖本轮不做。
实测:端点4s返回结构化卡;浏览器弹层与 summary-modal 设计图逐区块
一致。
This commit is contained in:
+609
-2
@@ -827,6 +827,13 @@
|
||||
<p v-for="item in sopResult.cautions" :key="item">⚠ {{ item }}</p>
|
||||
</div>
|
||||
<p v-if="sopResult.reference" class="digest-reference">{{ sopResult.reference }}</p>
|
||||
<div class="summary-card-entry">
|
||||
<button class="summary-card-trigger" type="button" :disabled="summaryCardLoading" @click="generateSummaryCard">
|
||||
<span>{{ summaryCardLoading ? '生成中…' : '✨ 生成总结卡' }}</span>
|
||||
<small>约10秒生成</small>
|
||||
</button>
|
||||
<em v-if="summaryCardMessage">{{ summaryCardMessage }}</em>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="sopResult.snippets?.length" class="snippet-list">
|
||||
@@ -850,6 +857,66 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section v-if="summaryCardOpen && summaryCard" class="summary-card-mask" aria-label="总结卡弹层" @click.self="closeSummaryCard">
|
||||
<article class="summary-card-sheet" role="dialog" aria-modal="true">
|
||||
<span class="summary-card-handle" aria-hidden="true"></span>
|
||||
<button class="summary-card-close" type="button" aria-label="关闭总结卡" @click="closeSummaryCard">×</button>
|
||||
<header class="summary-card-head">
|
||||
<h2>{{ summaryCard.title }}</h2>
|
||||
<span>由AI生成</span>
|
||||
</header>
|
||||
|
||||
<div class="summary-card-grid">
|
||||
<section class="summary-card-block is-steps">
|
||||
<h3>沟通四步法</h3>
|
||||
<ol>
|
||||
<li v-for="(step, index) in summaryCard.steps" :key="`${step.label}-${index}`">
|
||||
<em>{{ index + 1 }}</em>
|
||||
<span>
|
||||
<b>{{ step.label }}</b>
|
||||
<small>{{ step.desc }}</small>
|
||||
</span>
|
||||
</li>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<section class="summary-card-block is-objections">
|
||||
<h3>高频异议应对</h3>
|
||||
<div class="objection-list">
|
||||
<p v-for="(item, index) in summaryCard.objections" :key="`${item.concern}-${index}`">
|
||||
<b>{{ item.concern }}</b>
|
||||
<span>{{ item.response }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="summary-card-block is-scripts">
|
||||
<h3>开口话术(可直接使用)</h3>
|
||||
<blockquote v-for="(script, index) in summaryCard.scripts" :key="`${script}-${index}`">{{ script }}</blockquote>
|
||||
</section>
|
||||
|
||||
<section class="summary-card-block is-reminders">
|
||||
<h3>关键提醒</h3>
|
||||
<ul>
|
||||
<li v-for="(item, index) in summaryCard.reminders" :key="`${item}-${index}`">{{ item }}</li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<p v-if="summaryCardMessage" class="summary-card-inline-message">{{ summaryCardMessage }}</p>
|
||||
|
||||
<footer class="summary-card-footer">
|
||||
<div class="summary-card-footer-actions">
|
||||
<button class="listen" type="button" :disabled="cardSpeakLoading" @click="toggleSummaryCardSpeak">
|
||||
{{ cardSpeaking ? '停止播放' : cardSpeakLoading ? '合成中' : '🔊 听一遍' }}
|
||||
</button>
|
||||
<button class="practice" type="button" @click="startPracticeFromSummaryCard">🎤 开始跟读</button>
|
||||
</div>
|
||||
<p>总结卡由 AI 基于检索结果生成,请结合实际情况灵活使用</p>
|
||||
</footer>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<div v-if="toastMessage" class="build-toast" role="status" aria-live="polite">{{ toastMessage }}</div>
|
||||
</main>
|
||||
</template>
|
||||
@@ -1116,6 +1183,24 @@ interface KnowledgeSearchResponse {
|
||||
cautions?: string[];
|
||||
}
|
||||
|
||||
interface SummaryCardStep {
|
||||
label: string;
|
||||
desc: string;
|
||||
}
|
||||
|
||||
interface SummaryCardObjection {
|
||||
concern: string;
|
||||
response: string;
|
||||
}
|
||||
|
||||
interface SummaryCardResponse {
|
||||
title: string;
|
||||
steps: SummaryCardStep[];
|
||||
objections: SummaryCardObjection[];
|
||||
scripts: string[];
|
||||
reminders: string[];
|
||||
}
|
||||
|
||||
interface CaseTag {
|
||||
text: string;
|
||||
type: string;
|
||||
@@ -1301,7 +1386,14 @@ const sopSnippetsOpen = ref(false);
|
||||
const expandedSnippets = ref(new Set<number>());
|
||||
const sopSpeaking = ref(false);
|
||||
const sopSpeakLoading = ref(false);
|
||||
const summaryCard = ref<SummaryCardResponse | null>(null);
|
||||
const summaryCardOpen = ref(false);
|
||||
const summaryCardLoading = ref(false);
|
||||
const summaryCardMessage = ref('');
|
||||
const cardSpeaking = ref(false);
|
||||
const cardSpeakLoading = ref(false);
|
||||
let sopAudio: HTMLAudioElement | null = null;
|
||||
let cardAudio: HTMLAudioElement | null = null;
|
||||
const materialFile = ref<File | null>(null);
|
||||
const materialUploading = ref(false);
|
||||
const materialMessage = ref('');
|
||||
@@ -1593,15 +1685,30 @@ const openSopPanel = () => {
|
||||
sopQuery.value = '';
|
||||
sopResult.value = null;
|
||||
sopMessage.value = '';
|
||||
summaryCard.value = null;
|
||||
summaryCardMessage.value = '';
|
||||
summaryCardOpen.value = false;
|
||||
navigatePath(`${rolePaths[roleKey.value]}/sop`);
|
||||
};
|
||||
|
||||
const stopSummaryCardSpeak = () => {
|
||||
cardAudio?.pause();
|
||||
cardAudio = null;
|
||||
cardSpeaking.value = false;
|
||||
};
|
||||
|
||||
const closeSummaryCard = () => {
|
||||
stopSummaryCardSpeak();
|
||||
summaryCardOpen.value = false;
|
||||
};
|
||||
|
||||
const closeSopPanel = () => {
|
||||
if (sopRecording.value) {
|
||||
sopAutoSearchAfterTranscribe = false;
|
||||
sopMediaRecorder?.stop();
|
||||
}
|
||||
stopSopSpeak();
|
||||
closeSummaryCard();
|
||||
sopPanelOpen.value = false;
|
||||
activeTabLabel.value = sopReturnTabLabel.value;
|
||||
navigatePath(sopReturnPath.value || rolePaths[roleKey.value]);
|
||||
@@ -1740,6 +1847,76 @@ const toggleSopSpeak = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
const generateSummaryCard = async () => {
|
||||
const queryText = (sopQuery.value || sopResult.value?.queryText || '').trim();
|
||||
if (!sopResult.value || !queryText) return;
|
||||
summaryCardLoading.value = true;
|
||||
summaryCardMessage.value = '';
|
||||
closeSummaryCard();
|
||||
const controller = new AbortController();
|
||||
const timeout = window.setTimeout(() => controller.abort(), 35000);
|
||||
try {
|
||||
summaryCard.value = await readApi<SummaryCardResponse>(
|
||||
await fetch('/dev-api/api/knowledge/summary-card', {
|
||||
method: 'POST',
|
||||
headers: apiHeaders(),
|
||||
body: JSON.stringify({ queryText, category: 'sop' }),
|
||||
signal: controller.signal
|
||||
})
|
||||
);
|
||||
summaryCardOpen.value = true;
|
||||
} catch (error) {
|
||||
summaryCardMessage.value =
|
||||
error instanceof DOMException && error.name === 'AbortError'
|
||||
? '总结卡生成超时,请稍后重试'
|
||||
: error instanceof Error
|
||||
? error.message
|
||||
: '总结卡生成失败,请稍后重试';
|
||||
} finally {
|
||||
window.clearTimeout(timeout);
|
||||
summaryCardLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const toggleSummaryCardSpeak = async () => {
|
||||
if (cardSpeaking.value) {
|
||||
stopSummaryCardSpeak();
|
||||
return;
|
||||
}
|
||||
const card = summaryCard.value;
|
||||
if (!card) return;
|
||||
const speech = [
|
||||
card.title,
|
||||
...card.steps.map((step, index) => `${index + 1}、${step.label},${step.desc}`),
|
||||
...card.reminders.map((item) => `提醒,${item}`)
|
||||
].join('。').slice(0, 280);
|
||||
cardSpeakLoading.value = true;
|
||||
try {
|
||||
const data = await readApi<{ audioUrl: string }>(
|
||||
await fetch('/dev-api/api/ai/tts', {
|
||||
method: 'POST',
|
||||
headers: apiHeaders(),
|
||||
body: JSON.stringify({ text: speech, voice: '' })
|
||||
})
|
||||
);
|
||||
if (!summaryCardOpen.value) return;
|
||||
cardAudio = new Audio(data.audioUrl);
|
||||
cardAudio.onended = () => stopSummaryCardSpeak();
|
||||
cardSpeaking.value = true;
|
||||
void cardAudio.play().catch(() => stopSummaryCardSpeak());
|
||||
} catch {
|
||||
summaryCardMessage.value = '语音播报暂不可用(需启用 tts 模型)';
|
||||
} finally {
|
||||
cardSpeakLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const startPracticeFromSummaryCard = () => {
|
||||
closeSummaryCard();
|
||||
closeSopPanel();
|
||||
openUserPracticePanel();
|
||||
};
|
||||
|
||||
const searchSopKnowledge = async () => {
|
||||
const cleaned = sopQuery.value.trim();
|
||||
if (!cleaned) {
|
||||
@@ -1752,7 +1929,11 @@ const searchSopKnowledge = async () => {
|
||||
sopResult.value = null;
|
||||
sopSnippetsOpen.value = false;
|
||||
expandedSnippets.value = new Set();
|
||||
summaryCard.value = null;
|
||||
summaryCardMessage.value = '';
|
||||
summaryCardOpen.value = false;
|
||||
stopSopSpeak();
|
||||
stopSummaryCardSpeak();
|
||||
const controller = new AbortController();
|
||||
// 概要由 chat 模型生成,比纯检索多几秒,超时放宽到 25s
|
||||
const timeout = window.setTimeout(() => controller.abort(), 25000);
|
||||
@@ -2801,6 +2982,7 @@ const applyRouteFromLocation = () => {
|
||||
reviewMessage.value = '';
|
||||
userReviewMessage.value = '';
|
||||
sopPanelOpen.value = false;
|
||||
closeSummaryCard();
|
||||
|
||||
if (route.role === 'user') {
|
||||
if (route.page === 'training') {
|
||||
@@ -3053,6 +3235,8 @@ watch(
|
||||
onBeforeUnmount(() => {
|
||||
setMentorScrollListening(false);
|
||||
cleanupMentorVoicePress();
|
||||
stopSopSpeak();
|
||||
stopSummaryCardSpeak();
|
||||
window.removeEventListener('popstate', applyRouteFromLocation);
|
||||
});
|
||||
const ringStyle = (value: number) => ({ '--value': value } as Record<string, string | number>);
|
||||
@@ -5185,7 +5369,7 @@ p {
|
||||
justify-content: center;
|
||||
overflow-y: auto;
|
||||
background:
|
||||
radial-gradient(circle at 10% 20%, rgba(15, 174, 166, 0.07) 0%, transparent 40%),
|
||||
radial-gradient(circle at 10% 20%, rgba(46, 107, 230, 0.08) 0%, transparent 40%),
|
||||
radial-gradient(circle at 90% 10%, rgba(56, 132, 255, 0.07) 0%, transparent 35%),
|
||||
linear-gradient(180deg, #f5f8fa 0%, #fcfdfe 100%);
|
||||
}
|
||||
@@ -5293,6 +5477,428 @@ p {
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.sop-panel .sop-back {
|
||||
color: #1c56c6;
|
||||
background: rgba(46, 107, 230, 0.1);
|
||||
}
|
||||
|
||||
.sop-panel .sop-form input {
|
||||
border-color: rgba(46, 107, 230, 0.2);
|
||||
}
|
||||
|
||||
.sop-panel .sop-form input:focus {
|
||||
border-color: #2e6be6;
|
||||
box-shadow: 0 0 0 3px rgba(46, 107, 230, 0.14);
|
||||
}
|
||||
|
||||
.sop-panel .sop-voice,
|
||||
.sop-panel .digest-speak,
|
||||
.sop-panel .snippet-toggle,
|
||||
.sop-panel .snippet-list blockquote em,
|
||||
.sop-panel .panel-message {
|
||||
color: #1c56c6;
|
||||
}
|
||||
|
||||
.sop-panel .sop-voice {
|
||||
background: rgba(46, 107, 230, 0.1);
|
||||
border-color: rgba(46, 107, 230, 0.16);
|
||||
}
|
||||
|
||||
.sop-panel .sop-submit {
|
||||
background: linear-gradient(135deg, #2e6be6, #0f58d6);
|
||||
box-shadow: 0 4px 12px rgba(46, 107, 230, 0.18);
|
||||
}
|
||||
|
||||
.sop-panel .digest-card {
|
||||
border-color: rgba(46, 107, 230, 0.24);
|
||||
background: linear-gradient(160deg, rgba(46, 107, 230, 0.09), rgba(46, 107, 230, 0.02));
|
||||
}
|
||||
|
||||
.sop-panel .digest-speak {
|
||||
border-color: rgba(46, 107, 230, 0.36);
|
||||
}
|
||||
|
||||
.sop-panel .snippet-toggle {
|
||||
border-color: rgba(46, 107, 230, 0.35);
|
||||
}
|
||||
|
||||
.summary-card-entry {
|
||||
margin-top: 12px;
|
||||
display: grid;
|
||||
gap: 7px;
|
||||
}
|
||||
|
||||
.summary-card-trigger {
|
||||
width: 100%;
|
||||
min-height: 58px;
|
||||
border-radius: 22px;
|
||||
color: #ffffff;
|
||||
background: linear-gradient(135deg, #2e6be6, #0b55d8);
|
||||
box-shadow: 0 10px 24px rgba(46, 107, 230, 0.26);
|
||||
display: grid;
|
||||
place-items: center;
|
||||
gap: 2px;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.summary-card-trigger span {
|
||||
font-size: 16px;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.summary-card-trigger small {
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
opacity: 0.82;
|
||||
}
|
||||
|
||||
.summary-card-trigger:disabled {
|
||||
opacity: 0.62;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.summary-card-entry em {
|
||||
color: #c25712;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
font-weight: 750;
|
||||
}
|
||||
|
||||
.summary-card-mask {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 180;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
background: rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
|
||||
.summary-card-sheet {
|
||||
position: relative;
|
||||
width: min(100vw, 860px);
|
||||
height: 88vh;
|
||||
max-height: 88vh;
|
||||
padding: 22px 16px 0;
|
||||
border-radius: 24px 24px 0 0;
|
||||
background: #f8fbff;
|
||||
box-shadow: 0 -18px 44px rgba(13, 31, 68, 0.24);
|
||||
overflow-y: auto;
|
||||
animation: summary-sheet-in 220ms ease-out;
|
||||
}
|
||||
|
||||
@keyframes summary-sheet-in {
|
||||
from {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
to {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.summary-card-handle {
|
||||
position: absolute;
|
||||
top: 9px;
|
||||
left: 50%;
|
||||
width: 66px;
|
||||
height: 6px;
|
||||
border-radius: 999px;
|
||||
background: rgba(25, 42, 70, 0.18);
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.summary-card-close {
|
||||
position: absolute;
|
||||
top: 18px;
|
||||
right: 16px;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid rgba(21, 39, 70, 0.16);
|
||||
color: #182842;
|
||||
background: #ffffff;
|
||||
font-size: 30px;
|
||||
line-height: 1;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.summary-card-head {
|
||||
min-height: 46px;
|
||||
padding: 12px 48px 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.summary-card-head h2 {
|
||||
min-width: 0;
|
||||
margin: 0;
|
||||
color: #101828;
|
||||
font-size: 22px;
|
||||
line-height: 1.2;
|
||||
font-weight: 900;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.summary-card-head span {
|
||||
flex-shrink: 0;
|
||||
padding: 2px 7px;
|
||||
border: 1px solid rgba(46, 107, 230, 0.35);
|
||||
border-radius: 999px;
|
||||
color: #1f62dc;
|
||||
background: #ffffff;
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.summary-card-grid {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
@media (min-width: 400px) {
|
||||
.summary-card-grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
.summary-card-block {
|
||||
min-width: 0;
|
||||
padding: 12px;
|
||||
border: 1px solid rgba(213, 225, 233, 0.8);
|
||||
border-radius: 10px;
|
||||
background: #ffffff;
|
||||
box-shadow: 0 6px 18px rgba(17, 33, 63, 0.06);
|
||||
}
|
||||
|
||||
.summary-card-block h3 {
|
||||
margin: 0 0 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
color: #101828;
|
||||
font-size: 15px;
|
||||
line-height: 1.2;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.summary-card-block h3::before {
|
||||
content: "";
|
||||
width: 5px;
|
||||
height: 22px;
|
||||
border-radius: 999px;
|
||||
background: var(--card-accent);
|
||||
}
|
||||
|
||||
.summary-card-block.is-steps {
|
||||
--card-accent: #2e6be6;
|
||||
}
|
||||
|
||||
.summary-card-block.is-objections {
|
||||
--card-accent: #ff7a1a;
|
||||
}
|
||||
|
||||
.summary-card-block.is-scripts {
|
||||
--card-accent: #18b66a;
|
||||
}
|
||||
|
||||
.summary-card-block.is-reminders {
|
||||
--card-accent: #f6b91a;
|
||||
}
|
||||
|
||||
.summary-card-block ol,
|
||||
.summary-card-block ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.summary-card-block.is-steps ol {
|
||||
display: grid;
|
||||
gap: 7px;
|
||||
}
|
||||
|
||||
.summary-card-block.is-steps li {
|
||||
min-height: 54px;
|
||||
padding: 8px;
|
||||
border-radius: 10px;
|
||||
background: linear-gradient(135deg, rgba(46, 107, 230, 0.11), rgba(46, 107, 230, 0.04));
|
||||
display: grid;
|
||||
grid-template-columns: 34px minmax(0, 1fr);
|
||||
align-items: center;
|
||||
gap: 9px;
|
||||
}
|
||||
|
||||
.summary-card-block.is-steps em {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 50%;
|
||||
color: #ffffff;
|
||||
background: #2e6be6;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.summary-card-block b {
|
||||
display: block;
|
||||
min-width: 0;
|
||||
color: #101828;
|
||||
font-size: 14px;
|
||||
line-height: 1.25;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.summary-card-block small,
|
||||
.summary-card-block span {
|
||||
min-width: 0;
|
||||
color: #475467;
|
||||
font-size: 12px;
|
||||
line-height: 1.35;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.objection-list {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.objection-list p {
|
||||
margin: 0;
|
||||
min-height: 42px;
|
||||
padding: 8px 10px;
|
||||
border-radius: 10px;
|
||||
background: linear-gradient(90deg, rgba(255, 122, 26, 0.14), rgba(255, 122, 26, 0.05));
|
||||
display: grid;
|
||||
grid-template-columns: minmax(72px, 0.55fr) minmax(0, 1fr);
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.summary-card-block.is-scripts blockquote {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
padding: 11px 14px;
|
||||
border: 1px solid rgba(24, 182, 106, 0.18);
|
||||
border-radius: 10px;
|
||||
color: #183b2b;
|
||||
background: rgba(24, 182, 106, 0.08);
|
||||
font-size: 13px;
|
||||
line-height: 1.55;
|
||||
font-weight: 750;
|
||||
}
|
||||
|
||||
.summary-card-block.is-scripts blockquote + blockquote {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.summary-card-block.is-scripts blockquote::before,
|
||||
.summary-card-block.is-scripts blockquote::after {
|
||||
position: absolute;
|
||||
color: rgba(24, 182, 106, 0.32);
|
||||
font-size: 28px;
|
||||
line-height: 1;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.summary-card-block.is-scripts blockquote::before {
|
||||
content: "“";
|
||||
left: 5px;
|
||||
top: 2px;
|
||||
}
|
||||
|
||||
.summary-card-block.is-scripts blockquote::after {
|
||||
content: "”";
|
||||
right: 8px;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.summary-card-block.is-reminders ul {
|
||||
display: grid;
|
||||
gap: 9px;
|
||||
}
|
||||
|
||||
.summary-card-block.is-reminders li {
|
||||
display: grid;
|
||||
grid-template-columns: 20px minmax(0, 1fr);
|
||||
gap: 8px;
|
||||
color: #182842;
|
||||
font-size: 13px;
|
||||
line-height: 1.35;
|
||||
font-weight: 780;
|
||||
}
|
||||
|
||||
.summary-card-block.is-reminders li::before {
|
||||
content: "✓";
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 50%;
|
||||
color: #ffffff;
|
||||
background: #f6b91a;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
font-size: 12px;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.summary-card-inline-message {
|
||||
margin: 10px 0 0;
|
||||
color: #c25712;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
line-height: 1.35;
|
||||
font-weight: 750;
|
||||
}
|
||||
|
||||
.summary-card-footer {
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
margin: 14px -16px 0;
|
||||
padding: 10px 16px calc(10px + env(safe-area-inset-bottom));
|
||||
border-top: 1px solid rgba(213, 225, 233, 0.72);
|
||||
background: rgba(248, 251, 255, 0.96);
|
||||
}
|
||||
|
||||
.summary-card-footer-actions {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.summary-card-footer button {
|
||||
min-height: 52px;
|
||||
border-radius: 12px;
|
||||
color: #ffffff;
|
||||
font-size: 16px;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.summary-card-footer button.listen {
|
||||
background: linear-gradient(135deg, #2e6be6, #0f58d6);
|
||||
box-shadow: 0 8px 18px rgba(46, 107, 230, 0.2);
|
||||
}
|
||||
|
||||
.summary-card-footer button.practice {
|
||||
background: linear-gradient(135deg, #20bf72, #0a9f59);
|
||||
box-shadow: 0 8px 18px rgba(32, 191, 114, 0.2);
|
||||
}
|
||||
|
||||
.summary-card-footer button:disabled {
|
||||
opacity: 0.62;
|
||||
}
|
||||
|
||||
.summary-card-footer p {
|
||||
margin: 8px 0 0;
|
||||
color: #667085;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
line-height: 1.35;
|
||||
font-weight: 650;
|
||||
}
|
||||
|
||||
.tool-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
@@ -5980,7 +6586,8 @@ p {
|
||||
.mentor-avatar img,
|
||||
.mentor-eyelid,
|
||||
.mentor-thinking i,
|
||||
.mentor-waves i {
|
||||
.mentor-waves i,
|
||||
.summary-card-sheet {
|
||||
animation: none !important;
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user