32 KiB
平台文档管理功能实现计划
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 实现平台文档管理功能,支持运营端文档 CRUD + 分类管理,门户端文档展示
Architecture: 在 txw-yygl-service-biz 中新增文档和分类的 DO/Mapper/Service/Controller,前端使用 Vue 2 + mavon-editor,门户端使用 marked 渲染 Markdown
Tech Stack: Spring Boot + MyBatis-Plus + Vue 2 + TDesign + mavon-editor + marked
文件结构总览
后端(txw-yygl-service-biz)
src/main/java/com/css/txw/yygl/
├── controller/
│ └── DocumentController.java # 文档 + 分类 API
├── service/
│ ├── DocumentService.java # 接口
│ └── impl/
│ └── DocumentServiceImpl.java # 实现
├── mapper/
│ └── DocumentMapper.java # MyBatis Mapper
├── pojo/
│ ├── domain/
│ │ ├── DocumentCategoryDO.java # 分类实体
│ │ └── DocumentDO.java # 文档实体
│ └── vo/
│ ├── DocumentListVO.java # 列表响应
│ └── DocumentDetailVO.java # 详情响应
└── resources/mapper/
└── DocumentMapper.xml # Mapper XML
前端(txw-yygl-web)
src/pages/document/
├── list/index.vue # 文档列表页
├── edit/index.vue # 新增/编辑页
└── category/index.vue # 分类管理页
前端(txw-mhzc-web)
src/pages/help/
├── index.vue # 文档中心首页
└── detail/index.vue # 文档详情页
第一阶段:数据库表创建
Task 1: 创建数据库表
SQL文件: 直接在 txw-mhzc 数据库执行
-- 文档分类表
CREATE TABLE document_category (
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
name VARCHAR(100) NOT NULL COMMENT '分类名称',
sort INT DEFAULT 0 COMMENT '排序号',
status TINYINT DEFAULT 1 COMMENT '状态:0-禁用,1-启用',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
) COMMENT '文档分类表';
-- 文档表
CREATE TABLE document (
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
category_id BIGINT NOT NULL COMMENT '分类ID',
title VARCHAR(200) NOT NULL COMMENT '文档标题',
content TEXT NOT NULL COMMENT 'Markdown正文内容',
status TINYINT DEFAULT 0 COMMENT '状态:0-草稿,1-已发布',
sort INT DEFAULT 0 COMMENT '排序号',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
FOREIGN KEY (category_id) REFERENCES document_category(id)
) COMMENT '文档表';
- Step 1: 在 txw-mhzc 数据库执行上述 SQL
第二阶段:后端实现
Task 2: 创建 DO 实体类
文件: txw-yygl-service-biz/src/main/java/com/css/txw/yygl/pojo/domain/
- Step 1: 创建 DocumentCategoryDO.java
package com.css.txw.yygl.pojo.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;
@TableName(value = "document_category")
@Data
public class DocumentCategoryDO implements Serializable {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@TableField(value = "name")
private String name;
@TableField(value = "sort")
private Integer sort;
@TableField(value = "status")
private Integer status;
@TableField(value = "create_time")
private Date createTime;
@TableField(value = "update_time")
private Date updateTime;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}
- Step 2: 创建 DocumentDO.java
package com.css.txw.yygl.pojo.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;
@TableName(value = "document")
@Data
public class DocumentDO implements Serializable {
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@TableField(value = "category_id")
private Long categoryId;
@TableField(value = "title")
private String title;
@TableField(value = "content")
private String content;
@TableField(value = "status")
private Integer status;
@TableField(value = "sort")
private Integer sort;
@TableField(value = "create_time")
private Date createTime;
@TableField(value = "update_time")
private Date updateTime;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}
Task 3: 创建 VO 类
文件: txw-yygl-service-biz/src/main/java/com/css/txw/yygl/pojo/vo/
- Step 1: 创建 DocumentListVO.java
package com.css.txw.yygl.pojo.vo;
import lombok.Data;
import java.util.Date;
@Data
public class DocumentListVO {
private Long id;
private Long categoryId;
private String categoryName;
private String title;
private Integer status;
private Integer sort;
private Date createTime;
private Date updateTime;
}
- Step 2: 创建 DocumentDetailVO.java
package com.css.txw.yygl.pojo.vo;
import lombok.Data;
import java.util.Date;
@Data
public class DocumentDetailVO {
private Long id;
private Long categoryId;
private String categoryName;
private String title;
private String content;
private Integer status;
private Integer sort;
private Date createTime;
private Date updateTime;
}
- Step 3: 创建 CategoryVO.java
package com.css.txw.yygl.pojo.vo;
import lombok.Data;
import java.util.Date;
@Data
public class CategoryVO {
private Long id;
private String name;
private Integer sort;
private Integer status;
private Date createTime;
}
Task 4: 创建 Mapper 接口和 XML
文件: txw-yygl-service-biz/src/main/java/com/css/txw/yygl/mapper/
- Step 1: 创建 DocumentCategoryMapper.java
package com.css.txw.yygl.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.css.txw.yygl.pojo.domain.DocumentCategoryDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface DocumentCategoryMapper extends BaseMapper<DocumentCategoryDO> {
}
- Step 2: 创建 DocumentMapper.java
package com.css.txw.yygl.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.css.txw.yygl.pojo.domain.DocumentDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface DocumentMapper extends BaseMapper<DocumentDO> {
}
文件: txw-yygl-service-biz/src/main/resources/mapper/
- Step 3: 创建 DocumentCategoryMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.css.txw.yygl.mapper.DocumentCategoryMapper">
<resultMap id="BaseResultMap" type="com.css.txw.yygl.pojo.domain.DocumentCategoryDO">
<id property="id" column="id" jdbcType="BIGINT"/>
<result property="name" column="name" jdbcType="VARCHAR"/>
<result property="sort" column="sort" jdbcType="INTEGER"/>
<result property="status" column="status" jdbcType="TINYINT"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
</resultMap>
<sql id="Base_Column_List">id,name,sort,status,create_time,update_time</sql>
</mapper>
- Step 4: 创建 DocumentMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.css.txw.yygl.mapper.DocumentMapper">
<resultMap id="BaseResultMap" type="com.css.txw.yygl.pojo.domain.DocumentDO">
<id property="id" column="id" jdbcType="BIGINT"/>
<result property="categoryId" column="category_id" jdbcType="BIGINT"/>
<result property="title" column="title" jdbcType="VARCHAR"/>
<result property="content" column="content" jdbcType="VARCHAR"/>
<result property="status" column="status" jdbcType="TINYINT"/>
<result property="sort" column="sort" jdbcType="INTEGER"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
</resultMap>
<sql id="Base_Column_List">id,category_id,title,content,status,sort,create_time,update_time</sql>
</mapper>
Task 5: 创建 Service 层
文件: txw-yygl-service-biz/src/main/java/com/css/txw/yygl/service/
- Step 1: 创建 DocumentService.java 接口
package com.css.txw.yygl.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.css.txw.yygl.pojo.domain.DocumentDO;
import com.css.txw.yygl.pojo.vo.DocumentDetailVO;
import com.css.txw.yygl.pojo.vo.DocumentListVO;
import java.util.List;
public interface DocumentService extends IService<DocumentDO> {
List<DocumentListVO> getDocumentList(Long categoryId, Integer status);
DocumentDetailVO getDocumentDetail(Long id);
void saveDocument(DocumentDO document);
void updateDocument(DocumentDO document);
void deleteDocument(Long id);
}
- Step 2: 创建 DocumentServiceImpl.java 实现
package com.css.txw.yygl.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.css.txw.yygl.mapper.DocumentCategoryMapper;
import com.css.txw.yygl.mapper.DocumentMapper;
import com.css.txw.yygl.pojo.domain.DocumentDO;
import com.css.txw.yygl.pojo.vo.DocumentDetailVO;
import com.css.txw.yygl.pojo.vo.DocumentListVO;
import com.css.txw.yygl.service.DocumentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class DocumentServiceImpl extends ServiceImpl<DocumentMapper, DocumentDO> implements DocumentService {
@Autowired
private DocumentCategoryMapper categoryMapper;
@Override
public List<DocumentListVO> getDocumentList(Long categoryId, Integer status) {
var wrapper = new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<DocumentDO>();
if (categoryId != null) wrapper.eq(DocumentDO::getCategoryId, categoryId);
if (status != null) wrapper.eq(DocumentDO::getStatus, status);
wrapper.orderByAsc(DocumentDO::getSort).orderByDesc(DocumentDO::getCreateTime);
List<DocumentDO> list = this.list(wrapper);
return list.stream().map(doc -> {
DocumentListVO vo = new DocumentListVO();
vo.setId(doc.getId());
vo.setCategoryId(doc.getCategoryId());
vo.setTitle(doc.getTitle());
vo.setStatus(doc.getStatus());
vo.setSort(doc.getSort());
vo.setCreateTime(doc.getCreateTime());
vo.setUpdateTime(doc.getUpdateTime());
return vo;
}).collect(Collectors.toList());
}
@Override
public DocumentDetailVO getDocumentDetail(Long id) {
DocumentDO doc = this.getById(id);
if (doc == null) return null;
DocumentDetailVO vo = new DocumentDetailVO();
vo.setId(doc.getId());
vo.setCategoryId(doc.getCategoryId());
vo.setTitle(doc.getTitle());
vo.setContent(doc.getContent());
vo.setStatus(doc.getStatus());
vo.setSort(doc.getSort());
vo.setCreateTime(doc.getCreateTime());
vo.setUpdateTime(doc.getUpdateTime());
return vo;
}
@Transactional
@Override
public void saveDocument(DocumentDO document) {
this.save(document);
}
@Transactional
@Override
public void updateDocument(DocumentDO document) {
this.updateById(document);
}
@Transactional
@Override
public void deleteDocument(Long id) {
this.removeById(id);
}
}
- Step 3: 创建 DocumentCategoryService.java 接口
package com.css.txw.yygl.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.css.txw.yygl.pojo.domain.DocumentCategoryDO;
import com.css.txw.yygl.pojo.vo.CategoryVO;
import java.util.List;
public interface DocumentCategoryService extends IService<DocumentCategoryDO> {
List<CategoryVO> getCategoryList();
void saveCategory(DocumentCategoryDO category);
void updateCategory(DocumentCategoryDO category);
void deleteCategory(Long id);
}
- Step 4: 创建 DocumentCategoryServiceImpl.java 实现
package com.css.txw.yygl.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.css.txw.yygl.mapper.DocumentCategoryMapper;
import com.css.txw.yygl.pojo.domain.DocumentCategoryDO;
import com.css.txw.yygl.pojo.vo.CategoryVO;
import com.css.txw.yygl.service.DocumentCategoryService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class DocumentCategoryServiceImpl extends ServiceImpl<DocumentCategoryMapper, DocumentCategoryDO>
implements DocumentCategoryService {
@Override
public List<CategoryVO> getCategoryList() {
var wrapper = new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<DocumentCategoryDO>();
wrapper.eq(DocumentCategoryDO::getStatus, 1);
wrapper.orderByAsc(DocumentCategoryDO::getSort);
List<DocumentCategoryDO> list = this.list(wrapper);
return list.stream().map(c -> {
CategoryVO vo = new CategoryVO();
vo.setId(c.getId());
vo.setName(c.getName());
vo.setSort(c.getSort());
vo.setStatus(c.getStatus());
vo.setCreateTime(c.getCreateTime());
return vo;
}).collect(Collectors.toList());
}
@Transactional
@Override
public void saveCategory(DocumentCategoryDO category) {
this.save(category);
}
@Transactional
@Override
public void updateCategory(DocumentCategoryDO category) {
this.updateById(category);
}
@Transactional
@Override
public void deleteCategory(Long id) {
this.removeById(id);
}
}
Task 6: 创建 Controller
文件: txw-yygl-service-biz/src/main/java/com/css/txw/yygl/controller/
- Step 1: 创建 DocumentController.java
package com.css.txw.yygl.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.css.ggzc.framework.common.pojo.CommonResult;
import com.css.txw.yygl.pojo.domain.DocumentDO;
import com.css.txw.yygl.pojo.vo.DocumentDetailVO;
import com.css.txw.yygl.pojo.vo.DocumentListVO;
import com.css.txw.yygl.service.DocumentService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
@RestController
@RequestMapping("/document")
@Tag(name = "文档管理")
@Validated
public class DocumentController {
@Resource
private DocumentService documentService;
@GetMapping("/list")
@Operation(summary = "文档列表")
public CommonResult<List<DocumentListVO>> getDocumentList(
@RequestParam(required = false) Long categoryId,
@RequestParam(required = false) Integer status) {
return CommonResult.success(documentService.getDocumentList(categoryId, status));
}
@GetMapping("/{id}")
@Operation(summary = "文档详情")
public CommonResult<DocumentDetailVO> getDocumentDetail(@PathVariable Long id) {
return CommonResult.success(documentService.getDocumentDetail(id));
}
@PostMapping
@Operation(summary = "新增文档")
public CommonResult<String> saveDocument(@RequestBody DocumentDO document) {
documentService.saveDocument(document);
return CommonResult.success("success");
}
@PutMapping("/{id}")
@Operation(summary = "编辑文档")
public CommonResult<String> updateDocument(@PathVariable Long id, @RequestBody DocumentDO document) {
document.setId(id);
documentService.updateDocument(document);
return CommonResult.success("success");
}
@DeleteMapping("/{id}")
@Operation(summary = "删除文档")
public CommonResult<String> deleteDocument(@PathVariable Long id) {
documentService.deleteDocument(id);
return CommonResult.success("success");
}
}
- Step 2: 创建 DocumentCategoryController.java
package com.css.txw.yygl.controller;
import com.css.ggzc.framework.common.pojo.CommonResult;
import com.css.txw.yygl.pojo.domain.DocumentCategoryDO;
import com.css.txw.yygl.pojo.vo.CategoryVO;
import com.css.txw.yygl.service.DocumentCategoryService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
@RestController
@RequestMapping("/document/category")
@Tag(name = "文档分类管理")
@Validated
public class DocumentCategoryController {
@Resource
private DocumentCategoryService categoryService;
@GetMapping("/list")
@Operation(summary = "分类列表")
public CommonResult<List<CategoryVO>> getCategoryList() {
return CommonResult.success(categoryService.getCategoryList());
}
@PostMapping
@Operation(summary = "新增分类")
public CommonResult<String> saveCategory(@RequestBody DocumentCategoryDO category) {
categoryService.saveCategory(category);
return CommonResult.success("success");
}
@PutMapping("/{id}")
@Operation(summary = "编辑分类")
public CommonResult<String> updateCategory(@PathVariable Long id, @RequestBody DocumentCategoryDO category) {
category.setId(id);
categoryService.updateCategory(category);
return CommonResult.success("success");
}
@DeleteMapping("/{id}")
@Operation(summary = "删除分类")
public CommonResult<String> deleteCategory(@PathVariable Long id) {
categoryService.deleteCategory(id);
return CommonResult.success("success");
}
}
第三阶段:前端实现(txw-yygl-web)
Task 7: 安装 Markdown 编辑器依赖
- Step 1: 在 txw-yygl-web 安装 mavon-editor
cd txw-yygl-web && npm install mavon-editor --save
Task 8: 创建文档管理页面
文件: txw-yygl-web/src/pages/document/list/index.vue
- Step 1: 创建文档列表页
<template>
<div class="document-list-container">
<div class="filter-bar">
<t-select v-model="filters.categoryId" :options="categoryOptions" placeholder="选择分类" clearable />
<t-select v-model="filters.status" :options="statusOptions" placeholder="选择状态" clearable />
<t-button @click="loadDocumentList">查询</t-button>
<t-button theme="primary" @click="goToEdit">新建文档</t-button>
</div>
<t-table :data="documentList" :columns="columns" row-key="id">
<template #operations="{ row }">
<t-button size="small" @click="goToEdit(row.id)">编辑</t-button>
<t-button size="small" theme="danger" @click="deleteDocument(row.id)">删除</t-button>
</template>
</t-table>
</div>
</template>
<script>
export default {
name: 'DocumentList',
data() {
return {
filters: { categoryId: null, status: null },
categoryOptions: [],
statusOptions: [
{ label: '草稿', value: 0 },
{ label: '已发布', value: 1 }
],
documentList: [],
columns: [
{ colKey: 'id', title: 'ID' },
{ colKey: 'title', title: '标题' },
{ colKey: 'categoryName', title: '分类' },
{ colKey: 'status', title: '状态', formatter: (v) => v === 1 ? '已发布' : '草稿' },
{ colKey: 'createTime', title: '创建时间' },
{ slotName: 'operations', title: '操作' }
]
}
},
mounted() {
this.loadCategoryList()
this.loadDocumentList()
},
methods: {
loadCategoryList() {
this.$request.get('/document/category/list').then(res => {
if (res.data.code === 0) {
this.categoryOptions = res.data.data.map(c => ({ label: c.name, value: c.id }))
}
})
},
loadDocumentList() {
this.$request.get('/document/list', { params: this.filters }).then(res => {
if (res.data.code === 0) {
this.documentList = res.data.data
}
})
},
goToEdit(id) {
this.$router.push(id ? `/document/edit/${id}` : '/document/edit')
},
deleteDocument(id) {
this.$request.delete(`/document/${id}`).then(res => {
if (res.data.code === 0) {
this.$message.success('删除成功')
this.loadDocumentList()
}
})
}
}
}
</script>
文件: txw-yygl-web/src/pages/document/edit/index.vue
- Step 2: 创建文档编辑页(含 mavon-editor)
<template>
<div class="document-edit-container">
<t-form :model="document" label-width="100px">
<t-form-item label="所属分类">
<t-select v-model="document.categoryId" :options="categoryOptions" placeholder="请选择分类" />
</t-form-item>
<t-form-item label="文档标题">
<t-input v-model="document.title" placeholder="请输入文档标题" />
</t-form-item>
<t-form-item label="排序号">
<t-input-number v-model="document.sort" :min="0" />
</t-form-item>
<t-form-item label="状态">
<t-radio-group v-model="document.status">
<t-radio :value="0">草稿</t-radio>
<t-radio :value="1">已发布</t-radio>
</t-radio-group>
</t-form-item>
<t-form-item label="文档内容">
<mavon-editor v-model="document.content" />
</t-form-item>
<t-form-item>
<t-button theme="primary" @click="saveDocument">保存</t-button>
<t-button @click="goBack">取消</t-button>
</t-form-item>
</t-form>
</div>
</template>
<script>
import { mavonEditor } from 'mavon-editor'
import 'mavon-editor/dist/css/index.css'
export default {
name: 'DocumentEdit',
components: { mavonEditor },
data() {
return {
document: {
id: null,
categoryId: null,
title: '',
content: '',
status: 0,
sort: 0
},
categoryOptions: []
}
},
mounted() {
this.loadCategoryList()
const id = this.$route.params.id
if (id) {
this.loadDocument(id)
}
},
methods: {
loadCategoryList() {
this.$request.get('/document/category/list').then(res => {
if (res.data.code === 0) {
this.categoryOptions = res.data.data.map(c => ({ label: c.name, value: c.id }))
}
})
},
loadDocument(id) {
this.$request.get(`/document/${id}`).then(res => {
if (res.data.code === 0) {
this.document = res.data.data
}
})
},
saveDocument() {
const id = this.document.id
const promise = id
? this.$request.put(`/document/${id}`, this.document)
: this.$request.post('/document', this.document)
promise.then(res => {
if (res.data.code === 0) {
this.$message.success('保存成功')
this.goBack()
}
})
},
goBack() {
this.$router.back()
}
}
}
</script>
文件: txw-yygl-web/src/pages/document/category/index.vue
- Step 3: 创建分类管理页
<template>
<div class="category-manage-container">
<div class="toolbar">
<t-button theme="primary" @click="showAddDialog">新建分类</t-button>
</div>
<t-table :data="categoryList" :columns="columns" row-key="id">
<template #operations="{ row }">
<t-button size="small" @click="showEditDialog(row)">编辑</t-button>
<t-button size="small" theme="danger" @click="deleteCategory(row.id)">删除</t-button>
</template>
</t-table>
<t-dialog :visible="dialogVisible" @close="dialogVisible = false" :header="isEdit ? '编辑分类' : '新建分类'">
<t-form :model="category" label-width="80px">
<t-form-item label="分类名称">
<t-input v-model="category.name" />
</t-form-item>
<t-form-item label="排序号">
<t-input-number v-model="category.sort" :min="0" />
</t-form-item>
<t-form-item label="状态">
<t-radio-group v-model="category.status">
<t-radio :value="0">禁用</t-radio>
<t-radio :value="1">启用</t-radio>
</t-radio-group>
</t-form-item>
</t-form>
<template #footer>
<t-button @click="dialogVisible = false">取消</t-button>
<t-button theme="primary" @click="saveCategory">确定</t-button>
</template>
</t-dialog>
</div>
</template>
<script>
export default {
name: 'CategoryManage',
data() {
return {
categoryList: [],
dialogVisible: false,
isEdit: false,
category: { id: null, name: '', sort: 0, status: 1 },
columns: [
{ colKey: 'id', title: 'ID' },
{ colKey: 'name', title: '分类名称' },
{ colKey: 'sort', title: '排序号' },
{ colKey: 'status', title: '状态', formatter: (v) => v === 1 ? '启用' : '禁用' },
{ slotName: 'operations', title: '操作' }
]
}
},
mounted() {
this.loadCategoryList()
},
methods: {
loadCategoryList() {
this.$request.get('/document/category/list').then(res => {
if (res.data.code === 0) {
this.categoryList = res.data.data
}
})
},
showAddDialog() {
this.isEdit = false
this.category = { id: null, name: '', sort: 0, status: 1 }
this.dialogVisible = true
},
showEditDialog(row) {
this.isEdit = true
this.category = { ...row }
this.dialogVisible = true
},
saveCategory() {
const promise = this.isEdit
? this.$request.put(`/document/category/${this.category.id}`, this.category)
: this.$request.post('/document/category', this.category)
promise.then(res => {
if (res.data.code === 0) {
this.$message.success('保存成功')
this.dialogVisible = false
this.loadCategoryList()
}
})
},
deleteCategory(id) {
this.$request.delete(`/document/category/${id}`).then(res => {
if (res.data.code === 0) {
this.$message.success('删除成功')
this.loadCategoryList()
}
})
}
}
}
</script>
第四阶段:前端实现(txw-mhzc-web)
Task 9: 安装 Markdown 渲染依赖
- Step 1: 在 txw-mhzc-web 安装 marked 和 highlight.js
cd txw-mhzc-web && npm install marked highlight.js --save
Task 10: 创建门户文档中心页面
文件: txw-mhzc-web/src/pages/help/index.vue
- Step 1: 创建文档中心首页
<template>
<div class="help-center">
<div class="category-list">
<div
v-for="cat in categoryList"
:key="cat.id"
:class="['category-item', { active: selectedCategoryId === cat.id }]"
@click="selectCategory(cat.id)"
>
{{ cat.name }}
</div>
</div>
<div class="document-list">
<div
v-for="doc in documentList"
:key="doc.id"
class="document-item"
@click="goToDetail(doc.id)"
>
<span class="doc-title">{{ doc.title }}</span>
<span class="doc-date">{{ doc.createTime }}</span>
</div>
<div v-if="documentList.length === 0" class="empty">暂无文档</div>
</div>
</div>
</template>
<script>
export default {
name: 'HelpCenter',
data() {
return {
categoryList: [],
selectedCategoryId: null,
documentList: []
}
},
mounted() {
this.loadCategoryList()
this.loadDocumentList()
},
methods: {
loadCategoryList() {
this.$request.get('/document/category/list').then(res => {
if (res.data.code === 0) {
this.categoryList = res.data.data
}
})
},
loadDocumentList() {
this.$request.get('/document/list', { params: { status: 1 } }).then(res => {
if (res.data.code === 0) {
this.documentList = res.data.data
}
})
},
selectCategory(categoryId) {
this.selectedCategoryId = categoryId
this.$request.get('/document/list', { params: { categoryId, status: 1 } }).then(res => {
if (res.data.code === 0) {
this.documentList = res.data.data
}
})
},
goToDetail(id) {
this.$router.push(`/help/${id}`)
}
}
}
</script>
文件: txw-mhzc-web/src/pages/help/detail/index.vue
- Step 2: 创建文档详情页(Markdown 渲染)
<template>
<div class="document-detail">
<div class="back-btn" @click="goBack">返回</div>
<h1 class="doc-title">{{ document.title }}</h1>
<div class="doc-content" v-html="renderedContent"></div>
</div>
</template>
<script>
import marked from 'marked'
import hljs from 'highlight.js'
import 'highlight.js/styles/github.css'
export default {
name: 'DocumentDetail',
data() {
return {
document: { id: null, title: '', content: '' }
}
},
computed: {
renderedContent() {
marked.setOptions({ highlight: (code, lang) => hljs.highlight(code, { language: lang }).value })
return marked(this.document.content || '')
}
},
mounted() {
const id = this.$route.params.id
if (id) {
this.loadDocument(id)
}
},
methods: {
loadDocument(id) {
this.$request.get(`/document/${id}`).then(res => {
if (res.data.code === 0) {
this.document = res.data.data
}
})
},
goBack() {
this.$router.back()
}
}
}
</script>
第五阶段:路由配置
Task 11: 配置 yygl-web 路由
文件: txw-yygl-web/src/core/router/
- Step 1: 在路由配置中添加文档管理路由
在现有路由配置中新增:
{
path: '/document',
component: () => import('@/pages/document'),
children: [
{ path: 'list', component: () => import('@/pages/document/list') },
{ path: 'edit/:id?', component: () => import('@/pages/document/edit') },
{ path: 'category', component: () => import('@/pages/document/category') }
]
}
Task 12: 配置 mhzc-web 路由
文件: txw-mhzc-web/src/core/router/
- Step 1: 在路由配置中添加文档中心路由
{
path: '/help',
component: () => import('@/pages/help'),
children: [
{ path: '', component: () => import('@/pages/help/index') },
{ path: ':id', component: () => import('@/pages/help/detail') }
]
}
执行方式
Plan 完成。两种执行方式:
- Subagent-Driven(推荐) - 每 task 由独立子 agent 执行,任务间有审核
- Inline Execution - 在当前会话中批量执行,带检查点审核
选择哪种方式?