Deploy / deploy (push) Canceled after 0s
只改界面文字和注释:网址 /problemset、接口、表名、字段名都不动,老链接照常能打开。 设计稿页名「题单重设计」和已执行的迁移文件保留原样。 线上 3 个成就(题单收藏家 / 入门 / 通关)已同步改名。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
625 lines
20 KiB
Vue
625 lines
20 KiB
Vue
<script setup lang="ts">
|
||
import { useThemeVars } from 'naive-ui'
|
||
import { storeToRefs } from 'pinia'
|
||
import { useCodeStore } from 'oj/store/code'
|
||
import { isFlowchartPass, useFlowchartStore } from 'oj/store/flowchart'
|
||
import { useProblemStore } from 'oj/store/problem'
|
||
import { useLessonStore } from 'shared/store/lesson'
|
||
import { useUserStore } from 'shared/store/user'
|
||
import { sortFlowchartCriteria } from 'utils/constants'
|
||
import { parseTime } from 'utils/functions'
|
||
import { mermaidOrder, signature } from 'shared/components/FlowchartEditor/model'
|
||
import FlowchartNote from 'shared/components/FlowchartNote.vue'
|
||
import { caseReport, flowchartVerdict } from 'utils/flowchart'
|
||
|
||
/**
|
||
* 「结果」页签里流程图作业的那一块:AI 在评 / 评完了几分 / 历次分数 / 评语和改进建议。
|
||
*
|
||
* 原来评分详情是提交按钮旁一个 1000px 的弹框:盖住画布,学生没法一边看改进建议一边改图;
|
||
* 翻历次评分要一页一页点分页器。现在摆在左栏,画布整列都在右边(设计文档第 6 节)。
|
||
*
|
||
*/
|
||
|
||
const MyFlowchart = defineAsyncComponent(() => import('./MyFlowchart.vue'))
|
||
|
||
const flowchartStore = useFlowchartStore()
|
||
const { phase, scores, hiddenCount, selectedId, details, detailLoading, scoresFailed, editor } = storeToRefs(flowchartStore)
|
||
const problemStore = useProblemStore()
|
||
const codeStore = useCodeStore()
|
||
const userStore = useUserStore()
|
||
|
||
// 历次分数原来只由题面(ProblemContent)去读。手机上题面是另一个页签、不切过去就不挂载,
|
||
// 直接打开 ?tab=result 的话「流程图点评」一条都没有。这里自己也读一次(同一道题只读一次)
|
||
watch(
|
||
() => [problemStore.problem?.id, userStore.isAuthed] as const,
|
||
([id, authed]) => {
|
||
if (id && authed) flowchartStore.ensureLoaded()
|
||
},
|
||
{ immediate: true },
|
||
)
|
||
const message = useMessage()
|
||
const theme = useThemeVars()
|
||
// 暗色主题的成功色是浅薄荷绿,上面的白字看不清
|
||
const isDark = useDark()
|
||
const passText = computed(() => (isDark.value ? 'rgba(0, 0, 0, 0.85)' : '#fff'))
|
||
|
||
/** 正在看的那一次:点过哪次就是哪次,否则是最新的 */
|
||
const current = computed(() => {
|
||
const id = selectedId.value ?? scores.value.at(-1)?.id
|
||
return id ? (scores.value.find((row) => row.id === id) ?? null) : null
|
||
})
|
||
const detail = computed(() => (current.value ? details.value[current.value.id] : undefined))
|
||
const viewingLatest = computed(() => selectedId.value === null)
|
||
|
||
// ==================== 这一版的图 ====================
|
||
/**
|
||
* 点历史分数条上的哪一次,就摆哪一次交上去的图(只读的 Mermaid 渲染,不动画布)。
|
||
* 原来的评分弹框左边就是这张图;挪进结果页签时丢了,学生翻到「第 2 次 C 42」
|
||
* 只看得到评语,想不起来那一版画成什么样。图从评语同一个详情接口来,随评语一起现拉。
|
||
*/
|
||
const versionIndex = computed(() => (current.value ? scores.value.findIndex((row) => row.id === current.value!.id) : -1))
|
||
const versionTitle = computed(() => {
|
||
if (!current.value) return ''
|
||
return `第 ${versionIndex.value + 1} 次画的图 · ${parseTime(current.value.createTime, 'MM-DD HH:mm')}`
|
||
})
|
||
/** 收起来之后翻别的次也保持收着:左栏在机房屏上只有六百来像素高 */
|
||
const versionOpen = ref(true)
|
||
const versionZoom = ref(false)
|
||
|
||
// 评完的那次、点到的那次,评语都要现拉(历次分数里只有分数)
|
||
watch(
|
||
() => current.value?.id,
|
||
(id) => {
|
||
if (id && !details.value[id]) flowchartStore.select(selectedId.value)
|
||
},
|
||
{ immediate: true },
|
||
)
|
||
|
||
const criteria = computed(() =>
|
||
sortFlowchartCriteria((detail.value?.aiCriteriaDetails ?? {}) as Record<string, { score: number; max: number; comment?: string }>),
|
||
)
|
||
const suggestions = computed(() =>
|
||
(detail.value?.aiSuggestions ?? '')
|
||
.split('\n')
|
||
.map((line) => line.trim())
|
||
.filter(Boolean),
|
||
)
|
||
|
||
function gradeType(grade: string) {
|
||
if (isFlowchartPass(grade)) return 'success'
|
||
if (grade === 'B') return 'warning'
|
||
return 'error'
|
||
}
|
||
|
||
function percentType(ratio: number) {
|
||
if (ratio >= 0.6) return 'success'
|
||
if (ratio >= 0.4) return 'warning'
|
||
return 'error'
|
||
}
|
||
|
||
// ==================== 载回画布 ====================
|
||
/**
|
||
* 这一版的图(结构树)。2026-10 换成插入式编辑器之前交的是 vue-flow 的格式,读不出来就是
|
||
* null:那些只能看上面的图,载不回画布
|
||
*/
|
||
const versionDoc = computed(() => flowchartStore.versionDoc(detail.value))
|
||
const versionSignature = computed(() => (versionDoc.value ? signature(versionDoc.value) : null))
|
||
|
||
/**
|
||
* 画布和这一版一样不一样。不一样才给「载回画布」(刚交完还没动过时点了什么都不会变,
|
||
* 只会让人以为它坏了);一样才在画布上亮路径。读 getDoc() 会顺带跟踪画布的数据
|
||
*/
|
||
const canvasSame = computed(() => {
|
||
if (!editor.value || !versionSignature.value) return false
|
||
return signature(editor.value.getDoc()) === versionSignature.value
|
||
})
|
||
const canvasDiffers = computed(() => !!editor.value && !!versionSignature.value && !canvasSame.value)
|
||
|
||
function loadToCanvas() {
|
||
if (!editor.value || !versionDoc.value) return
|
||
editor.value.setDoc(versionDoc.value)
|
||
message.success('已载回画布,按 Ctrl+Z 可以撤回')
|
||
}
|
||
|
||
// ==================== 走例子 ====================
|
||
/** 2026-10 起的评法:逐组走一遍。老提交是 null,照旧显示四项分 */
|
||
const report = computed(() => caseReport(detail.value?.aiCriteriaDetails))
|
||
|
||
/**
|
||
* 逐组数据默认收着、一组也不点开(设计稿 FlowNoteFail:学生不关心过了几组,先看哪里错、怎么改)。
|
||
* 换一次提交就收回去
|
||
*/
|
||
const selectedCase = ref<number | null>(null)
|
||
const casesOpen = ref(false)
|
||
watch(
|
||
() => current.value?.id,
|
||
() => {
|
||
selectedCase.value = null
|
||
casesOpen.value = false
|
||
},
|
||
)
|
||
|
||
/**
|
||
* 画布上标什么。没点开哪一组:只把要改的那个框标红、旁边写「要改这个框」,别的框照常;
|
||
* 点开了一组:亮出它走过的路。AI 报的是 Mermaid 的框号(n3),按交上去那一版的结构对回
|
||
* 画布上的框 —— 所以只在画布还是那一版时亮,改过了就不亮(载回画布之后又能亮)
|
||
*/
|
||
watch(
|
||
() => [selectedCase.value, report.value, canvasSame.value, editor.value] as const,
|
||
([index, next, same, instance]) => {
|
||
if (!instance) return
|
||
if (!next || !same || !versionDoc.value) {
|
||
instance.setHighlight(null)
|
||
return
|
||
}
|
||
const order = mermaidOrder(versionDoc.value)
|
||
const toStep = (id: string | null) => (id ? (order[Number(id.slice(1))] ?? null) : null)
|
||
// 要改的框:AI 对着答案指出的(错的根子),没有才退回程序找到的第一处出错的框
|
||
const focus = next.focus ?? next.cases.find((row) => !row.ok)?.wrongAt ?? null
|
||
const row = index === null ? undefined : next.cases[index]
|
||
if (!row) {
|
||
const bad = toStep(focus)
|
||
instance.setHighlight(bad ? { order: [], bad, label: '要改这个框' } : null)
|
||
return
|
||
}
|
||
const bad = row.ok ? null : toStep(focus)
|
||
instance.setHighlight({
|
||
order: row.path.map(toStep).filter((id): id is string => !!id),
|
||
bad,
|
||
})
|
||
},
|
||
// 切回「结果」页签时组件重新挂载,这些值早就齐了,不会再变一次
|
||
{ immediate: true },
|
||
)
|
||
onBeforeUnmount(() => editor.value?.setHighlight(null))
|
||
|
||
/** A / S 之后:照着图写代码。切回学生习惯的那门语言 */
|
||
/**
|
||
* 「照着它写代码」:只画流程图的课里不给(老师这节课只要画),这道题在要写代码的课里就换到
|
||
* 布置的语言,否则回到他习惯的那门
|
||
*/
|
||
const lessonStore = useLessonStore()
|
||
const lessonItem = computed(() => (problemStore.problem ? lessonStore.find(problemStore.problem._id) : undefined))
|
||
const canCode = computed(() => problemStore.codeLanguages.length > 0 && !(lessonItem.value?.draw && !lessonItem.value.code))
|
||
/** 课上这道题要写代码、还没交对:画对了不算做完 */
|
||
const codeStillNeeded = computed(() => !!lessonItem.value && lessonItem.value.code !== null && lessonItem.value.code !== 'accepted')
|
||
function goCode() {
|
||
const language = lessonStore.activity?.language
|
||
if (lessonItem.value?.code && language && problemStore.codeLanguages.includes(language)) problemStore.switchLanguage(language)
|
||
else problemStore.switchLanguage(problemStore.supportedLanguage(codeStore.preferredLanguage()))
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<!-- 设计稿「画流程图:画布占满右栏,AI 点评在左边」「流程图评分中」 -->
|
||
<div class="flowchart-result">
|
||
<div v-if="scoresFailed && !scores.length && phase !== 'evaluating'" class="failed load-failed">
|
||
<span>以前交过的流程图评分没读出来,可能是网络断了一下</span>
|
||
<n-button size="small" secondary @click="flowchartStore.ensureLoaded()">再读一次</n-button>
|
||
</div>
|
||
<n-empty
|
||
v-else-if="!scores.length && phase !== 'evaluating'"
|
||
class="empty"
|
||
description="还没交过流程图。画好之后点「交给 AI 点评」,评分和建议会出现在这里"
|
||
/>
|
||
|
||
<!-- 历次分数连成一条:一眼看出是不是越改越好;正在评的那次是虚线框 -->
|
||
<div v-if="scores.length || phase === 'evaluating'" class="history">
|
||
<span class="history-lead">AI 点评 · 画过 {{ scores.length + (phase === 'evaluating' ? 1 : 0) }} 次:</span>
|
||
<template v-for="(row, index) in scores" :key="row.id">
|
||
<span v-if="index > 0" class="arrow" aria-hidden="true">→</span>
|
||
<button
|
||
type="button"
|
||
class="attempt"
|
||
:class="{
|
||
pass: isFlowchartPass(row.grade),
|
||
active: current?.id === row.id && !(phase === 'evaluating' && viewingLatest),
|
||
}"
|
||
:title="`第 ${index + 1} 次 · ${parseTime(row.createTime, 'MM-DD HH:mm')}`"
|
||
@click="flowchartStore.select(index === scores.length - 1 ? null : row.id)"
|
||
>
|
||
{{ row.grade }}
|
||
</button>
|
||
</template>
|
||
<template v-if="phase === 'evaluating'">
|
||
<span v-if="scores.length" class="arrow" aria-hidden="true">→</span>
|
||
<span class="attempt pending">第 {{ scores.length + 1 }} 次</span>
|
||
</template>
|
||
</div>
|
||
<p v-if="hiddenCount" class="note">另外 {{ hiddenCount }} 次是专题布置之前画的,先藏起来了:在专题里画到 A 或 S,或者布置期结束就解锁</p>
|
||
|
||
<!-- 正在评:评的是最新这一次,所以只在看最新时显示 -->
|
||
<template v-if="phase === 'evaluating' && viewingLatest">
|
||
<div class="grading">
|
||
<n-spin :size="26" />
|
||
<div>
|
||
<div class="grading-title">AI 正在看你的图</div>
|
||
<div class="grading-note">一般 5 秒左右。可以先去看题目,评完这里会亮起来</div>
|
||
</div>
|
||
</div>
|
||
<p class="note">评的时候画布上还能改,改了不影响这一次的分数</p>
|
||
</template>
|
||
|
||
<div v-else-if="phase === 'failed' && viewingLatest" class="failed">AI 这次没评出来,再点一次「交给 AI 点评」试试</div>
|
||
|
||
<section v-else-if="current" class="current">
|
||
<!-- 设计稿 FlowResFail / FlowResPass:等级 + 几组走对,下面逐组 -->
|
||
<div v-if="report" class="headline">
|
||
<span class="grade" :class="gradeType(current.grade)">{{ current.grade }} 级</span>
|
||
<span class="headline-text">{{ flowchartVerdict(report, current.grade) }}</span>
|
||
<span v-if="isFlowchartPass(current.grade)" class="headline-note">{{ codeStillNeeded ? '接着写代码' : '这道题算做完了' }}</span>
|
||
</div>
|
||
<div v-else class="score-line">
|
||
<span class="score" :class="gradeType(current.grade)">{{ current.score }}</span>
|
||
<span class="score-unit">分</span>
|
||
<span class="grade" :class="gradeType(current.grade)">{{ current.grade }} 级</span>
|
||
<span v-if="detail?.aiFeedback || isFlowchartPass(current.grade)" class="feedback">
|
||
{{ detail?.aiFeedback ?? '' }}{{ isFlowchartPass(current.grade) ? (codeStillNeeded ? ' 接着写代码吧。' : ' 这道题算做完了。') : '' }}
|
||
</span>
|
||
</div>
|
||
|
||
<n-flex v-if="detailLoading && !detail" justify="center" class="pad">
|
||
<n-spin size="small" />
|
||
</n-flex>
|
||
|
||
<template v-if="detail && report">
|
||
<n-button v-if="isFlowchartPass(current.grade) && canCode" class="code-btn" type="primary" size="large" @click="goCode"> 照着它写代码 → </n-button>
|
||
<FlowchartNote
|
||
v-model:selected="selectedCase"
|
||
v-model:open="casesOpen"
|
||
:report="report"
|
||
:feedback="detail.aiFeedback"
|
||
:suggestions="detail.aiSuggestions"
|
||
:previous="detail.previousCases"
|
||
class="block"
|
||
/>
|
||
</template>
|
||
|
||
<template v-if="detail">
|
||
<div v-if="detail.mermaidCode && (!report || canvasDiffers)" class="version">
|
||
<div class="version-head">
|
||
<span class="version-title">{{ versionTitle }}</span>
|
||
<n-button text type="primary" size="small" @click="versionZoom = true"> 放大看 </n-button>
|
||
<n-button text type="primary" size="small" @click="versionOpen = !versionOpen">
|
||
{{ versionOpen ? '收起' : '展开' }}
|
||
</n-button>
|
||
</div>
|
||
<div v-if="versionOpen" class="version-chart" role="button" :aria-label="`放大看${versionTitle}`" @click="versionZoom = true">
|
||
<MyFlowchart compact :code="detail.mermaidCode" />
|
||
</div>
|
||
<n-modal v-model:show="versionZoom" preset="card" :title="versionTitle" :style="{ maxWidth: '900px' }">
|
||
<MyFlowchart :code="detail.mermaidCode" />
|
||
</n-modal>
|
||
</div>
|
||
|
||
<div v-if="!report && criteria.length" class="criteria">
|
||
<div v-for="[name, item] in criteria" :key="name" class="criterion">
|
||
<div class="criterion-head">
|
||
<b>{{ name }}</b>
|
||
<span class="criterion-score">{{ item.score || 0 }} / {{ item.max }}</span>
|
||
</div>
|
||
<div class="bar">
|
||
<div class="bar-fill" :class="percentType(item.score / item.max)" :style="{ width: `${Math.min(100, ((item.score || 0) / item.max) * 100)}%` }" />
|
||
</div>
|
||
<div v-if="item.comment" class="comment">{{ item.comment }}</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div v-if="!report && suggestions.length" class="suggestions">
|
||
<div class="suggestions-title">还可以改进</div>
|
||
<div v-for="(line, index) in suggestions" :key="index" class="suggestion">
|
||
<span v-if="index === 0" class="tag">重点</span>
|
||
<span>{{ line }}</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="actions">
|
||
<n-button v-if="!report && isFlowchartPass(current.grade) && canCode" type="primary" size="large" @click="goCode"> 照着它写代码 → </n-button>
|
||
<n-button v-if="canvasDiffers" secondary @click="loadToCanvas">把这一版载回画布</n-button>
|
||
</div>
|
||
</template>
|
||
</section>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.flowchart-result {
|
||
padding-bottom: 16px;
|
||
}
|
||
|
||
.empty {
|
||
margin-top: 48px;
|
||
}
|
||
|
||
.note {
|
||
font-size: 13px;
|
||
color: v-bind('theme.textColor3');
|
||
margin: 0 0 10px;
|
||
}
|
||
|
||
.pad {
|
||
padding: 16px 0;
|
||
}
|
||
|
||
.history {
|
||
display: flex;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
gap: 6px;
|
||
margin-bottom: 12px;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.history-lead {
|
||
color: v-bind('theme.textColor2');
|
||
}
|
||
|
||
.arrow {
|
||
color: v-bind('theme.textColor3');
|
||
}
|
||
|
||
.attempt {
|
||
height: 26px;
|
||
box-sizing: border-box;
|
||
padding: 0 9px;
|
||
border-radius: 13px;
|
||
border: 1px solid transparent;
|
||
background-color: rgba(128, 128, 128, 0.12);
|
||
color: v-bind('theme.textColor2');
|
||
font: inherit;
|
||
font-size: 13px;
|
||
font-variant-numeric: tabular-nums;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.attempt.active {
|
||
background-color: v-bind('theme.textColor2');
|
||
color: v-bind('theme.cardColor');
|
||
font-weight: 600;
|
||
}
|
||
|
||
.attempt.pass.active {
|
||
background-color: v-bind('theme.successColor');
|
||
color: v-bind('passText');
|
||
}
|
||
|
||
.attempt.pending {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
cursor: default;
|
||
background: transparent;
|
||
border: 1px dashed v-bind('theme.successColor');
|
||
color: v-bind('theme.successColorPressed');
|
||
}
|
||
|
||
.grading {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 14px;
|
||
padding: 18px;
|
||
margin-bottom: 10px;
|
||
border-radius: 8px;
|
||
background-color: rgba(24, 160, 88, 0.07);
|
||
border: 1px solid rgba(24, 160, 88, 0.25);
|
||
}
|
||
|
||
.grading-title {
|
||
font-size: 16px;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.grading-note {
|
||
margin-top: 2px;
|
||
font-size: 13px;
|
||
color: v-bind('theme.textColor2');
|
||
}
|
||
|
||
.failed {
|
||
padding: 12px 14px;
|
||
border-radius: 6px;
|
||
background-color: rgba(240, 160, 32, 0.1);
|
||
border: 1px solid rgba(240, 160, 32, 0.35);
|
||
}
|
||
|
||
.load-failed {
|
||
display: flex;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
gap: 8px 12px;
|
||
}
|
||
|
||
/* 这一版的图:和题面里「你画的流程图」同一个样子,点一下放大 */
|
||
.version {
|
||
margin-bottom: 14px;
|
||
}
|
||
|
||
.version-head {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
margin-bottom: 6px;
|
||
}
|
||
|
||
.version-title {
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.version-chart {
|
||
cursor: zoom-in;
|
||
border-radius: 6px;
|
||
background-color: rgba(128, 128, 128, 0.06);
|
||
padding: 8px;
|
||
}
|
||
|
||
.score-line {
|
||
display: flex;
|
||
align-items: baseline;
|
||
flex-wrap: wrap;
|
||
gap: 4px 8px;
|
||
margin-bottom: 14px;
|
||
}
|
||
|
||
.score {
|
||
font-size: 34px;
|
||
font-weight: 700;
|
||
line-height: 1;
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
|
||
.score.success {
|
||
color: v-bind('theme.successColorPressed');
|
||
}
|
||
|
||
.score.warning {
|
||
color: v-bind('theme.warningColorPressed');
|
||
}
|
||
|
||
.score.error {
|
||
color: v-bind('theme.errorColorPressed');
|
||
}
|
||
|
||
.score-unit {
|
||
color: v-bind('theme.textColor2');
|
||
}
|
||
|
||
.grade {
|
||
align-self: center;
|
||
padding: 2px 8px;
|
||
border-radius: 4px;
|
||
font-size: 13px;
|
||
font-weight: 700;
|
||
color: #fff;
|
||
background-color: v-bind('theme.errorColor');
|
||
}
|
||
|
||
.grade.success {
|
||
background-color: v-bind('theme.successColor');
|
||
color: v-bind('passText');
|
||
}
|
||
|
||
.grade.warning {
|
||
background-color: v-bind('theme.warningColor');
|
||
}
|
||
|
||
.feedback {
|
||
flex: 1 1 200px;
|
||
align-self: center;
|
||
margin-left: 6px;
|
||
font-size: 14px;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.headline {
|
||
display: flex;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
gap: 4px 10px;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.headline .grade {
|
||
font-size: 16px;
|
||
padding: 2px 10px;
|
||
}
|
||
|
||
.headline-text {
|
||
font-size: 22px;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.headline-note {
|
||
font-size: 14px;
|
||
color: v-bind('theme.textColor2');
|
||
}
|
||
|
||
.block {
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.code-btn {
|
||
margin-bottom: 14px;
|
||
}
|
||
|
||
/* 四项得分:两列,每项一条进度 */
|
||
.criteria {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||
gap: 12px 20px;
|
||
margin-bottom: 14px;
|
||
}
|
||
|
||
.criterion-head {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.criterion-score {
|
||
font-variant-numeric: tabular-nums;
|
||
color: v-bind('theme.textColor2');
|
||
}
|
||
|
||
.bar {
|
||
height: 6px;
|
||
margin: 4px 0;
|
||
border-radius: 3px;
|
||
background-color: rgba(128, 128, 128, 0.14);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.bar-fill {
|
||
height: 100%;
|
||
border-radius: 3px;
|
||
background-color: v-bind('theme.successColor');
|
||
}
|
||
|
||
.bar-fill.warning {
|
||
background-color: v-bind('theme.warningColor');
|
||
}
|
||
|
||
.bar-fill.error {
|
||
background-color: v-bind('theme.errorColor');
|
||
}
|
||
|
||
.comment {
|
||
font-size: 13px;
|
||
color: v-bind('theme.textColor2');
|
||
}
|
||
|
||
.suggestions {
|
||
padding: 12px 14px;
|
||
margin-bottom: 14px;
|
||
border-radius: 6px;
|
||
background-color: rgba(128, 128, 128, 0.06);
|
||
border: 1px solid v-bind('theme.dividerColor');
|
||
}
|
||
|
||
.suggestions-title {
|
||
font-weight: 600;
|
||
margin-bottom: 6px;
|
||
}
|
||
|
||
.suggestion {
|
||
display: flex;
|
||
align-items: baseline;
|
||
gap: 8px;
|
||
line-height: 1.7;
|
||
}
|
||
|
||
.tag {
|
||
flex: none;
|
||
padding: 0 6px;
|
||
border-radius: 3px;
|
||
font-size: 12px;
|
||
background-color: rgba(240, 160, 32, 0.16);
|
||
color: v-bind('theme.warningColorPressed');
|
||
}
|
||
|
||
.actions {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
}
|
||
</style>
|