概述:
本篇是接著上一篇,細分出說明書的編寫部分,實現這個功能的需求,是內部很多同事反饋,需要有個地方存工具,并且可以寫說明書,如果需要的人,那么可以在界面上直接下載工具和查看工具的說明,這樣就不用每次都找人發文檔,各種本地找,很浪費時間,故此需要實現這樣的一個功能
新建說明書表
CREATE TABLE IF NOT EXISTS `manual` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵ID',`tool_id` int(11) NOT NULL COMMENT '關聯的工具ID',`version` varchar(20) NOT NULL DEFAULT '1.0' COMMENT '版本號',`title` varchar(255) NOT NULL COMMENT '說明書標題',`content` text COMMENT '富文本內容',`file_path` varchar(255) DEFAULT NULL COMMENT '附件存儲路徑',`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',PRIMARY KEY (`id`),UNIQUE KEY `idx_tool_version` (`tool_id`, `version`) COMMENT '工具ID和版本號的唯一索引',KEY `idx_tool_id` (`tool_id`) COMMENT '工具ID索引'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工具說明書表';
驗證數據表
-- 給 manual 表添加缺失的 version 和 file_path 字段
ALTER TABLE manual
ADD COLUMN version VARCHAR(20) NOT NULL DEFAULT '1.0' COMMENT '版本號',
ADD COLUMN file_path VARCHAR(255) NULL COMMENT '文件路徑';-- 驗證字段是否添加成功
DESCRIBE manual; -- 應顯示所有字段:id, tool_id, title, content, version, file_path, created_at
建立數據表模型
# app/models.py(Manual 模型定義)
from datetime import datetime
from extensions import dbclass Manual(db.Model):__tablename__ = 'manual' # 表名必須與數據庫一致id = db.Column(db.Integer, primary_key=True)tool_id = db.Column(db.Integer, nullable=False, comment='工具ID')title = db.Column(db.String(255), nullable=False, comment='標題') # 確保表中有 title 字段content = db.Column(db.Text, comment='富文本內容')version = db.Column(db.String(20), default='1.0', comment='版本號') # 新增字段file_path = db.Column(db.String(255), nullable=True, comment='文件路徑') # 新增字段created_at = db.Column(db.DateTime, default=datetime.now, comment='創建時間')# 確保沒有其他多余字段(如 updated_at 若表中不存在需刪除)__table_args__ = (db.Index('idx_tool_id', 'tool_id'), # 添加索引)
新增保存說明書的接口和獲取說明書的接口
保存說明書的接口開發
@tool_bp.post('/manual/save')
def save_manual():try:data = request.get_json()if not data:return jsonify({"code": 40000,"message": "請求數據不能為空","data": None,"total": 0})# 強制校驗字段required = ['tool_id', 'title', 'content']if not all(k in data for k in required):return jsonify({"code": 40000,"message": f"缺少必填字段: {', '.join(required)}","data": None,"total": 0})# 類型檢查try:tool_id = int(data['tool_id'])except ValueError:return jsonify({"code": 40000,"message": "tool_id必須為整數","data": None,"total": 0})# 數據庫操作manual = Manual.query.filter_by(tool_id=tool_id).first()if manual:manual.title = data['title']manual.content = data['content']else:manual = Manual(tool_id=tool_id,title=data['title'],content=data['content'])db.session.add(manual)db.session.commit()return jsonify({"code": 20000,"message": "保存成功","data": {"id": manual.id},"total": 1})except Exception as e:db.session.rollback()current_app.logger.error(f"保存失敗: {str(e)}") # 記錄詳細錯誤return jsonify({"code": 40000,"message": f"保存失敗: {str(e)}", # 返回具體錯誤信息"data": None,"total": 0})
驗證接口是不是可以保存數據成功,接口成功,至于前端界面的集成編輯器功能,我們在前面的文章中有提到,如何在vue2.x中集成編輯器,可以往上看上一篇文章;
另外這里的說明書我在此基礎上增加了一個PDF導出的功能,說明書如果想發送給別人,那么這里可以直接導出,這樣就可以在本地看到一個文件,也可以發送給其他人員
目前我們說明書部分可以保存了,接下來需要實現一個接口,從數據庫中讀取我們的數據展示,這樣每次點擊查看說明書時,默認展示存儲的說明書數據
開發接口
@tool_bp.route('/info/<int:tool_id>', methods=['GET'])
def get_tool_info(tool_id):"""獲取工具基本信息(原接口,包含 toolId、toolName 等)"""tool = Tool.query.get(tool_id) return jsonify({"code": 20000,"data": {"toolId": tool.id,"toolName": tool.name,"manuals": [] }})
獲取指定工具說明書,點擊后自動獲取工具關聯的說明書數據
@tool_bp.route('/manual/<int:tool_id>', methods=['GET']) # 說明書回顯接口(專屬)
def get_manual(tool_id):"""獲取指定工具的說明書(僅返回 title 和 content)"""manual = Manual.query.filter_by(tool_id=tool_id).first()return jsonify({"code": 20000,"data": {"title": manual.title if manual else "","content": manual.content if manual else ""}})
驗證下效果,點擊查看說明書后跳轉如下
至此,說明書關聯部分開發完成,完整前端代碼如下
<template><div class="manual-edit-container"><el-card><!-- 標題區域 --><div slot="header" class="card-header"><el-breadcrumb separator="/"><el-breadcrumb-item>工具管理</el-breadcrumb-item><el-breadcrumb-item>編輯說明書</el-breadcrumb-item></el-breadcrumb></div><!-- 表單內容 --><el-form ref="form" :model="form" label-width="120px"><!-- 說明書標題 --><el-form-item label="說明書標題" required><el-inputv-model="form.title"placeholder="請輸入標題"maxlength="200"show-word-limitstyle="width: 600px"/></el-form-item><!-- 富文本編輯器 --><el-form-item label="說明書內容" required><TinymceEditorv-model="form.content":height="500":disabled="loading"/></el-form-item><!-- 操作按鈕 --><el-form-item><el-buttontype="primary"@click="handleSave":loading="loading"><i class="el-icon-check"></i> 保存</el-button><el-buttontype="success"@click="handleExportPDF":disabled="!form.content.trim()"><i class="el-icon-download"></i> 導出PDF</el-button><el-button @click="handleCancel">取消</el-button></el-form-item></el-form></el-card></div>
</template><script>
import TinymceEditor from '@/components/TinymceEditor.vue' // 富文本編輯器組件
import axios from 'axios' // HTTP請求庫
import html2pdf from 'html2pdf.js' // PDF導出庫export default {name: 'ManualEdit',components: { TinymceEditor },data() {return {form: {tool_id: this.$route.params.id, // 從路由獲取工具ID(例如31)title: '', // 存儲數據庫中的標題content: '' // 存儲數據庫中的富文本內容},loading: false // 保存按鈕加載狀態}},created() {// 頁面加載時立即從數據庫獲取數據this.loadManualFromDatabase()},methods: {/*** 從數據庫加載說明書數據(核心方法)*/async loadManualFromDatabase() {// 1. 顯示加載提示this.$message.info('正在加載說明書數據...')try {// 2. 調用后端回顯接口(已驗證返回正確數據)const response = await axios.get(`http://172.16.60.60:5000/api/tool/manual/${this.form.tool_id}`)// 3. 驗證接口響應格式if (response.data.code === 20000) {const manualData = response.data.data || {}// 4. 賦值到表單(覆蓋默認空值)this.form.title = manualData.title || '未命名說明書'this.form.content = manualData.content || '<p>請輸入說明書內容...</p>'this.$message.success('加載成功!')} else {this.$message.warning('未找到說明書數據')}} catch (error) {// 5. 捕獲網絡錯誤this.$message.error(`加載失敗: ${error.message || '網絡異常'}`)}},/*** 保存數據到數據庫*/async handleSave() {// 1. 基礎校驗if (!this.form.title.trim()) {this.$message.warning('請輸入說明書標題')return}if (!this.form.content.trim()) {this.$message.warning('請輸入說明書內容')return}this.loading = truetry {// 2. 調用保存接口(確保后端保存接口路徑正確)const response = await axios.post('http://172.16.60.60:5000/api/tool/manual/save', this.form)// 3. 處理響應if (response.data.code === 20000) {this.$message.success('保存成功!')} else {this.$message.error(`保存失敗: ${response.data.message || '未知錯誤'}`)}} catch (error) {this.$message.error(`請求失敗: ${error.message}`)} finally {this.loading = false}},/*** 導出PDF(保留功能)*/handleExportPDF() {const opt = {margin: 15,filename: `${this.form.title || '說明書'}.pdf`,image: { type: 'jpeg', quality: 0.98 },html2canvas: { scale: 2, useCORS: true },jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' }}// 導出當前編輯器內容html2pdf().from(document.querySelector('.tox-edit-area__iframe').contentDocument.body).set(opt).save()},/*** 取消編輯返回上一頁*/handleCancel() {this.$router.go(-1)}}
}
</script><style scoped>
.card-header {background-color: #f5f7fa;padding: 10px 20px;
}
.manual-edit-container {padding: 20px;
}
</style>
?
?