style: 颜色统一为CSS变量并优化复习卡片交互

This commit is contained in:
2026-09-13 22:11:23 +08:00
parent cc4c20e77b
commit fae27e7c20
12 changed files with 372 additions and 100 deletions
+19 -4
View File
@@ -85,12 +85,27 @@ lpt-fe/src/
### 按钮规范
- 页面主操作 / 卡片 CTA`size="large"`,如 Study 开始任务、TaskForm 保存、Welcome 快速入口。
- 区块工具栏 / 页内操作:默认尺寸,如 Review 刷新、ReviewRecall 面板操作、历史记录
- 表格/列表行内操作:`size="small"`,主操作实底 `type="success"`,次操作 `text`;如 Review 卡片中「回忆复习」为主、「查看记录」为次
- 主操作 / 卡片 CTA`size="large"`(实测 40px 高、15px 字),如 Study 开始任务、Welcome 快速入口。
- 区块工具栏 / 页内操作:默认尺寸32px 高、14px 字),如 Review 刷新、ReviewRecall 面板操作。
- 表格/列表行内操作:`size="small"`(28px 高、13px 字;不得再调小)
- 弹窗 footer:默认尺寸;确认按钮 `type="success"`,取消按钮默认;危险操作统一 `type="danger"`
- 正向/保存语义统一 `success`,次级编辑语义可用 `primary`,不要在同一功能上混用。
- 窄屏(`max-width: 768px`)操作区按钮纵向全宽,全局规则位于 `src/assets/main.css``.action-row / .edit-actions / .fragment-edit-actions / .task-actions``.task-actions` 中主操作在上、次操作在下(如 Review 的回忆复习/查看记录)
- 窄屏(`max-width: 768px`)操作区按钮纵向全宽,全局规则位于 `src/assets/main.css``.action-row / .edit-actions / .fragment-edit-actions / .task-actions`
### 颜色与主题
- 禁止在组件内写颜色字面量:新增颜色先加到 `src/assets/base.css``:root`,组件只用 `var(--x)`。语义色已提供 `--accent-warning / --accent-warning-soft / --accent-warning-strong / --accent-success-soft / --accent-success-softer / --accent-success-strong / --accent-success-text`
- Element Plus 主题色在 `src/assets/main.css``:root` 覆盖(`--el-color-success` / `--el-color-primary` 指向 `--green-700`),全站按钮/标签/开关自动生效,不要在组件里逐个覆盖 EP 组件色。
- `src/main.ts` 中自定义样式必须写在 `import 'element-plus/dist/index.css'` **之后**,否则主题覆盖会被 EP 覆盖。
- 实底主色统一用满足 WCAG AA(白字对比度 ≥4.5:1)的 `--green-700``--green-600` 仅用于文字/链接。
- 颜色字面量不得出现在 JS/模板表达式里(如 `:color="'#xxx'"` 引 CSS 变量会失效),需要动态色时保留字面量或改用 CSS 类。
### 卡片列表交互
- 卡片列表的卡片整体可点击进入详情:外层加 `role="link" tabindex="0"``aria-label`,并绑定 `@click``@keydown.enter/@keydown.space.prevent`
- 卡片内的附加按钮必须 `@click.stop`,避免被卡片点击吞掉(如 Review 的「回忆复习」)。
- 卡片操作使用右对齐操作簇(`justify-content: flex-end`),**禁止用 `space-between` 把两个按钮拉到卡片两端**。
- 加载失败必须区分于空数据:保留一个 `loadFailed` 状态,失败时给出可读提示 + 重试按钮,重试前先重置该状态;不要只靠 `finally` 关 loading 导致空白页。
### 移动端适配
+23 -3
View File
@@ -41,12 +41,14 @@ test.describe('Review page', () => {
await page.waitForSelector('.review-page', { timeout: 10_000 });
});
test('displays summary metrics and task table', async ({ page }) => {
test('displays summary metrics and task list', async ({ page }) => {
await expect(page.locator('.metric-card')).toHaveCount(3);
// Check metric values
await expect(page.locator('.metric-card').first().locator('strong')).toContainText('2');
// Check table rows
await expect(page.locator('.el-table__row')).toHaveCount(2);
// 任务以卡片列表展示(不再是 el-table)
await expect(page.locator('.task-card')).toHaveCount(2);
await expect(page.getByText('学习 Vue3')).toBeVisible();
await expect(page.getByRole('button', { name: '回忆复习' })).toHaveCount(2);
});
test('shows empty state when no tasks', async ({ page }) => {
@@ -61,5 +63,23 @@ test.describe('Review page', () => {
await page.goto('/review');
await page.waitForSelector('.review-page', { timeout: 10_000 });
await expect(page.locator('.metric-card').first().locator('strong')).toContainText('0');
await expect(page.getByText('暂无复习任务')).toBeVisible();
});
test('接口失败时提示加载失败而不是显示空列表', async ({ page }) => {
await page.route('**/api/review/tasks**', (route) =>
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ code: 500, message: '服务异常', data: null }),
}),
);
await page.goto('/review');
await page.waitForSelector('.review-page', { timeout: 10_000 });
await expect(page.getByText('复习数据加载失败,请稍后重试')).toBeVisible();
await expect(page.getByRole('button', { name: '重新加载' })).toBeVisible();
await expect(page.getByText('暂无复习任务')).toHaveCount(0);
});
});
@@ -0,0 +1,143 @@
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { flushPromises, mount } from '@vue/test-utils'
import ElementPlus from 'element-plus'
import { createMemoryHistory, createRouter } from 'vue-router'
import Review from '@/components/Review.vue'
import { getReviewTaskStats } from '@/api/review'
vi.mock('@/api/review', () => ({
getReviewTaskStats: vi.fn(),
getReviewFeed: vi.fn(),
getTaskReview: vi.fn(),
}))
const tasks = [
{
taskNum: 'T001',
taskName: '学习 Vue3 组合式 API',
reportCount: 3,
fragmentCount: 5,
effectiveTime: 9000,
},
{
taskNum: 'T002',
taskName: 'TypeScript 类型体操',
reportCount: 1,
fragmentCount: 2,
effectiveTime: 4500,
},
]
/**
* 交互式集成测试:真实挂载 Review 总览页,
* 覆盖卡片进入详情、回忆复习跳转、加载失败提示三条用户可见路径。
*/
describe('Review 交互流程(集成)', () => {
beforeEach(() => {
vi.clearAllMocks()
})
const mountReview = async () => {
const router = createRouter({
history: createMemoryHistory(),
routes: [
{ path: '/review', name: 'review', component: Review },
{ path: '/review/detail/task/:taskNum', name: 'review-detail', component: { template: '<div />' } },
{ path: '/review/recall/:taskNum', name: 'review-recall', component: { template: '<div />' } },
],
})
await router.push('/review')
await router.isReady()
const wrapper = mount(Review, {
global: {
plugins: [ElementPlus, router],
// el-tag 在 jsdom + VTU 下 vnode mounted 钩子崩溃(EP 2.8 已知问题),纯视觉组件,stub 掉
stubs: { ElTag: true },
},
})
await flushPromises()
return { wrapper, router }
}
it('点击卡片空白处进入该任务的复习详情', async () => {
vi.mocked(getReviewTaskStats).mockResolvedValue({ code: 200, data: tasks })
const { wrapper, router } = await mountReview()
expect(wrapper.findAll('.task-card')).toHaveLength(2)
await wrapper.findAll('.task-card')[1].trigger('click')
await flushPromises()
expect(router.currentRoute.value.path).toBe('/review/detail/task/T002')
})
it('键盘 Enter 与 Space 也能进入详情', async () => {
vi.mocked(getReviewTaskStats).mockResolvedValue({ code: 200, data: tasks })
const { wrapper, router } = await mountReview()
await wrapper.findAll('.task-card')[0].trigger('keydown', { key: 'Enter' })
await flushPromises()
expect(router.currentRoute.value.path).toBe('/review/detail/task/T001')
await router.push('/review')
await flushPromises()
await wrapper.findAll('.task-card')[0].trigger('keydown', { key: ' ' })
await flushPromises()
expect(router.currentRoute.value.path).toBe('/review/detail/task/T001')
})
it('点击「回忆复习」进入回忆页,且不被卡片点击覆盖', async () => {
vi.mocked(getReviewTaskStats).mockResolvedValue({ code: 200, data: tasks })
const { wrapper, router } = await mountReview()
await wrapper.find('.recall-button').trigger('click')
await flushPromises()
expect(router.currentRoute.value.path).toBe('/review/recall/T001')
})
it('卡片暴露 role 与 aria-label,具备可访问语义', async () => {
vi.mocked(getReviewTaskStats).mockResolvedValue({ code: 200, data: tasks })
const { wrapper } = await mountReview()
const card = wrapper.findAll('.task-card')[0]
expect(card.attributes('role')).toBe('link')
expect(card.attributes('tabindex')).toBe('0')
expect(card.attributes('aria-label')).toContain('学习 Vue3 组合式 API')
})
it('接口失败时提示加载失败并提供重试,而不是显示空列表', async () => {
vi.mocked(getReviewTaskStats).mockRejectedValue({ code: 500, message: '服务异常' })
const { wrapper } = await mountReview()
expect(wrapper.text()).toContain('复习数据加载失败,请稍后重试')
expect(wrapper.text()).not.toContain('暂无复习任务')
expect(wrapper.find('button').exists()).toBe(true)
})
it('重试按钮会重新请求接口,成功后展示卡片列表', async () => {
vi.mocked(getReviewTaskStats)
.mockRejectedValueOnce({ code: 500, message: '服务异常' })
.mockResolvedValueOnce({ code: 200, data: tasks })
const { wrapper } = await mountReview()
expect(wrapper.text()).toContain('复习数据加载失败,请稍后重试')
const retry = wrapper.findAll('button').find(b => b.text().includes('重新加载'))
expect(retry).toBeTruthy()
await retry!.trigger('click')
await flushPromises()
expect(getReviewTaskStats).toHaveBeenCalledTimes(2)
expect(wrapper.findAll('.task-card')).toHaveLength(2)
expect(wrapper.text()).not.toContain('复习数据加载失败')
})
it('无任务时展示空列表文案,不显示失败提示', async () => {
vi.mocked(getReviewTaskStats).mockResolvedValue({ code: 200, data: [] })
const { wrapper } = await mountReview()
expect(wrapper.text()).toContain('暂无复习任务')
expect(wrapper.text()).not.toContain('复习数据加载失败')
})
})
-41
View File
@@ -1,41 +0,0 @@
:root {
--green-900: #123d2d;
--green-800: #1a5a42;
--green-700: #237556;
--green-600: #2f8f68;
--green-500: #43a879;
--green-200: #d9f1e5;
--green-100: #eef9f3;
--surface: #f7fbf8;
--surface-strong: #ffffff;
--text-primary: #1b2a23;
--text-secondary: #4b6156;
--border-soft: #d6e6dc;
--shadow-soft: 0 14px 30px rgba(19, 61, 45, 0.09);
--shadow-strong: 0 20px 45px rgba(19, 61, 45, 0.16);
}
*,
*::before,
*::after {
box-sizing: border-box;
}
html,
body,
#app {
min-height: 100%;
}
body {
margin: 0;
color: var(--text-primary);
background:
radial-gradient(circle at 12% 18%, rgba(67, 168, 121, 0.2), transparent 32%),
radial-gradient(circle at 85% 0%, rgba(35, 117, 86, 0.14), transparent 28%),
linear-gradient(165deg, #eef9f3 0%, #f8fcfa 50%, #edf7f1 100%);
font-family: "Source Han Sans SC", "Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif;
line-height: 1.6;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
+107 -1
View File
@@ -1,4 +1,84 @@
@import "./base.css";
:root {
--green-900: #123d2d;
--green-800: #1a5a42;
--green-700: #237556;
--green-600: #2f8f68;
--green-500: #43a879;
--green-100: #eef9f3;
--surface: #f7fbf8;
--surface-strong: #ffffff;
--text-primary: #1b2a23;
--text-secondary: #4b6156;
--border-soft: #d6e6dc;
--shadow-soft: 0 14px 30px rgba(19, 61, 45, 0.09);
--shadow-strong: 0 20px 45px rgba(19, 61, 45, 0.16);
/* 残片/提醒语义色(对应 EP warning 色系,避免各组件硬编码) */
--accent-warning: #e6a23c;
--accent-warning-soft: #fff8e1;
--accent-warning-strong: #b8860b;
/* 成功/命中语义的浅底与深字(统计卡片、标签共用) */
--accent-success-soft: #e8f5e9;
--accent-success-softer: #f1f8e9;
--accent-success-strong: #2e7d32;
--accent-success-text: #856404;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
html,
body,
#app {
min-height: 100%;
}
body {
margin: 0;
color: var(--text-primary);
background:
radial-gradient(circle at 12% 18%, rgba(67, 168, 121, 0.2), transparent 32%),
radial-gradient(circle at 85% 0%, rgba(35, 117, 86, 0.14), transparent 28%),
linear-gradient(165deg, #eef9f3 0%, #f8fcfa 50%, #edf7f1 100%);
font-family: "Source Han Sans SC", "Noto Sans SC", "PingFang SC", "Microsoft YaHei", sans-serif;
line-height: 1.6;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* ------------------------------------------------------------------
* Element Plus 主题对齐
*
* EP 默认 success 是黄绿 #67c23a,与本站墨绿主题色相不一致,
* 且白字压在其上对比度仅 2.24:1(低于 WCAG AA 4.5:1)。
* 这里统一改写 EP 的 success / primary 色阶,使全站按钮、标签、
* 开关等组件自动使用主题绿。
*
* 注意:本文件在 main.ts 中必须导入在 element-plus 样式之后,
* 否则同特异度下会被 EP 默认值覆盖。
* ------------------------------------------------------------------ */
:root {
/* primary 与 success 同源:本站正向语义统一走 success,primary 仅次级编辑 */
--el-color-primary: var(--green-700);
--el-color-primary-light-3: #4a8f74;
--el-color-primary-light-5: #6ba98f;
--el-color-primary-light-7: #9cc7b5;
--el-color-primary-light-8: #bcd9cc;
--el-color-primary-light-9: #eef9f3;
--el-color-primary-dark-2: var(--green-800);
--el-color-success: var(--green-700);
--el-color-success-light-3: #4a8f74;
--el-color-success-light-5: #6ba98f;
--el-color-success-light-7: #9cc7b5;
--el-color-success-light-8: #bcd9cc;
--el-color-success-light-9: #eef9f3;
--el-color-success-dark-2: var(--green-800);
}
#app {
width: 100%;
@@ -35,6 +115,32 @@ a:hover {
box-shadow: var(--shadow-soft);
}
/* ------------------------------------------------------------------
* 按钮尺寸收敛
*
* EP 默认 large / default / small 三档实测高度为 32 / 32 / 24px
* large 与 default 在视觉上无法区分,行内 small 的 24px 又低于触屏
* 舒适点击区。这里保持两个视觉层级:
* 主操作 large → 40px 高
* 常规/行内 default、small → 至少 28px 高(保留 small 的紧凑感)
* 字号统一 13px 上限,避免行内按钮字号过小(原为 12px)。
* ------------------------------------------------------------------ */
.el-button--large {
--el-button-size: 40px;
padding: 0 20px;
font-size: 15px;
}
.el-button--small {
--el-button-size: 28px;
font-size: 13px;
}
.el-button--small.is-text,
.el-button--small.is-link {
padding: 0 8px;
}
/* 按钮操作区统一规范:窄屏下操作按钮纵向全宽 */
.action-row .el-button,
.edit-actions .el-button,
+2 -2
View File
@@ -82,7 +82,7 @@ const rendered = computed(() => {
margin: 0.5em 0;
padding: 0.5em 1em;
border-left: 3px solid var(--green-600, #2f8f68);
background: #f1f8e9;
background: var(--accent-success-softer);
color: #555;
}
@@ -121,7 +121,7 @@ const rendered = computed(() => {
}
.markdown-body :deep(th) {
background: #e8f5e9;
background: var(--accent-success-soft);
font-weight: 600;
}
+46 -18
View File
@@ -9,6 +9,7 @@ import {
const router = useRouter();
const loading = ref(false);
const loadFailed = ref(false);
const tasks = ref<ReviewTaskStats[]>([]);
const summary = computed(() => ({
@@ -28,9 +29,13 @@ const formatEffectiveTime = (seconds: number | string): string => {
const loadTasks = async () => {
loading.value = true;
loadFailed.value = false;
try {
const res = await getReviewTaskStats();
tasks.value = res?.data || [];
} catch {
// 请求层已提示具体错误,这里只保证不把失败当作空列表展示
loadFailed.value = true;
} finally {
loading.value = false;
}
@@ -48,9 +53,8 @@ onMounted(() => {
<template>
<section class="review-page">
<div class="table-toolbar">
<span class="toolbar-placeholder"></span>
<el-button type="success" plain :loading="loading" @click="loadTasks">刷新</el-button>
<div class="page-toolbar">
<el-button :loading="loading" @click="loadTasks">刷新</el-button>
</div>
<div class="summary-grid">
@@ -73,6 +77,12 @@ onMounted(() => {
v-for="row in tasks"
:key="row.taskNum"
class="surface-card task-card"
role="link"
tabindex="0"
:aria-label="`查看 ${row.taskName} 的学习记录`"
@click="openTaskReview(row)"
@keydown.enter.prevent="openTaskReview(row)"
@keydown.space.prevent="openTaskReview(row)"
>
<div class="task-head">
<strong>{{ row.taskName }}</strong>
@@ -84,27 +94,26 @@ onMounted(() => {
<span>学习报告数量{{ row.reportCount || 0 }}</span>
<span>学习残片数量{{ row.fragmentCount || 0 }}</span>
</div>
<!-- 卡片整体已可进详情这里只保留回忆复习作为明确的主操作 -->
<div class="task-actions">
<el-button
text
type="success"
size="small"
@click="openTaskReview(row)"
>
查看记录
</el-button>
<el-button
class="recall-button"
type="success"
size="small"
@click="router.push(`/review/recall/${row.taskNum}`)"
@click.stop="router.push(`/review/recall/${row.taskNum}`)"
>
回忆复习
</el-button>
</div>
</article>
</div>
<el-empty v-if="!loading && tasks.length === 0" description="暂无复习任务" />
<el-empty
v-if="!loading && loadFailed"
description="复习数据加载失败,请稍后重试"
>
<el-button type="success" plain @click="loadTasks">重新加载</el-button>
</el-empty>
<el-empty v-else-if="!loading && tasks.length === 0" description="暂无复习任务" />
</section>
</template>
@@ -115,7 +124,7 @@ onMounted(() => {
gap: 14px;
}
.table-toolbar {
.page-toolbar {
display: flex;
justify-content: flex-end;
}
@@ -154,6 +163,19 @@ onMounted(() => {
display: flex;
flex-direction: column;
gap: 10px;
cursor: pointer;
transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}
.task-card:hover {
transform: translateY(-1px);
border-color: var(--green-500, #43a879);
box-shadow: var(--shadow-strong, var(--shadow-soft));
}
.task-card:focus-visible {
outline: 2px solid var(--green-700);
outline-offset: 2px;
}
.task-head strong {
@@ -174,9 +196,9 @@ onMounted(() => {
.task-actions {
display: flex;
justify-content: space-between;
justify-content: flex-end;
align-items: center;
gap: 4px;
gap: 8px;
}
@media (max-width: 900px) {
@@ -186,8 +208,14 @@ onMounted(() => {
}
@media (max-width: 768px) {
.task-actions .recall-button {
order: -1;
/* 窄屏只保留一个主操作,不需要纵向堆叠与顺序调整 */
.task-actions {
flex-direction: row;
}
.task-actions .el-button {
width: auto;
margin-left: auto;
}
}
</style>
+5 -5
View File
@@ -402,7 +402,7 @@ watch(
}
.history-session.is-current {
background: #f1f8e9;
background: var(--accent-success-softer);
margin: 0 -10px;
padding: 14px 10px;
border-radius: 8px;
@@ -456,7 +456,7 @@ watch(
}
.clickable-text.active {
background: #e8f5e9;
background: var(--accent-success-soft);
border-left: 3px solid var(--green-600);
}
@@ -471,13 +471,13 @@ watch(
}
.report-tag {
background: #e8f5e9;
background: var(--accent-success-soft);
color: var(--green-700);
}
.fragment-tag {
background: #fff8e1;
color: #b8860b;
background: var(--accent-warning-soft);
color: var(--accent-warning-strong);
}
.edit-area {
+9 -9
View File
@@ -657,7 +657,7 @@ onMounted(() => {
display: flex;
gap: 24px;
flex-wrap: wrap;
background: linear-gradient(135deg, #f1f8e9 0%, #e8f5e9 100%);
background: linear-gradient(135deg, var(--accent-success-softer) 0%, var(--accent-success-soft) 100%);
}
.stat-item {
@@ -676,8 +676,8 @@ onMounted(() => {
margin-top: 4px;
}
.highlight { color: #2e7d32; }
.matched { color: #2e7d32; }
.highlight { color: var(--accent-success-strong); }
.matched { color: var(--accent-success-strong); }
.missed { color: #c62828; }
.extra { color: #1565c0; }
@@ -710,7 +710,7 @@ onMounted(() => {
.ai-waiting {
font-size: 13px;
color: #e6a23c;
color: var(--accent-warning);
margin: 4px 0 0;
display: flex;
align-items: center;
@@ -722,7 +722,7 @@ onMounted(() => {
width: 14px;
height: 14px;
border: 2px solid #f3d19e;
border-top-color: #e6a23c;
border-top-color: var(--accent-warning);
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@@ -851,7 +851,7 @@ onMounted(() => {
}
.history-item:hover {
background: #f1f8e9;
background: var(--accent-success-softer);
}
.history-time {
@@ -891,12 +891,12 @@ onMounted(() => {
.start-guide {
padding: 14px 22px;
background: linear-gradient(135deg, #fff8e1 0%, #fff3cd 100%);
background: linear-gradient(135deg, var(--accent-warning-soft) 0%, #fff3cd 100%);
border-left: 4px solid #ffc107;
border-radius: 8px;
font-size: 14px;
line-height: 1.6;
color: #856404;
color: var(--accent-success-text);
}
.start-guide p {
@@ -905,7 +905,7 @@ onMounted(() => {
.select-hint {
font-size: 13px;
color: #856404;
color: var(--accent-success-text);
margin: 0 0 8px;
padding: 6px 12px;
background: #fdf6ec;
+5 -5
View File
@@ -904,7 +904,7 @@ defineExpose({
font-size: 12px;
font-weight: 700;
color: var(--green-700);
background: #e8f5e9;
background: var(--accent-success-soft);
padding: 2px 8px;
border-radius: 4px;
}
@@ -931,7 +931,7 @@ defineExpose({
font-size: 12px;
font-weight: 700;
color: var(--green-700);
background: #e8f5e9;
background: var(--accent-success-soft);
padding: 2px 8px;
border-radius: 4px;
}
@@ -968,7 +968,7 @@ defineExpose({
.ai-waiting {
font-size: 13px;
color: #e6a23c;
color: var(--accent-warning);
margin: 0 0 8px;
display: flex;
align-items: center;
@@ -980,7 +980,7 @@ defineExpose({
width: 14px;
height: 14px;
border: 2px solid #f3d19e;
border-top-color: #e6a23c;
border-top-color: var(--accent-warning);
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@@ -1238,7 +1238,7 @@ defineExpose({
flex-shrink: 0;
font-size: 11px;
color: var(--green-700);
background: #e8f5e9;
background: var(--accent-success-soft);
padding: 2px 8px;
border-radius: 4px;
white-space: nowrap;
+9 -9
View File
@@ -461,7 +461,7 @@ onBeforeUnmount(() => {
}
.dot-fragment {
background: #e6a23c;
background: var(--accent-warning);
}
.review-scroll-track {
@@ -485,7 +485,7 @@ onBeforeUnmount(() => {
padding: 8px 16px;
background: var(--surface-strong);
border: 1px solid var(--border-soft);
border-left: 3px solid #e6a23c;
border-left: 3px solid var(--accent-warning);
border-radius: 6px;
cursor: pointer;
transition: background 0.2s, box-shadow 0.2s;
@@ -495,7 +495,7 @@ onBeforeUnmount(() => {
.review-chip:hover {
box-shadow: var(--shadow-soft);
background: #fff8e1;
background: var(--accent-warning-soft);
}
.chip-task {
@@ -524,7 +524,7 @@ onBeforeUnmount(() => {
margin: 0 0 14px;
padding: 12px 16px;
border-left: 3px solid var(--green-600);
background: #f1f8e9;
background: var(--accent-success-softer);
border-radius: 0 6px 6px 0;
font-size: 14px;
color: var(--text-primary);
@@ -558,8 +558,8 @@ onBeforeUnmount(() => {
}
.recall-full-item.is-clicked {
background: #fff8e1;
border-color: #e6a23c;
background: var(--accent-warning-soft);
border-color: var(--accent-warning);
}
.recall-item-tag {
@@ -571,13 +571,13 @@ onBeforeUnmount(() => {
}
.tag-report {
background: #e8f5e9;
background: var(--accent-success-soft);
color: var(--green-700);
}
.tag-fragment {
background: #fff8e1;
color: #b8860b;
background: var(--accent-warning-soft);
color: var(--accent-warning-strong);
}
.recall-item-content {
+4 -3
View File
@@ -1,11 +1,12 @@
import './assets/main.css'
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import 'normalize.css'
// 必须在 EP 样式之后:同特异度下靠加载顺序覆盖 EP 主题变量
import './assets/main.css'
import App from './App.vue'
import router from "@/router"
import 'normalize.css'
const app = createApp(App)
app.use(ElementPlus)