1、需求简述
导入Word习题进行解析渲染,可能存在一些不准确的地方,然后进行微调,进而保存习题入库。
如下图详细见需求文档
图1
2、需求简析
第一 、内容同步,对原始切割数据originCutData进行区域切割,切割数据变化后会更新在线编辑区块内容,
对在线编辑里面的内容进行编辑后会同步至预览区块,进而导致预览区块的内容更新。
第二、滚动同步,任何区域内容滚动都会同时
第三、数据转化与校验, 编辑完内容后会对数据进行校验,将数据结构转化为库里面对应的结构。
3、模块设计实现
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类型声明等。