wip
This commit is contained in:
@@ -1,25 +1,277 @@
|
||||
<script setup lang="ts">
|
||||
import { GITHUB, ProjectName } from "@/config/env.ts";
|
||||
import {APP_NAME, GITHUB} from "@/config/env.ts";
|
||||
import BaseIcon from "@/components/BaseIcon.vue";
|
||||
import { defineAsyncComponent } from "vue";
|
||||
import {defineAsyncComponent, onMounted, ref, watch} from "vue";
|
||||
import {usePracticeStore} from "@/stores/practice.ts";
|
||||
import {useBaseStore} from "@/stores/base.ts";
|
||||
import {msToHourMinute} from "@/utils";
|
||||
import dayjs from "dayjs";
|
||||
import BaseButton from "@/components/BaseButton.vue";
|
||||
import Toast from "@/components/base/toast/Toast.ts";
|
||||
|
||||
const Dialog = defineAsyncComponent(() => import('@/components/dialog/Dialog.vue'))
|
||||
|
||||
const practiceStore = usePracticeStore()
|
||||
const baseStore = useBaseStore()
|
||||
|
||||
let showWechatDialog = $ref(false)
|
||||
let showXhsDialog = $ref(false)
|
||||
let showQQDialog = $ref(false)
|
||||
let showShareImageDialog = $ref(false)
|
||||
let isGeneratingImage = $ref(false)
|
||||
let generatedImageUrl = $ref<string | null>(null)
|
||||
|
||||
// 计算学习统计数据
|
||||
const studyStats = $computed(() => {
|
||||
const accuracyRate = practiceStore.total === 0 ? 100 : Math.round(((practiceStore.total - practiceStore.wrong) / practiceStore.total) * 100)
|
||||
const studyTime = msToHourMinute(practiceStore.spend).replace('小时', 'h ').replace('分钟', 'm')
|
||||
|
||||
return {
|
||||
total: practiceStore.total,
|
||||
newWords: practiceStore.newWordNumber,
|
||||
review: practiceStore.reviewWordNumber + practiceStore.writeWordNumber,
|
||||
wrong: practiceStore.wrong,
|
||||
correct: practiceStore.total - practiceStore.wrong,
|
||||
accuracy: accuracyRate,
|
||||
time: studyTime,
|
||||
date: dayjs().format('MM月DD日'),
|
||||
dictionary: baseStore.sdict.name || '未知词书'
|
||||
}
|
||||
})
|
||||
|
||||
// 监听对话框打开事件,自动生成图片
|
||||
watch(() => showShareImageDialog, (newVal) => {
|
||||
if (newVal && !generatedImageUrl) {
|
||||
generateShareImage()
|
||||
}
|
||||
})
|
||||
|
||||
// 生成分享图片
|
||||
async function generateShareImage() {
|
||||
if (isGeneratingImage || generatedImageUrl) return
|
||||
|
||||
isGeneratingImage = true
|
||||
|
||||
try {
|
||||
// 创建canvas元素
|
||||
const canvas = document.createElement('canvas')
|
||||
const ctx = canvas.getContext('2d')
|
||||
|
||||
// 设置尺寸为1.3倍高度比例 (宽度720,高度936)
|
||||
const width = 720
|
||||
const height = Math.round(width * 1.3)
|
||||
canvas.width = width
|
||||
canvas.height = height
|
||||
|
||||
if (!ctx) return
|
||||
|
||||
// 创建灰黑色渐变背景
|
||||
const gradient = ctx.createLinearGradient(0, 0, width, height)
|
||||
gradient.addColorStop(0, '#1f2937')
|
||||
gradient.addColorStop(1, '#111827')
|
||||
ctx.fillStyle = gradient
|
||||
ctx.fillRect(0, 0, width, height)
|
||||
|
||||
// 添加装饰性圆形
|
||||
ctx.fillStyle = 'rgba(255, 255, 255, 0.05)'
|
||||
ctx.beginPath()
|
||||
ctx.arc(width * 0.8, height * 0.2, 60, 0, Math.PI * 2)
|
||||
ctx.fill()
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.arc(width * 0.2, height * 0.8, 40, 0, Math.PI * 2)
|
||||
ctx.fill()
|
||||
|
||||
// 设置文字样式
|
||||
ctx.fillStyle = '#ffffff'
|
||||
ctx.textAlign = 'left'
|
||||
|
||||
// 顶部用户信息
|
||||
const avatarX = width * 0.1
|
||||
const avatarY = height * 0.08
|
||||
|
||||
// 绘制头像背景
|
||||
ctx.fillStyle = 'rgba(255, 255, 255, 0.2)'
|
||||
ctx.beginPath()
|
||||
ctx.arc(avatarX + 25, avatarY + 25, 20, 0, Math.PI * 2)
|
||||
ctx.fill()
|
||||
|
||||
ctx.fillStyle = '#ffffff'
|
||||
ctx.font = 'bold 18px Arial'
|
||||
ctx.fillText(baseStore.user?.name || '学习者', avatarX + 60, avatarY + 20)
|
||||
|
||||
ctx.font = '14px Arial'
|
||||
ctx.fillStyle = 'rgba(255, 255, 255, 0.7)'
|
||||
ctx.fillText(dayjs().format('YYYY年MM月DD日'), avatarX + 60, avatarY + 40)
|
||||
|
||||
// 右上角标签
|
||||
ctx.textAlign = 'right'
|
||||
ctx.font = '12px Arial'
|
||||
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)'
|
||||
ctx.fillText('Type Words | 英语学习', width * 0.9, avatarY + 20)
|
||||
|
||||
// 统计数据区域 (三个圆角矩形)
|
||||
const statsY = height * 0.25
|
||||
const statWidth = width * 0.25
|
||||
const statHeight = height * 0.12
|
||||
const statSpacing = width * 0.05
|
||||
|
||||
const stats = [
|
||||
{ label: '总词数', value: studyStats.total, color: '#60a5fa' },
|
||||
{ label: '学习时长', value: studyStats.time, color: '#34d399' },
|
||||
{ label: '正确率', value: studyStats.accuracy + '%', color: '#f59e0b' }
|
||||
]
|
||||
|
||||
stats.forEach((stat, index) => {
|
||||
const x = width * 0.1 + index * (statWidth + statSpacing)
|
||||
const y = statsY
|
||||
|
||||
// 绘制圆角矩形背景
|
||||
ctx.fillStyle = 'rgba(255, 255, 255, 0.1)'
|
||||
roundRect(ctx, x, y, statWidth, statHeight, 15)
|
||||
ctx.fill()
|
||||
|
||||
// 数值
|
||||
ctx.fillStyle = '#ffffff'
|
||||
ctx.font = 'bold 24px Arial'
|
||||
ctx.textAlign = 'center'
|
||||
ctx.fillText(stat.value.toString(), x + statWidth / 2, y + statHeight * 0.4)
|
||||
|
||||
// 标签
|
||||
ctx.fillStyle = 'rgba(255, 255, 255, 0.7)'
|
||||
ctx.font = '12px Arial'
|
||||
ctx.fillText(stat.label, x + statWidth / 2, y + statHeight * 0.7)
|
||||
})
|
||||
|
||||
// 励志语句
|
||||
ctx.textAlign = 'center'
|
||||
ctx.fillStyle = '#ffffff'
|
||||
ctx.font = 'italic 20px Arial'
|
||||
ctx.fillText('Keep going, never give up!', width / 2, height * 0.45)
|
||||
|
||||
ctx.font = '16px Arial'
|
||||
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)'
|
||||
ctx.fillText('坚持就是胜利', width / 2, height * 0.5)
|
||||
|
||||
// 底部品牌信息
|
||||
const bottomY = height * 0.65
|
||||
const brandX = width * 0.1
|
||||
|
||||
ctx.textAlign = 'left'
|
||||
ctx.fillStyle = '#ffffff'
|
||||
ctx.font = 'bold 20px Arial'
|
||||
ctx.fillText('Type Words', brandX, bottomY)
|
||||
|
||||
ctx.font = '12px Arial'
|
||||
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)'
|
||||
ctx.fillText('词文记 - 高效英语学习', brandX, bottomY + 20)
|
||||
ctx.fillText(window.location.origin, brandX, bottomY + 35)
|
||||
|
||||
// 二维码区域
|
||||
const qrX = width * 0.75
|
||||
const qrY = bottomY - 10
|
||||
|
||||
// 二维码背景
|
||||
ctx.fillStyle = '#ffffff'
|
||||
roundRect(ctx, qrX - 5, qrY - 5, 50, 50, 5)
|
||||
ctx.fill()
|
||||
|
||||
// 绘制简单二维码
|
||||
ctx.fillStyle = '#000000'
|
||||
const moduleSize = 2
|
||||
for (let row = 0; row < 20; row++) {
|
||||
for (let col = 0; col < 20; col++) {
|
||||
if (Math.random() > 0.5) {
|
||||
ctx.fillRect(qrX + col * moduleSize, qrY + row * moduleSize, moduleSize, moduleSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 将canvas转换为图片
|
||||
const imageUrl = canvas.toDataURL('image/png', 1.0)
|
||||
generatedImageUrl = imageUrl
|
||||
|
||||
} catch (error) {
|
||||
console.error('生成图片失败:', error)
|
||||
alert('生成图片失败,请重试')
|
||||
} finally {
|
||||
isGeneratingImage = false
|
||||
}
|
||||
}
|
||||
|
||||
// 复制图片到剪贴板
|
||||
async function copyImageToClipboard() {
|
||||
if (!generatedImageUrl) return
|
||||
|
||||
try {
|
||||
const response = await fetch(generatedImageUrl)
|
||||
const blob = await response.blob()
|
||||
|
||||
if (navigator.clipboard && window.ClipboardItem) {
|
||||
await navigator.clipboard.write([
|
||||
new ClipboardItem({
|
||||
'image/png': blob
|
||||
})
|
||||
])
|
||||
Toast.success('图片已复制到剪贴板!')
|
||||
} else {
|
||||
// 降级方案:下载图片
|
||||
downloadImage()
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('复制失败:', error)
|
||||
// 降级方案:下载图片
|
||||
downloadImage()
|
||||
}
|
||||
}
|
||||
|
||||
// 下载图片
|
||||
function downloadImage() {
|
||||
if (!generatedImageUrl) return
|
||||
|
||||
const link = document.createElement('a')
|
||||
link.download = `${APP_NAME} 分享_${studyStats.date}_${studyStats.dictionary}.png`
|
||||
link.href = generatedImageUrl
|
||||
link.click()
|
||||
}
|
||||
|
||||
// 切换背景
|
||||
function changeBackground() {
|
||||
// 这里可以实现背景切换逻辑
|
||||
console.log('切换背景')
|
||||
}
|
||||
|
||||
// 绘制圆角矩形辅助函数
|
||||
function roundRect(ctx: CanvasRenderingContext2D, x: number, y: number, width: number, height: number, radius: number) {
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(x + radius, y)
|
||||
ctx.lineTo(x + width - radius, y)
|
||||
ctx.quadraticCurveTo(x + width, y, x + width, y + radius)
|
||||
ctx.lineTo(x + width, y + height - radius)
|
||||
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height)
|
||||
ctx.lineTo(x + radius, y + height)
|
||||
ctx.quadraticCurveTo(x, y + height, x, y + height - radius)
|
||||
ctx.lineTo(x, y + radius)
|
||||
ctx.quadraticCurveTo(x, y, x + radius, y)
|
||||
ctx.closePath()
|
||||
}
|
||||
|
||||
onMounted(generateShareImage)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex-col center gap-1">
|
||||
<BaseIcon>
|
||||
<IconFluentShare20Regular/>
|
||||
<!-- 分享学习总结按钮 -->
|
||||
<BaseIcon @click="showShareImageDialog = true"
|
||||
class="cursor-pointer hover:scale-110 transition-transform duration-200">
|
||||
<IconFluentShare20Regular class="text-blue-500 hover:text-blue-600"/>
|
||||
</BaseIcon>
|
||||
|
||||
<a
|
||||
:href="GITHUB"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
aria-label="GITHUB 项目地址">
|
||||
:href="GITHUB"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
aria-label="GITHUB 项目地址">
|
||||
<BaseIcon>
|
||||
<IconSimpleIconsGithub/>
|
||||
</BaseIcon>
|
||||
@@ -36,25 +288,178 @@ let showQQDialog = $ref(false)
|
||||
</BaseIcon>
|
||||
|
||||
<a
|
||||
href="https://x.com/typewords2"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
aria-label="关注我的 X 账户 typewords2">
|
||||
href="https://x.com/typewords2"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
aria-label="关注我的 X 账户 typewords2">
|
||||
<BaseIcon>
|
||||
<IconRiTwitterFill class="color-blue"/>
|
||||
</BaseIcon>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="mailto:zyronon@163.com"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
aria-label="发送邮件到 zyronon@163.com">
|
||||
href="mailto:zyronon@163.com"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
aria-label="发送邮件到 zyronon@163.com">
|
||||
<BaseIcon>
|
||||
<IconMaterialSymbolsMail class="color-blue"/>
|
||||
</BaseIcon>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- 学习总结分享图片生成对话框 -->
|
||||
<Dialog
|
||||
title="分享"
|
||||
:close-on-click-bg="true"
|
||||
@close="generatedImageUrl = null"
|
||||
custom-class="!max-w-4xl !w-auto">
|
||||
<div class="flex min-w-160 max-w-200">
|
||||
<!-- 左侧:海报预览区域 -->
|
||||
<div class="flex-1 p-6 border-r border-gray-200">
|
||||
<!-- 生成中状态 -->
|
||||
<div v-if="isGeneratingImage" class="relative">
|
||||
<div class="w-80 h-104 bg-gradient-to-br from-gray-800 to-gray-900 rounded-xl p-6 text-white relative overflow-hidden">
|
||||
<!-- 背景装饰 -->
|
||||
<div class="absolute top-4 right-4 w-16 h-16 bg-white bg-opacity-10 rounded-full"></div>
|
||||
<div class="absolute bottom-8 left-8 w-12 h-12 bg-white bg-opacity-5 rounded-full"></div>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<div class="flex items-center justify-center h-full">
|
||||
<div class="text-center">
|
||||
<div class="w-12 h-12 border-2 border-white border-t-transparent rounded-full animate-spin mx-auto mb-4"></div>
|
||||
<div class="text-white text-sm">正在生成海报...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 海报预览 -->
|
||||
<div v-else-if="generatedImageUrl" class="relative">
|
||||
<img
|
||||
:src="generatedImageUrl"
|
||||
alt="学习总结海报"
|
||||
class="w-80 h-auto rounded-xl shadow-lg">
|
||||
</div>
|
||||
|
||||
<!-- 默认预览状态 -->
|
||||
<div v-else class="w-80 h-104 bg-gradient-to-br from-gray-800 to-gray-900 rounded-xl p-6 text-white relative overflow-hidden">
|
||||
<!-- 背景装饰 -->
|
||||
<div class="absolute top-4 right-4 w-16 h-16 bg-white bg-opacity-10 rounded-full"></div>
|
||||
<div class="absolute bottom-8 left-8 w-12 h-12 bg-white bg-opacity-5 rounded-full"></div>
|
||||
|
||||
<!-- 顶部用户信息 -->
|
||||
<div class="flex items-center mb-6">
|
||||
<div class="w-12 h-12 bg-gray-600 rounded-full mr-3 flex items-center justify-center">
|
||||
<IconSimpleIconsGithub class="w-6 h-6 text-white"/>
|
||||
</div>
|
||||
<div>
|
||||
<div class="font-semibold text-lg">{{ baseStore.user?.name || '学习者' }}</div>
|
||||
<div class="text-gray-300 text-sm">{{ dayjs().format('YYYY年MM月DD日') }}</div>
|
||||
</div>
|
||||
<div class="ml-auto text-xs text-gray-300">
|
||||
Type Words | 英语学习
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 统计数据 -->
|
||||
<div class="grid grid-cols-3 gap-4 mb-8">
|
||||
<div class="text-center">
|
||||
<div class="text-2xl font-bold">{{ studyStats.total }}</div>
|
||||
<div class="text-gray-300 text-xs">总词数</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="text-2xl font-bold">{{ studyStats.time }}</div>
|
||||
<div class="text-gray-300 text-xs">学习时长</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="text-2xl font-bold">{{ studyStats.accuracy }}%</div>
|
||||
<div class="text-gray-300 text-xs">正确率</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 励志语句 -->
|
||||
<div class="text-center mb-8">
|
||||
<div class="text-lg italic mb-2">Keep going, never give up!</div>
|
||||
<div class="text-sm text-gray-300">坚持就是胜利</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部品牌信息 -->
|
||||
<div class="absolute bottom-6 left-6 right-6">
|
||||
<div class="flex justify-between items-end">
|
||||
<div>
|
||||
<div class="font-bold text-lg">Type Words</div>
|
||||
<div class="text-xs text-gray-300">词文记 - 高效英语学习</div>
|
||||
<div class="text-xs text-gray-400">{{ window.location.origin }}</div>
|
||||
</div>
|
||||
<div class="w-16 h-16 bg-white rounded p-2">
|
||||
<div class="w-full h-full bg-black grid grid-cols-8 gap-0.5">
|
||||
<div v-for="i in 64" :key="i" :class="Math.random() > 0.5 ? 'bg-black' : 'bg-white'"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧:分享引导区域 -->
|
||||
<div class="flex-1 p-8 pt-0 space-y-6">
|
||||
<div class="mb-8">
|
||||
<h2 class="text-2xl font-bold text-gray-800 mb-4 flex items-center">
|
||||
<span class="mr-2">🎯</span>
|
||||
分享你的进步
|
||||
</h2>
|
||||
|
||||
<div class="">
|
||||
<div class="flex items-start">
|
||||
<span class="mr-2">🚀</span>
|
||||
在 {{ APP_NAME }},学习英语也能成为超酷的事情!
|
||||
</div>
|
||||
<div class="flex items-start">
|
||||
<span class="mr-2">📸</span>
|
||||
快来分享你的学习图片,让你的进步刷屏朋友圈,成为最受瞩目的英语学霸!😎
|
||||
</div>
|
||||
<div class="flex items-start">
|
||||
<span class="mr-2">💪</span>
|
||||
这不只是简单的打卡,更是你秀出英语实力的舞台!
|
||||
</div>
|
||||
<div class="flex items-start">
|
||||
<span class="mr-2">🔥</span>
|
||||
分享你的战绩,收获朋友们的点赞和认可,让你的朋友圈也掀起一股英语学习的热潮!
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 个性化装扮 -->
|
||||
<div
|
||||
class="flex items-center justify-between px-6 py-3 bg-gray-200 rounded-lg cp hover:bg-gray-100 transition-all duration-200">
|
||||
<div
|
||||
@click="changeBackground"
|
||||
class="flex items-center gap-2">
|
||||
<IconMdiSparkles class="w-4 h-4 text-yellow-500"/>
|
||||
换个背景
|
||||
</div>
|
||||
<span class="text-sm text-gray-500 bg-gray-100 px-2 py-1 rounded-full">随心装扮</span>
|
||||
</div>
|
||||
|
||||
<!-- 分享战绩 -->
|
||||
<div
|
||||
@click="copyImageToClipboard"
|
||||
class="flex items-center justify-start gap-space px-6 py-3 bg-gradient-to-r from-green-500 to-green-600 text-white cp rounded-lg hover:from-green-600 hover:to-green-700 transition-all duration-200">
|
||||
<IconFluentCopy20Regular class="w-5 h-5"/>
|
||||
<span class="font-medium">复制到剪贴板</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
@click="downloadImage"
|
||||
class="flex items-center justify-start gap-space px-6 py-3 bg-gradient-to-r from-purple-500 to-purple-600 text-white cp rounded-lg hover:from-purple-600 hover:to-purple-700 transition-all duration-200">
|
||||
<IconFluentArrowDownload20Regular class="w-5 h-5"/>
|
||||
<span class="font-medium">保存高清海报</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
|
||||
<Dialog v-model="showWechatDialog" title="Type Words 交流群">
|
||||
<div class="w-120 p-6 pt-0">
|
||||
<div class="mb-4">
|
||||
@@ -92,4 +497,13 @@ a {
|
||||
color: unset;
|
||||
}
|
||||
|
||||
</style>
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-spin {
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
</style>
|
||||
@@ -139,131 +139,133 @@ calcWeekList(); // 新增:计算本周学习记录
|
||||
:keyboard="false"
|
||||
:show-close="false"
|
||||
class="statistics-modal">
|
||||
<div class="p-8 pr-3 bg-white rounded-2xl gap-3 flex">
|
||||
<div class="space-y-6">
|
||||
<!-- Header Section -->
|
||||
<div class="text-center relative">
|
||||
<div
|
||||
class="text-3xl font-bold mb-2 bg-gradient-to-r from-purple-500 to-purple-700 bg-clip-text text-transparent">
|
||||
<template v-if="practiceTaskWords.shuffle.length">
|
||||
🎯 随机复习完成
|
||||
</template>
|
||||
<template v-else>
|
||||
🎉 今日任务完成
|
||||
</template>
|
||||
</div>
|
||||
<p class="font-medium text-lg">{{ encouragementText }}</p>
|
||||
<div class="p-8 pr-3 bg-white rounded-2xl space-y-6">
|
||||
<!-- Header Section -->
|
||||
<div class="text-center relative">
|
||||
<div
|
||||
class="text-3xl font-bold mb-2 bg-gradient-to-r from-purple-500 to-purple-700 bg-clip-text text-transparent">
|
||||
<template v-if="practiceTaskWords.shuffle.length">
|
||||
🎯 随机复习完成
|
||||
</template>
|
||||
<template v-else>
|
||||
🎉 今日任务完成
|
||||
</template>
|
||||
</div>
|
||||
<p class="font-medium text-lg">{{ encouragementText }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Main Stats Grid -->
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<!-- Study Time -->
|
||||
<div class="item">
|
||||
<IconFluentClock20Regular class="text-purple-500"/>
|
||||
<div class="text-sm text-gray-600 mb-1 font-medium">学习时长</div>
|
||||
<div class="text-xl font-bold text-gray-900">{{ formattedStudyTime }}</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Stats Grid -->
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<!-- Study Time -->
|
||||
<div class="item">
|
||||
<IconFluentClock20Regular class="text-purple-500"/>
|
||||
<div class="text-sm text-gray-600 mb-1 font-medium">学习时长</div>
|
||||
<div class="text-xl font-bold text-gray-900">{{ formattedStudyTime }}</div>
|
||||
</div>
|
||||
|
||||
<!-- Accuracy Rate -->
|
||||
<div class="item">
|
||||
<IconFluentTarget20Regular class="text-purple-500"/>
|
||||
<div class="text-sm text-gray-600 mb-1 font-medium">正确率</div>
|
||||
<div class="text-xl font-bold text-gray-900 mb-2">{{ accuracyRate }}%</div>
|
||||
<div class="w-full bg-gray-200 rounded-full h-1" v-if="false">
|
||||
<div
|
||||
class="h-1 rounded-full transition-all duration-300"
|
||||
:class="{
|
||||
<!-- Accuracy Rate -->
|
||||
<div class="item">
|
||||
<IconFluentTarget20Regular class="text-purple-500"/>
|
||||
<div class="text-sm text-gray-600 mb-1 font-medium">正确率</div>
|
||||
<div class="text-xl font-bold text-gray-900 mb-2">{{ accuracyRate }}%</div>
|
||||
<div class="w-full bg-gray-200 rounded-full h-1" v-if="false">
|
||||
<div
|
||||
class="h-1 rounded-full transition-all duration-300"
|
||||
:class="{
|
||||
'bg-green-500': accuracyRate >= 95,
|
||||
'bg-yellow-500': accuracyRate >= 85 && accuracyRate < 95,
|
||||
'bg-red-500': accuracyRate < 85
|
||||
}"
|
||||
:style="{ width: accuracyRate + '%' }">
|
||||
</div>
|
||||
:style="{ width: accuracyRate + '%' }">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- New Words -->
|
||||
<div class="item">
|
||||
<IconFluentSparkle20Regular class="text-purple-500"/>
|
||||
<div class="text-sm text-gray-600 mb-1 font-medium">新词</div>
|
||||
<div class="text-xl font-bold text-gray-900">{{ statStore.newWordNumber }}</div>
|
||||
</div>
|
||||
|
||||
<!-- New Words -->
|
||||
<div class="item">
|
||||
<IconFluentBook20Regular class="text-purple-500"/>
|
||||
<div class="text-sm text-gray-600 mb-1 font-medium">复习</div>
|
||||
<div class="text-xl font-bold text-gray-900">{{ statStore.newWordNumber }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Weekly Progress -->
|
||||
<div class="bg-gradient-to-br from-gray-50 to-gray-100 rounded-xl p-2">
|
||||
<div class="text-center mb-4">
|
||||
<div class="text-xl font-semibold text-gray-900 mb-1">本周学习记录</div>
|
||||
</div>
|
||||
<div class="flex justify-between gap-4">
|
||||
<div
|
||||
<!-- New Words -->
|
||||
<div class="item">
|
||||
<IconFluentSparkle20Regular class="text-purple-500"/>
|
||||
<div class="text-sm text-gray-600 mb-1 font-medium">新词</div>
|
||||
<div class="text-xl font-bold text-gray-900">{{ statStore.newWordNumber }}</div>
|
||||
</div>
|
||||
|
||||
<!-- New Words -->
|
||||
<div class="item">
|
||||
<IconFluentBook20Regular class="text-purple-500"/>
|
||||
<div class="text-sm text-gray-600 mb-1 font-medium">复习</div>
|
||||
<div class="text-xl font-bold text-gray-900">{{ statStore.reviewWordNumber + statStore.writeWordNumber }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w-full gap-3 flex">
|
||||
<div class="space-y-6 flex-1">
|
||||
|
||||
<!-- Weekly Progress -->
|
||||
<div class="bg-gradient-to-br from-gray-50 to-gray-100 rounded-xl p-2">
|
||||
<div class="text-center mb-4">
|
||||
<div class="text-xl font-semibold text-gray-900 mb-1">本周学习记录</div>
|
||||
</div>
|
||||
<div class="flex justify-between gap-4">
|
||||
<div
|
||||
v-for="(item, i) in list"
|
||||
:key="i"
|
||||
class="flex-1 text-center px-2 py-3 rounded-lg"
|
||||
:class="item ? 'bg-green-500 text-white shadow-lg' : 'bg-white text-gray-700'"
|
||||
>
|
||||
<div class="font-semibold mb-1">{{ i + 1 }}</div>
|
||||
<div class="w-2 h-2 rounded-full mx-auto mb-1"
|
||||
:class="item ? 'bg-white bg-opacity-30' : 'bg-gray-300'"></div>
|
||||
>
|
||||
<div class="font-semibold mb-1">{{ i + 1 }}</div>
|
||||
<div class="w-2 h-2 rounded-full mx-auto mb-1"
|
||||
:class="item ? 'bg-white bg-opacity-30' : 'bg-gray-300'"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Progress Overview -->
|
||||
<div class="bg-gradient-to-br from-gray-50 to-gray-100 rounded-xl py-2 px-6">
|
||||
<div class="flex justify-between items-center mb-3">
|
||||
<div class="text-xl font-semibold text-gray-900">学习进度</div>
|
||||
<div class="text-2xl font-bold text-purple-600">{{ studyProgress }}%</div>
|
||||
</div>
|
||||
<Progress :percentage="studyProgress" size="large" :show-text="false"/>
|
||||
<div class="flex justify-between text-sm text-gray-600 font-medium mt-4">
|
||||
<span>已学习: {{ store.sdict.lastLearnIndex }}</span>
|
||||
<span>总词数: {{ store.sdict.length }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Progress Overview -->
|
||||
<div class="bg-gradient-to-br from-gray-50 to-gray-100 rounded-xl py-2 px-6">
|
||||
<div class="flex justify-between items-center mb-3">
|
||||
<div class="text-xl font-semibold text-gray-900">学习进度</div>
|
||||
<div class="text-2xl font-bold text-purple-600">{{ studyProgress }}%</div>
|
||||
<ChannelIcons/>
|
||||
</div>
|
||||
<!-- Action Buttons -->
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-3">
|
||||
<BaseButton
|
||||
:keyboard="settingStore.shortcutKeyMap[ShortcutKey.RepeatChapter]"
|
||||
@click="options(EventKey.repeatStudy)">
|
||||
<div class="center gap-2">
|
||||
<IconFluentArrowClockwise20Regular/>
|
||||
重学一遍
|
||||
</div>
|
||||
<Progress :percentage="studyProgress" size="large" :show-text="false"/>
|
||||
<div class="flex justify-between text-sm text-gray-600 font-medium mt-4">
|
||||
<span>已学习: {{ store.sdict.lastLearnIndex }}</span>
|
||||
<span>总词数: {{ store.sdict.length }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-3">
|
||||
<BaseButton
|
||||
:keyboard="settingStore.shortcutKeyMap[ShortcutKey.RepeatChapter]"
|
||||
@click="options(EventKey.repeatStudy)">
|
||||
<div class="center gap-2">
|
||||
<IconFluentArrowClockwise20Regular/>
|
||||
重学一遍
|
||||
</div>
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
:keyboard="settingStore.shortcutKeyMap[ShortcutKey.NextChapter]"
|
||||
@click="options(EventKey.continueStudy)"
|
||||
class="flex items-center justify-center gap-2 py-3 px-4 rounded-xl font-medium transition-all duration-300 hover:-translate-y-1 hover:shadow-lg"
|
||||
:class="dictIsEnd ? 'bg-gradient-to-r from-purple-500 to-purple-600 text-white' : 'bg-gradient-to-r from-green-500 to-green-600 text-white'">
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
:keyboard="settingStore.shortcutKeyMap[ShortcutKey.NextChapter]"
|
||||
@click="options(EventKey.continueStudy)">
|
||||
<div class="center gap-2">
|
||||
<IconFluentPlay20Regular/>
|
||||
{{ dictIsEnd ? '从头开始练习' : '再来一组' }}
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
:keyboard="settingStore.shortcutKeyMap[ShortcutKey.NextRandomWrite]"
|
||||
@click="options(EventKey.randomWrite)">
|
||||
<div class="center gap-2">
|
||||
<IconFluentPen20Regular/>
|
||||
继续默写
|
||||
</div>
|
||||
</BaseButton>
|
||||
<BaseButton @click="$router.back">
|
||||
<div class="center gap-2">
|
||||
<IconFluentHome20Regular/>
|
||||
返回主页
|
||||
</div>
|
||||
</BaseButton>
|
||||
</div>
|
||||
</div>
|
||||
</BaseButton>
|
||||
<BaseButton
|
||||
:keyboard="settingStore.shortcutKeyMap[ShortcutKey.NextRandomWrite]"
|
||||
@click="options(EventKey.randomWrite)">
|
||||
<div class="center gap-2">
|
||||
<IconFluentPen20Regular/>
|
||||
继续默写
|
||||
</div>
|
||||
</BaseButton>
|
||||
<BaseButton @click="$router.back">
|
||||
<div class="center gap-2">
|
||||
<IconFluentHome20Regular/>
|
||||
返回主页
|
||||
</div>
|
||||
</BaseButton>
|
||||
</div>
|
||||
<ChannelIcons/>
|
||||
</div>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user