优化登录与任务管理相关页面交互
This commit is contained in:
+146
-50
@@ -1,36 +1,27 @@
|
||||
<script setup lang="ts">
|
||||
import {reactive, ref} from "vue";
|
||||
import {ElMessage, type FormInstance, type FormRules} from "element-plus";
|
||||
import type {InternalRuleItem, Value} from "async-validator";
|
||||
import { reactive, ref } from "vue";
|
||||
import { ElMessage, type FormInstance, type FormRules } from "element-plus";
|
||||
import type { InternalRuleItem, Value } from "async-validator";
|
||||
import router from "@/router";
|
||||
import { login } from "@/api/login";
|
||||
|
||||
interface RegisterData {
|
||||
username: string
|
||||
password: string
|
||||
username: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
const registerData = reactive<RegisterData>({
|
||||
username: "",
|
||||
password: ""
|
||||
password: "",
|
||||
});
|
||||
|
||||
const loginForm = ref<FormInstance>();
|
||||
const loading = ref(false);
|
||||
|
||||
const rules = reactive<FormRules<RegisterData>>({
|
||||
password: [
|
||||
{
|
||||
validator: isNotEmpty,
|
||||
message:"请输入密码!"
|
||||
}
|
||||
],
|
||||
username: [
|
||||
{
|
||||
validator: isNotEmpty,
|
||||
message:"请输入账号!"
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
password: [{ validator: isNotEmpty, message: "请输入密码!" }],
|
||||
username: [{ validator: isNotEmpty, message: "请输入账号!" }],
|
||||
});
|
||||
|
||||
function isNotEmpty(rule: InternalRuleItem, value: Value, callback: any) {
|
||||
if (!value) {
|
||||
@@ -41,52 +32,157 @@ function isNotEmpty(rule: InternalRuleItem, value: Value, callback: any) {
|
||||
}
|
||||
|
||||
const submitForm = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return
|
||||
await formEl.validate((valid, fields) => {
|
||||
if (valid) {
|
||||
login(registerData.username, registerData.password).then(result =>{
|
||||
if (!formEl || loading.value) return;
|
||||
await formEl.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
loading.value = true;
|
||||
try {
|
||||
const result = await login(registerData.username, registerData.password);
|
||||
if (result.code === 200) {
|
||||
localStorage.setItem("isLoggedIn", "true");
|
||||
ElMessage.success(result.message)
|
||||
router.push("/welcome")
|
||||
ElMessage.success(result.message);
|
||||
await router.push("/welcome");
|
||||
} else {
|
||||
ElMessage.error(result.message)
|
||||
ElMessage.error(result.message);
|
||||
}
|
||||
})
|
||||
} else {
|
||||
console.log('error submit!', fields)
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
<template>
|
||||
<div class="login">
|
||||
<el-form ref="loginForm" :model = "registerData">
|
||||
<el-form-item label="账号:" :rules="rules.username" prop="username">
|
||||
<el-input v-model= "registerData.username"/>
|
||||
<section class="login-page">
|
||||
<div class="login-panel surface-card">
|
||||
<div class="hero">
|
||||
<p class="hero-kicker">LPT · Focus Flow</p>
|
||||
<h1>今天的学习,从一次清晰登录开始</h1>
|
||||
<p>
|
||||
你将看到任务优先级、学习会话和复习数据在同一个系统里自然衔接。
|
||||
</p>
|
||||
<ul class="hero-points">
|
||||
<li>明确学习任务</li>
|
||||
<li>记录会话与残片</li>
|
||||
<li>持续复盘</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="form-area">
|
||||
<h2>账号登录</h2>
|
||||
<el-form ref="loginForm" :model="registerData" class="login-form">
|
||||
<el-form-item label="账号" :rules="rules.username" prop="username">
|
||||
<el-input
|
||||
v-model="registerData.username"
|
||||
placeholder="请输入账号"
|
||||
@keyup.enter="submitForm(loginForm)"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="密码:" :rules="rules.password" prop="password">
|
||||
<el-input v-model= "registerData.password" type="password"/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submitForm(loginForm)" style="width: 100%;">登录</el-button>
|
||||
<el-form-item label="密码" :rules="rules.password" prop="password">
|
||||
<el-input
|
||||
v-model="registerData.password"
|
||||
type="password"
|
||||
show-password
|
||||
placeholder="请输入密码"
|
||||
@keyup.enter="submitForm(loginForm)"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-button
|
||||
type="success"
|
||||
class="submit-btn"
|
||||
:loading="loading"
|
||||
@click="submitForm(loginForm)"
|
||||
>
|
||||
进入系统
|
||||
</el-button>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
*{
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
.login-page {
|
||||
min-height: calc(100vh - 240px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.login {
|
||||
min-height: 25vh;
|
||||
min-width: 30vw;
|
||||
background: #22b9fa;
|
||||
.login-panel {
|
||||
width: min(980px, 100%);
|
||||
display: grid;
|
||||
grid-template-columns: 1.1fr 1fr;
|
||||
gap: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hero {
|
||||
padding: 36px;
|
||||
background: linear-gradient(150deg, var(--green-800) 0%, var(--green-600) 100%);
|
||||
color: #effcf4;
|
||||
}
|
||||
|
||||
.hero-kicker {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
letter-spacing: 1.4px;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
margin: 14px 0 10px;
|
||||
line-height: 1.35;
|
||||
font-size: clamp(22px, 2.6vw, 32px);
|
||||
}
|
||||
|
||||
.hero p {
|
||||
margin: 0;
|
||||
opacity: 0.88;
|
||||
}
|
||||
|
||||
.hero-points {
|
||||
margin: 20px 0 0;
|
||||
padding-left: 18px;
|
||||
}
|
||||
|
||||
.hero-points li {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.form-area {
|
||||
background: var(--surface-strong);
|
||||
padding: 32px 30px;
|
||||
}
|
||||
|
||||
.form-area h2 {
|
||||
margin: 0 0 16px;
|
||||
color: var(--green-900);
|
||||
}
|
||||
|
||||
.login-form :deep(.el-form-item) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.login-panel {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.login-page {
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
.hero,
|
||||
.form-area {
|
||||
padding: 22px 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
+80
-11
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from "vue";
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import request from "@/utils/request";
|
||||
|
||||
interface ReviewTask {
|
||||
@@ -13,6 +13,12 @@ interface ReviewTask {
|
||||
const loading = ref(false);
|
||||
const tasks = ref<ReviewTask[]>([]);
|
||||
|
||||
const summary = computed(() => ({
|
||||
totalTasks: tasks.value.length,
|
||||
totalReports: tasks.value.reduce((acc, item) => acc + Number(item.reportCount || 0), 0),
|
||||
totalFragments: tasks.value.reduce((acc, item) => acc + Number(item.fragmentCount || 0), 0),
|
||||
}));
|
||||
|
||||
const loadTasks = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
@@ -35,27 +41,90 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-card>
|
||||
<template #header>
|
||||
<div class="header">
|
||||
<span>复习任务总览</span>
|
||||
<el-button type="primary" plain @click="loadTasks">刷新</el-button>
|
||||
<section class="review-page">
|
||||
<header class="surface-card review-head">
|
||||
<div>
|
||||
<h1 class="page-title">复习总览</h1>
|
||||
<p class="page-subtitle">根据学习报告与残片数量,快速识别需要回顾的任务</p>
|
||||
</div>
|
||||
</template>
|
||||
<el-button type="success" plain :loading="loading" @click="loadTasks">刷新</el-button>
|
||||
</header>
|
||||
|
||||
<div class="summary-grid">
|
||||
<article class="surface-card metric-card">
|
||||
<span>任务数</span>
|
||||
<strong>{{ summary.totalTasks }}</strong>
|
||||
</article>
|
||||
<article class="surface-card metric-card">
|
||||
<span>学习报告总数</span>
|
||||
<strong>{{ summary.totalReports }}</strong>
|
||||
</article>
|
||||
<article class="surface-card metric-card">
|
||||
<span>学习残片总数</span>
|
||||
<strong>{{ summary.totalFragments }}</strong>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<article class="surface-card table-card">
|
||||
<el-table v-loading="loading" :data="tasks" stripe>
|
||||
<el-table-column prop="taskName" label="任务名" min-width="180" />
|
||||
<el-table-column prop="effectiveTime" label="累计有效学习时长" min-width="140" />
|
||||
<el-table-column prop="effectiveTime" label="累计有效学习时长" min-width="150" />
|
||||
<el-table-column prop="reportCount" label="学习报告数" min-width="120" />
|
||||
<el-table-column prop="fragmentCount" label="学习残片数" min-width="120" />
|
||||
</el-table>
|
||||
</el-card>
|
||||
</article>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.header {
|
||||
.review-page {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.review-head {
|
||||
padding: 20px 22px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.summary-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.metric-card {
|
||||
padding: 16px 18px;
|
||||
}
|
||||
|
||||
.metric-card span {
|
||||
color: var(--text-secondary);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.metric-card strong {
|
||||
display: block;
|
||||
margin-top: 6px;
|
||||
font-size: 30px;
|
||||
line-height: 1;
|
||||
color: var(--green-900);
|
||||
}
|
||||
|
||||
.table-card {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.summary-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.review-head {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
+127
-121
@@ -1,19 +1,18 @@
|
||||
<script setup lang="ts">
|
||||
import {computed, onMounted, onUnmounted, ref} from 'vue';
|
||||
import { computed, onMounted, onUnmounted, ref } from "vue";
|
||||
import request from "@/utils/request";
|
||||
import {ElMessage} from "element-plus";
|
||||
import {useRoute, useRouter} from "vue-router";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
||||
const route = useRoute()
|
||||
const taskId = route.params.taskId ? Number(route.params.taskId) : null;
|
||||
const buttonName = route.meta.buttonText;
|
||||
const buttonName = route.meta.buttonText as string;
|
||||
|
||||
|
||||
const taskName = ref('');
|
||||
const taskDescription = ref('');
|
||||
const materialURL = ref('');
|
||||
const taskName = ref("");
|
||||
const taskDescription = ref("");
|
||||
const materialURL = ref("");
|
||||
const priority = ref({
|
||||
urgency: 0,
|
||||
importance: 0,
|
||||
@@ -23,33 +22,34 @@ const priority = ref({
|
||||
});
|
||||
|
||||
const priorityOptions = [0, 1, 2, 3, 4, 5];
|
||||
const pageTitle = computed(() => (route.name === "update-task" ? "更新学习任务" : "创建学习任务"));
|
||||
const pageSubtitle = computed(() =>
|
||||
route.name === "update-task"
|
||||
? "修改任务信息后保存,后续学习会话将使用最新内容"
|
||||
: "填写任务基本信息与优先级维度,帮助系统更好排序"
|
||||
);
|
||||
|
||||
const createTask = () => {
|
||||
console.log('创建任务', {
|
||||
taskName: taskName.value,
|
||||
taskDescription: taskDescription.value,
|
||||
priority: priority.value,
|
||||
});
|
||||
|
||||
request.post("/tasks", {
|
||||
request
|
||||
.post("/tasks", {
|
||||
taskName: taskName.value,
|
||||
taskDescription: taskDescription.value,
|
||||
materialURL: materialURL.value,
|
||||
...priority.value
|
||||
}).then(result => {
|
||||
if (result.code == 200) {
|
||||
ElMessage.success("创建任务成功,任务id:" + result.message)
|
||||
router.back()
|
||||
} else {
|
||||
ElMessage.error("任务创建失败,错误原因:" + result.message)
|
||||
}
|
||||
...priority.value,
|
||||
})
|
||||
.then((result) => {
|
||||
if (result.code === 200) {
|
||||
ElMessage.success("创建任务成功");
|
||||
router.back();
|
||||
} else {
|
||||
ElMessage.error("任务创建失败:" + result.message);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const loadTask = () => {
|
||||
console.info("开始加载任务信息")
|
||||
if (taskId !== null) {
|
||||
request.get(`/tasks/${taskId}`, {}).then(result => {
|
||||
request.get(`/tasks/${taskId}`, {}).then((result) => {
|
||||
if (result.code === 200) {
|
||||
const data = result.data;
|
||||
taskName.value = data.taskName;
|
||||
@@ -70,46 +70,41 @@ const loadTask = () => {
|
||||
};
|
||||
|
||||
const updateTask = () => {
|
||||
console.log('更新任务', {
|
||||
taskName: taskName.value,
|
||||
taskDescription: taskDescription.value,
|
||||
priority: priority.value,
|
||||
});
|
||||
|
||||
request.put("/tasks/" + taskId, {
|
||||
request
|
||||
.put("/tasks/" + taskId, {
|
||||
id: taskId,
|
||||
taskName: taskName.value,
|
||||
taskDescription: taskDescription.value,
|
||||
materialURL: materialURL.value,
|
||||
...priority.value
|
||||
}).then(result => {
|
||||
if (result.code == 200) {
|
||||
ElMessage.success("更新任务成功" + result.message)
|
||||
router.back()
|
||||
} else {
|
||||
ElMessage.error("任务更新失败,错误原因:" + result.message)
|
||||
}
|
||||
...priority.value,
|
||||
})
|
||||
.then((result) => {
|
||||
if (result.code === 200) {
|
||||
ElMessage.success("更新任务成功");
|
||||
router.back();
|
||||
} else {
|
||||
ElMessage.error("任务更新失败:" + result.message);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const cancelTask = () => {
|
||||
ElMessage.success("已取消创建")
|
||||
router.back()
|
||||
ElMessage.info("已取消操作");
|
||||
router.back();
|
||||
};
|
||||
|
||||
|
||||
const submitTask = () => {
|
||||
switch (route.name) {
|
||||
case 'add-task':
|
||||
case "add-task":
|
||||
createTask();
|
||||
break;
|
||||
case 'update-task':
|
||||
case "update-task":
|
||||
updateTask();
|
||||
break;
|
||||
default:
|
||||
ElMessage.error('未知操作类型');
|
||||
ElMessage.error("未知操作类型");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const screenWidth = ref(window.innerWidth);
|
||||
|
||||
@@ -117,123 +112,134 @@ const updateWidth = () => {
|
||||
screenWidth.value = window.innerWidth;
|
||||
};
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('resize', updateWidth)
|
||||
if (route.meta.action === 'update' && taskId !== null) {
|
||||
window.addEventListener("resize", updateWidth);
|
||||
if (route.meta.action === "update" && taskId !== null) {
|
||||
loadTask();
|
||||
}
|
||||
});
|
||||
onUnmounted(() => window.removeEventListener('resize', updateWidth));
|
||||
|
||||
const columnSpan = computed(() => {
|
||||
return screenWidth.value <= 768 ? 20 : 8;
|
||||
});
|
||||
onUnmounted(() => window.removeEventListener("resize", updateWidth));
|
||||
|
||||
const isMobile = computed(() => screenWidth.value <= 900);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-form label-width="100px">
|
||||
<el-form-item label="任务名:">
|
||||
<el-input v-model="taskName" placeholder="请输入任务名"></el-input>
|
||||
</el-form-item>
|
||||
<section class="task-form-page">
|
||||
<header class="surface-card form-header">
|
||||
<h1 class="page-title">{{ pageTitle }}</h1>
|
||||
<p class="page-subtitle">{{ pageSubtitle }}</p>
|
||||
</header>
|
||||
|
||||
<el-form-item label="任务描述:">
|
||||
<el-input
|
||||
v-model="taskDescription"
|
||||
type="textarea"
|
||||
placeholder="请输入任务描述"
|
||||
rows="6"
|
||||
></el-input>
|
||||
<el-form label-position="top" class="surface-card form-shell">
|
||||
<div class="group">
|
||||
<h3>基础信息</h3>
|
||||
<el-form-item label="任务名称">
|
||||
<el-input v-model="taskName" placeholder="例如:Vue3 组件通信实践" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="材料地址:">
|
||||
<el-input v-model="materialURL" placeholder="请输入学习材料URL"></el-input>
|
||||
<el-form-item label="任务描述">
|
||||
<el-input v-model="taskDescription" type="textarea" :rows="5" placeholder="请输入任务描述" />
|
||||
</el-form-item>
|
||||
<el-form-item label="学习材料地址">
|
||||
<el-input v-model="materialURL" placeholder="例如:https://..." />
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<el-form-item label="优先级:">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="columnSpan">
|
||||
<div class="group">
|
||||
<h3>优先级维度</h3>
|
||||
<div class="priority-grid" :class="{ mobile: isMobile }">
|
||||
<el-form-item label="急迫性">
|
||||
<el-select v-model="priority.urgency">
|
||||
<el-option
|
||||
v-for="item in priorityOptions"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
></el-option>
|
||||
<el-option v-for="item in priorityOptions" :key="'u' + item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="columnSpan">
|
||||
<el-form-item label="重要性">
|
||||
<el-select v-model="priority.importance">
|
||||
<el-option
|
||||
v-for="item in priorityOptions"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
></el-option>
|
||||
<el-option v-for="item in priorityOptions" :key="'i' + item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="columnSpan">
|
||||
<el-form-item label="内容难度">
|
||||
<el-select v-model="priority.contentDifficulty">
|
||||
<el-option
|
||||
v-for="item in priorityOptions"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
></el-option>
|
||||
<el-option v-for="item in priorityOptions" :key="'d' + item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="columnSpan">
|
||||
<el-form-item label="未来价值">
|
||||
<el-select v-model="priority.futureValue">
|
||||
<el-option
|
||||
v-for="item in priorityOptions"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
></el-option>
|
||||
<el-option v-for="item in priorityOptions" :key="'f' + item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="columnSpan">
|
||||
<el-form-item label="主观优先级">
|
||||
<el-select v-model="priority.subjectivePriority">
|
||||
<el-option
|
||||
v-for="item in priorityOptions"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
></el-option>
|
||||
<el-option v-for="item in priorityOptions" :key="'s' + item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submitTask">{{ buttonName }}</el-button>
|
||||
<el-button @click="cancelTask">取消</el-button>
|
||||
</el-form-item>
|
||||
<div class="action-row">
|
||||
<el-button type="success" size="large" @click="submitTask">{{ buttonName }}</el-button>
|
||||
<el-button size="large" @click="cancelTask">取消</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.el-form-item {
|
||||
margin-bottom: 15px;
|
||||
.task-form-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.form-header {
|
||||
padding: 20px 22px;
|
||||
}
|
||||
|
||||
.form-shell {
|
||||
padding: 22px;
|
||||
}
|
||||
|
||||
.group + .group {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.group h3 {
|
||||
margin: 0 0 12px;
|
||||
color: var(--green-900);
|
||||
}
|
||||
|
||||
.priority-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 0 14px;
|
||||
}
|
||||
|
||||
.priority-grid.mobile {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.action-row {
|
||||
margin-top: 8px;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.el-col {
|
||||
width: 100% !important;
|
||||
.form-shell {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.action-row {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.action-row .el-button {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
+93
-78
@@ -9,98 +9,113 @@ const startReview = () => {
|
||||
router.push("/review");
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-container class="container">
|
||||
<el-header class="header">
|
||||
欢迎回来,今天也稳步推进学习计划
|
||||
</el-header>
|
||||
|
||||
|
||||
<el-main>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="18">
|
||||
<el-card class="progress-card" shadow="always">
|
||||
<div class="progress-text">学习进度总揽</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="3">
|
||||
<div class="button-group">
|
||||
<el-button type="success" size="large" @click="startStudy" plain>开始学习</el-button>
|
||||
<el-button type="primary" size="large" @click="startReview" plain>开始复习</el-button>
|
||||
<section class="welcome-page">
|
||||
<div class="welcome-banner surface-card">
|
||||
<div>
|
||||
<h1 class="page-title">欢迎回来,准备好继续学习了吗?</h1>
|
||||
<p class="page-subtitle">
|
||||
先挑选任务开始一次会话,再在复习页回顾你的学习轨迹。
|
||||
</p>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-main>
|
||||
</el-container>
|
||||
<el-tag type="success" effect="dark" size="large">今日模式:稳步推进</el-tag>
|
||||
</div>
|
||||
|
||||
<div class="quick-grid">
|
||||
<article class="quick-card surface-card">
|
||||
<h3>学习会话</h3>
|
||||
<p>进入任务列表,选择当前最重要的一项并开始学习计时。</p>
|
||||
<el-button type="success" size="large" @click="startStudy">开始学习</el-button>
|
||||
</article>
|
||||
|
||||
<article class="quick-card surface-card">
|
||||
<h3>复习回看</h3>
|
||||
<p>查看学习报告与残片数量,明确哪些内容需要优先复习。</p>
|
||||
<el-button plain type="success" size="large" @click="startReview">开始复习</el-button>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div class="tips-board surface-card">
|
||||
<h3>建议流程</h3>
|
||||
<ol>
|
||||
<li>进入学习页,确认任务材料地址与优先级。</li>
|
||||
<li>开始会话并在休息时记录学习残片。</li>
|
||||
<li>结束会话后填写总结,再回到复习页检视数据。</li>
|
||||
</ol>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
*{
|
||||
margin: 0px;
|
||||
/*padding: 0;*/
|
||||
}
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
font-family: "Helvetica Neue", Arial, sans-serif;
|
||||
}
|
||||
|
||||
.header {
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.progress-card {
|
||||
height: 400px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
font-size: 18px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
.welcome-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
margin-top: 80px;
|
||||
align-items: center;
|
||||
}
|
||||
.button-group .el-button {
|
||||
margin-left: 0;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
|
||||
/*滚动样式*/
|
||||
.marquee-wrapper {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
height: 40px;
|
||||
.welcome-banner {
|
||||
padding: 20px 22px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.marquee-content {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
will-change: transform;
|
||||
animation: scroll-left 15s linear infinite;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
.quick-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
@keyframes scroll-left {
|
||||
0% {
|
||||
transform: translateX(100%);
|
||||
.quick-card {
|
||||
padding: 22px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.quick-card h3 {
|
||||
margin: 0;
|
||||
color: var(--green-900);
|
||||
}
|
||||
|
||||
.quick-card p {
|
||||
margin: 0;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.quick-card .el-button {
|
||||
margin-top: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tips-board {
|
||||
padding: 20px 22px;
|
||||
}
|
||||
|
||||
.tips-board h3 {
|
||||
margin: 0;
|
||||
color: var(--green-900);
|
||||
}
|
||||
|
||||
.tips-board ol {
|
||||
margin: 10px 0 0;
|
||||
padding-left: 20px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.tips-board li + li {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.welcome-banner {
|
||||
flex-direction: column;
|
||||
}
|
||||
100% {
|
||||
transform: translateX(-100%);
|
||||
|
||||
.quick-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user