1、需求简述

导入Word习题进行解析渲染,可能存在一些不准确的地方,然后进行微调,进而保存习题入库。

如下图详细见需求文档
图1

2、需求简析

第一 、内容同步,对原始切割数据originCutData进行区域切割,切割数据变化后会更新在线编辑区块内容,
对在线编辑里面的内容进行编辑后会同步至预览区块,进而导致预览区块的内容更新。

第二、滚动同步,任何区域内容滚动都会同时

第三、数据转化与校验, 编辑完内容后会对数据进行校验,将数据结构转化为库里面对应的结构。

3、模块设计实现

image.png

3.1 模块内区块组件拆分

路由页面容器组件exerciseContainer,设计成tools工具栏模块,cut-fragment模块, edit-fragment模块和preview-fragment模块。
tools工具栏模块:提供各种操作按钮,包括完成编辑,还原数据等
cut-fragment模块:用来实现对原始数据解析引擎解析不正确的地方进行切割识别校正。只要这里一变化就需要更新编辑和预览组件。
edit-fragment模块:用来对切割后的数据做进一步的校正。例如修改错误、添加选项或者修改答案等。
preview-fragment模块:用来显示编辑校正后的数据和显示错误信息,以提醒用户去修改。
图2

3.2 各个区块内的数据结构

1)、原始切割数据

interface FragDataItem {
blockTag: number;
isAnswer: 0 | 1;
textBlock: string[];
height?: string | number;
}

2)、在线编辑内数据结构

interface ExtItem {
// 后端很多地方错了,要改很多处
diffculty: string;
score: number;
questionExplain: string;
questionAnswer: string;
bodyItems: ExtBodyItem[];
questionTypeDetail: string;
questionOptions: string[];
questionOptionDtoList: OptionItem []
body: string;
questionType: number;
questionText: string;
extended: string[];
knowledge: string [];
uid?: string;
errorMsg?: string;
}

3)、入库的数据结构

interfaceMetaResources {
body:string;
// 格式化题干
formatBody:string;
// 选项
questionOptionDtoList:OptionItem [];
// 习题类型 选择、判断等
questionType:number;
// 答案
answer:string;
answerList:string[];
// 填空题的空数量
blankCount?:number;
// 解析
explain:string;
// 希沃知识点ID列表
knowledgeIds?:string[];
// 难度
difficulty?:string;
subject?:string;
// 附加信息
ext?:any;
// 渠道知识点ID列表
metaKnowledgeIds?:string[];
bodyItems?:BodyItemDetail[];
seewoId?:string;
appCode?:string;
authorId?:string;
tags?:TagItem[];
// 渠道资源ID
uid?:string;
// 多媒体数据url (未用)
multimedia?:string[];
// 习题题型,如完形填空、阅读表达
questionClass?:string;
// 希沃知识点ID列表
chapterIds?:string[];
// 渠道章节ID列表
metaChapterIds?:string[];
}

3.3 全局共享状态数据

  • 原始状态数据【originListState】:用于判断还原数据,点击还原时,恢复原始数据
  • 原始切割列表数据【fragmentListState】:作为获取编辑数据的参数
  • 预览数据【previewListState】:用于渲染预览区块组件
  • 错误数据【errorListState】:用做点击完成时更新预览错误提示
  • 滚动数据【scrollInfoState】:用于滚动信息同步

如下声明的状态数据

3.4 核心组件-切割组件的设计与实现

需要通过对一个题目拖拽来实现题目切割调整。如下视频所示
首先接受数据列表渲染切割数据列表,然后针对每个数据渲染虚拟的拖拽框,拖拽完后把数据暴露给使用者。

组件接受参数

interface CutFragmentProps {
// 数据列表
list: FragDataList;
// 可拖拽的方向
enableDirection?: DirectionMap;
// 拖拽完的回调
onChange: (data: FragDataList) => void;
}

第一步 模拟实现可拖拽的上下边框区域,可支持四个方向,目前需求仅要求上下
当鼠标移动至上下5px左右即可显示可拖拽样式,在中间时则不显示拖拽样式。

第二步 在拖拽元素上监听鼠标按下、移动和松开事件来做对应的逻辑处理。

  • 按下按键的时候记录以下信息,
  • 鼠标在移动过程中,更新高度信息,做好事件节流,以避免太过频繁的触发事件。
  • 鼠标松开的时候,要根据拖拽的众向距离,拖拽的方向和拖拽上下边框来判断是如何来操作数据。这里的核心逻辑是数据变化切割。

3.5 核心组件-编辑元素组件

在线编辑模块内容的所有元素均要求可编辑,目前是以标签的contentEditable属性来简单实现,内容变化则通过onInput来监听变化。

  • 支持元素是否可编辑.
  • 延时时间配置以降低触发频率.
  • 支持自定义验证配置.
  • 对内容进行自定义的格式化处理。
  • DOM引用,后续将会扩展富文本编辑,全局实例化一个富文本编辑器,当哪个元素获得焦点时,则把富文本编辑挂载过去,以满足更丰富的需求。

组件详细参数

interface EditableElementProps {
// 编辑内容
htmlText: string | React.ReactNode;
// 内容变化的回调
onChange: (value) => void;
classNames?: string;
isEditable?: boolean;
delay?: number;
format?: (value: string) => string;
validate?: (value: string) => boolean;
ref?: React.RefObject;
}
contentEditable={isEditable}
className={classNames}
dangerouslySetInnerHTML={{__html: htmlText}}
onInput={onInputHandle}
/>

4、埋点、性能与异常处理设计

4.1 埋点接入

[

](http://doc.psd.gz.cvte.cn/friday-doc/index.html)

4.2 异常处理设计及处理

4.3 性能与错误数据上报与分析处理

数据收集使用线程feMonitor。
[

](https://kb.cvte.com/pages/viewpage.action?pageId=175779454)

5、总结

1、在设计时充分考虑可扩展性和可维护性
2、由于项目时间紧,功能实现尽量选择以快而稳的方式。
3、项目文档说明非常详细包括设计目录结构、实现逻辑和TS类型声明等。