一、翻译与扩写的持久化分析
组内同学已经开发好了文章生成与翻译的功能,接下来只需要仿照之前的题集的设计,分别设计存放文章和翻译的数据库表即可。
这是保存文章生成的数据库表:
CREATE TABLE `articles` (
`article_id` bigint NOT NULL AUTO_INCREMENT,
`article_style` enum('COLLOQUIAL','FORMAL','GENERAL','HUMOROUS','LITERARY','PROFESSIONAL') COLLATE utf8mb4_general_ci DEFAULT NULL,
`article_type` enum('ARGUMENTATIVE','ENGLISH','ESSAY','EXPOSITORY','GENERAL','NOVEL','REPORT') COLLATE utf8mb4_general_ci DEFAULT NULL,
`content` longtext COLLATE utf8mb4_general_ci,
`conversation_id` int DEFAULT NULL,
`created_at` datetime(6) DEFAULT NULL,
`is_expanded` tinyint(1) DEFAULT '0',
`title` varchar(500) COLLATE utf8mb4_general_ci DEFAULT NULL,
`updated_at` datetime(6) DEFAULT NULL,
`user_id` int DEFAULT NULL,
`word_count` enum('EXTRA_LONG','LONG','MEDIUM','SHORT') COLLATE utf8mb4_general_ci DEFAULT NULL,
`original_article_id` bigint DEFAULT NULL,
PRIMARY KEY (`article_id`),
KEY `fk_article_original` (`original_article_id`),
CONSTRAINT `fk_article_original` FOREIGN KEY (`original_article_id`) REFERENCES `articles` (`article_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
核心字段
主键:article_id 作为自增主键,确保每条记录唯一标识
内容存储:content 使用 longtext 类型存储文章正文,支持大容量文本
分类与属性
文章风格:article_style 枚举类型,定义了6种文章风格(口语化、正式、通用、幽默、文学、专业)
文章类型:article_type 枚举类型,定义了7种文章类型(议论文、英语、散文、说明文、通用、小说、报告)
字数分类:word_count 枚举类型,将文章按长度分为4类(超长、长、中、短)
时间与状态
时间戳:created_at 和 updated_at 记录创建和更新时间
扩展状态:is_expanded 布尔值,可能标记文章是否被展开或扩展过
其他设计考虑
字符集:使用 utf8mb4 支持完整Unicode字符(包括emoji)
默认值:is_expanded 默认0,article_style 和 article_type 可为NULL
索引:仅主键索引,没有额外索引(可能根据查询需求后续添加)
接下来是翻译结果的保存:
CREATE TABLE `translations` (
`translation_id` bigint NOT NULL AUTO_INCREMENT,
`created_at` datetime(6) DEFAULT NULL,
`example_count` int DEFAULT NULL,
`examples` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci,
`include_examples` bit(1) DEFAULT NULL,
`learning_guidance` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci,
`learning_level` enum('ACADEMIC','ADVANCED','BEGINNER','INTERMEDIATE') COLLATE utf8mb4_general_ci DEFAULT NULL,
`original_text` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci,
`translated_text` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci,
`translation_type` enum('PARAGRAPH','PHRASE','SENTENCE','WORD') COLLATE utf8mb4_general_ci DEFAULT NULL,
`user_id` int DEFAULT NULL,
`updated_at` datetime(6) DEFAULT NULL,
`conversation_id` int DEFAULT NULL,
PRIMARY KEY (`translation_id`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
原文与译文:
original_text 存储待翻译的原始文本
translated_text 存储翻译后的文本
两者均使用 text 类型,支持较长文本但非超大内容
翻译元数据
翻译类型:translation_type 枚举定义4种翻译粒度(段落、短语、句子、单词)
学习级别:learning_level 枚举定义4种难度级别(高级、初级、中级)
时间戳:created_at 和 updated_at 记录创建和更新时间
教学辅助功能
学习指导:learning_guidance 提供与翻译相关的学习建议或解释
示例系统:
examples 存储相关用法示例
example_count 记录示例数量
include_examples 位类型标记是否包含示例
二、前端入口
在个人中心与生成页面都有历史查看的入口:
三、具体展现
翻译的查看如下,提供了按时间排序的功能,点击还可以查看学习指导:
文章的浏览也是类似,点击以展开全文:
排序与分页功能的实现:
<div
v-for="(article, index) in paginatedArticles"
:key="article.translationId"
class="record-card"
:class="{ expanded: expandedCard === index }"
@click="toggleExpand(index)"
>
<!-- 卡片头部 -->
<div class="card-header">
<div class="card-main-info">
<div class="text-pair">
<div class="original-text">
<span class="text-label">原文</span>
<span class="text-content">{{ article.originalText }}</span>
</div>
<div class="translated-text">
<span class="text-label">译文</span>
<span class="text-content">{{ article.translation }}</span>
</div>
</div>
</div>
<div class="card-meta">
<div class="meta-item">
<div class="meta-icon">
<el-icon><Calendar /></el-icon>
</div>
<span class="meta-text">{{
formatDate(article.updatedAt)
}}</span>
</div>
</div>
</div>
<!-- 展开内容 -->
<div class="card-content" v-if="expandedCard === index">
<!-- 学习指导 -->
<div class="content-section">
<div class="section-icon guidance-icon">
<el-icon><Reading /></el-icon>
</div>
<div class="section-body">
<h3 class="section-title-small">学习指导</h3>
<div
class="section-text"
v-html="formatLearningGuidance(article.learningGuidance)"
></div>
</div>
</div>
// 计算属性:过滤和排序后的文章列表
const filteredArticles = computed(() => {
let result = [...articles.value];
// 排序
if (sortBy.value === "date-desc") {
result.sort((a, b) => new Date(b.updatedAt) - new Date(a.updatedAt));
} else if (sortBy.value === "date-asc") {
result.sort((a, b) => new Date(a.updatedAt) - new Date(b.updatedAt));
}
return result;
});
// 分页后的文章列表
const paginatedArticles = computed(() => {
const start = (currentPage.value - 1) * pageSize.value;
const end = start + pageSize.value;
return filteredArticles.value.slice(start, end);
});