说明书编辑平台设计与开发实践

104 阅读28分钟

一、背景

1.1 工作困境

实际工作中,每年新上线的多款单品都需要制作相似交互的线上说明书页面,同时还需要支持多语言。说明书页面具有特定的模板和固定的样式:

https://web-api.textin.com/ocr_image/external/ccee8a9ac3b1fa99.jpg

另外,说明书大多需要支持多语言,多语言文案以excel文档的方式提供(如下图),目前需要支持的语言多达20+。

https://cdn.gamma.app/uxqgoqz787tn07l/26a103895b944024a68bd65aa59072d4/original/Pasted-image-20240924192631.png

所以,每次有新的在线说明书需求过来,开发过程大致是这样的:

1、把旧的说明书文件复制一份出来;

2、按照实际排版需求,修改每个页面的html代码(基本上只需要写HTML代码,CSS样式可以直接复用),并将文案从excel文档逐一复制到html代码中。

每种语言的页面实现都重复以上1-2步骤。输出大概如下:

https://i.imgur.com/eR9oUCx.png

可以看出,大部分开发工作变成了文案的复制黏贴,开发变成了没有感情的复制黏贴工具人。而且还存在效率低、易出错和重复性高的问题。

1.2 工具考察

考察市面上的H5生成工具。

HTMLPAGE、Tilda、mobirise、Strikingly

兼顾设计师、开发者的在线网页设计、制作工具,侧重在H5页面的自主设计和生成,基于网页模板、页面元素模块的灵活组合,快速制作HTML5页面,支持响应式,支持导入导出HTML。如下图:

https://i.imgur.com/Ey7ameN.jpeg

CMS(内容管理系统,一种用于创建、发布和管理网站内容的软件)

通过对主流的几个传统CMS(WordPress、Joomla、Drupal、PageAdmin CMS)进行调研,可以知晓这些CMS系统主要的功能点有:站点管理、文章管理、页面管理、菜单管理、用户管理、评论管理等,更适用于个人博客、企业门户网站等有站点搭建和内容管理需求的场景。

1.3 考察结果

总的来说,存在以下痛点:

  • 像HTMLPAGE这类H5页面生成工具过于注重页面自主设计,允许用户在预设组件中组装页面。而我们的线上说明书已有固定的模板和样式,不需要这种灵活性。这种过度的灵活性反而增加了页面制作的操作成本,使系统变得不够简单易用。

  • 传统CMS系统通常包含站点管理、文章管理、页面管理、菜单管理、用户管理、评论管理等功能模块,主要适用于个人博客、门户网站、社区网站和电商网站等有站点搭建以及内容管理需求的场景。而我们的线上说明书页面可能需要部署到不同域名上(不同业务线),且不涉及站点搭建和站点管理方面的需求,因此与我们的线上说明书制作的实际需求并不匹配。

  • 没有一个简洁易用的支持导入的多语言文案管理工具,也就是说不能支持一次制作,生成多个语言的页面。

1.4 解决方案

针对以上痛点,以及在线说明书需求的特点,我们考虑开发一个后台系统。

该后台系统可以:

  • 通过导入多语言excel文档,自动录入说明书所有语种的文案,并自动关联文案之间的多语言对应关系

  • 上传和管理说明书所需的图片

  • 支持多页面制作,支持页面管理

  • 根据实际页面排版需求,对文案和图片进行排版

  • 一键生成说明书页面html文档

  • 一键部署

这样的平台旨在为企业提供一套高效、便捷的方式来制作、管理各类产品的线上说明书文档,不仅能够减轻程序员的负担,还能提升整体的开发效率和内容管理的灵活性。其优势如下:

  • 快速开发,缩短开发周期

  • 降低门槛,非专业开发者也能快速上手,提高需求转化效率

  • 灵活性,提供的定制能力使得实际需求可以进行个性化定制

二、研发成果

在确保优先完成业务需求和外部项目开发的前提下,我们历时约一年完成了线上说明书编辑平台的规划、设计和开发和上线工作。该平台于2023年初正式上线,经过近两年的持续迭代和优化,系统现已趋于稳定,并在投入使用以来取得了显著成果。

2.1 效率提升

相比以往需要编写代码的繁琐操作,使用这个平台只需要简单地资源导入、排版、拖拽就能完成说明书的制作。平台提供了丰富的组件库,提供了文案管理工具,让多语言页面的制作变得轻松高效。

无论是专业人士还是普通用户,都可以通过这个平台快速生成模板化的说明书。它不仅大大节省了人力和时间成本,还能确保说明书的内容和格式更加规范,为用户提供更好的使用体验。

说明书编辑平台投入使用至今,已支持了151个说明书制作,假设平均每个页面需要支持5个语种,则大约可节省300+人日,随着时间的拉长,节省的时间会更多。

说明书数151、平均语种数5使用平台制作编码实现方式
估算规则制作+上线,每个说明书大约需要1人日首个单语种说明书平均需要1人日,每增加一个语种需要0.25d,跟进测试发布上线等共投入1人日
需要工时1×151 = 151人日151×(1+0.25×4+1) = 453人日

2.2 制作难度降低

非技术人员可以使用平台进行说明书制作,无需像以前一样必须研发介入。这个工具能够大大简化说明书的制作流程,让任何人都能轻松生成所需的内容。

2.3 链条缩短

  • 产品经理无需再和研发对接,减少对接环节提高整体工作效率。

  • 产品经理无需再画PRD,可以直接在系统上设计和生成最终的说明书页面。

  • 平台直接部署上线,无需进行代码资产管理,也无需研发和运维对接走部署流程,提高整体工作效率。

三、整体设计和系统搭建

通过对过往线上说明书需求的深入且集中的分析,我们系统地梳理了页面模式功能交互样式特点,同时,主动与需求方进行了多轮沟通,充分理解并整合了各方的期望与要求。

在此基础上,我们初步明确了系统的核心功能需求界面交互设计用户使用路径,梳理出一个力求全面覆盖业务需求的系统功能池,确保系统功能既满足当前业务需求,又具备持续进化的能力。并对各功能的实现路径与迭代策略进行了短期及长期的规划,为系统的落地提供一个坚实可靠的基础。

3.1 整体设计

在从0到1的起始阶段,为清晰界定系统功能范围,使后续的开发、测试及上线工作能顺利稳步推进,我们输出了一份详尽、结构化的产品需求文档(PRD)。

首先明确用户制作说明书的完整流程,如下图:

IfSBEiR.webp

一言以蔽之,即:导入资源(文案、图片),使用资源。

总结制作流程,有五个关键步骤:

新建说明书→导入文案→导入图片→新建页面→页面排版

因此,对应于五个关键步骤,我们把整个系统划分为五个大模块:

  • 系统管理:说明书管理(包括说明书的增删改查)

  • 文案管理:导入和编辑多语言文案

  • 图片管理:上传和管理说明书图片

  • 页面管理:创建和组织页面结构

  • 页面设计:设计和预览页面内容

3.2 平台搭建

3.2.1 技术栈选择

对于基础框架的选用,我们首先考虑的是尽可能接入大的生态圈子,因为长期来看,接入更大生态的项目会走得更远。

同时结合团队技术栈,最终选择如下:

  • 前端框架:vue全家桶(vue3、vue-router、vuex、axios)

  • UI框架:element-plus、tailwindcss

  • 语言构建:typescript、sass、vite

其他工具插件选择如下:

  • 富文本编辑:wangeditor,用于文案编辑

  • excel解析:node-xlsx,用于文案导入

  • 右键菜单:vue3-menus,用于页面组件操作

3.2.2 数据管理

考虑到文案数据、图片数据和页面结构数据不仅在文案管理、图片管理和页面管理模块中使用,在页面设计模块也需要使用,因此我们决定将这三部分数据放在Vuex中进行全局管理。

这样做有几个好处:

  • 首先,可以确保各个模块之间的数据保持一致,避免出现数据不同步的情况。

  • 其次,将数据集中管理有利于提高系统的可维护性和可扩展性,方便后期的功能迭代和优化。

  • 最后,Vuex作为一个成熟的状态管理工具,提供了丰富的API 和周边生态,有助于我们更好地管理应用程序的状态。

3.2.3 技术架构

https://web-api.textin.com/ocr_image/external/1d2710f1872bc6c3.jpg

四、详细设计与开发实践

下面将按以下模块顺序讲述系统的详细设计和开发实践:

https://web-api.textin.com/ocr_image/external/f9d0700624612e67.jpg

4.1 系统管理

PRD设计如图:

C0g5QY5.webp

👉 核心功能

  • 作为一个后台系统,需要支持基础的用户登入登出,这个接入IT 统一认证中心即可。

  • 作为一个说明书后台,需要支持说明书的以下基本管理功能。

    1. 说明书的增删改

    2. 说明书的切换

    a. 一个用户可以创建多个说明书,可以切换其中一个说明书为当前说明书进行编辑。

    b. 为保护说明书不受他人篡改,用户只可以在自己创建的说明书范围内进行切换。

    3. 说明书的导出

    a. 说明书制作完成后,可导出html文件,将导出的html文件走部署流程部署到服务器上方可进行访问。

    b. html文件是根据说明书的设计数据生成,这里留待后面”页面设计“模块细讲。

以上作为说明书管理的核心功能,应当优先实现。

👉 迭代和优化

4.1.1 说明书操作

  1. 说明书复制

为简化说明书的制作工作,当一本说明书的页面和之前做好的说明书(可以是自己做的,也可以是其他人做的)很相似,只是文案和图片不同时,可以一键复制之前的页面,然后在新生成的说明书里,进行文案替换和图片替换即可,不必再费心进行页面管理和页面排版。

这里的复制会遇到一个跨说明书的资源使用问题,留待后面讲到设计数据格式时细讲。

  1. 说明书移交

因为每个用户只能看到自己名下的说明书,当有人员离职或职能变动时,就需要把他名下的说明书移交给其他人进行后续的维护。

  1. 说明书汇总

随着说明书数量不断增加,如何有效管理多本说明书成为一个亟需解决的问题。

将所有说明书汇总到一个列表页面,可以大大提高说明书管理的效率。在这个页面,管理员可以轻松地查看、复制、转移和编辑所有的说明书。

此外,还赋予了管理员对所有说明书的完全控制权限。无论是处理人员变动导致的说明书移交,还是针对说明书内容的修改需求,管理员都可以在这个汇总页面上完成相关操作,有助于提高工作效率。

https://cdn.gamma.app/uxqgoqz787tn07l/f71c02123e5c422b960376dcbc37cdaf/original/image.png

  1. 支持新增版本

在说明书编辑平台增加对法条页面制作的支持时,我们需要为每次法条变更维护版本信息,并提供查看旧版本的功能。也就是说,每个版本的法条都是一个独立的页面,新增一个版本,相当于复制一个法条页面,并将新复制所得的页面打上新的版本信息。

  1. 说明书冻结和解冻

旧版本的法条页面需要确保其内容不变,因此需要冻结起来,冻结的页面不能编辑和删除,但可以查看效果和复制。

4.1.2 说明书部署

在系统的初始版本中,说明书制作完成后的部署流程大致如下:

导出html文件 ➜ 文件发给研发 ➜ 研发将文件放到指定代码仓库 ➜ 提部署申请单 ➜ 审批 ➜ 部署上线

这个流程需要耗费大量的沟通成本和等待时间,因此考虑对这个流程进行优化。

  1. 支持一键部署,简化部署流程。

省去一系列中间步骤,在系统上一键操作,程序自动生成html文件并将其上传到s3桶,即可支持线上网络访问。

  1. 增加”一键部署所有“操作。

一本说明书包含多种语言,一种语言生成一个html文件。当一本说明书包含20+语言时,若是一个一个html去点部署,则需要点击20+次,操作太过繁琐。

增加此操作,点一下按钮,自动生成所有语言的多个html文件并将这些文件分别上传到s3桶以支持线上网络访问,从而达到所有多语言页面上线的目的。当一本说明书的语言数较多时,此操作可以快速便捷的部署说明书。

  1. 增加页面链接汇总。

程序将html文件上传到指定的S3桶后,根据S3桶的配置,可以得到页面所对应的链接,前面也提到一种语言会生成一个html文件,因此一种语言会对应于一个链接。

这些链接可以提供给任何需要访问说明书页面的地方,比如APP内,比如产品详情页等。

将不同语言对应的链接汇总和展示,并提供复制按钮,方便用户将制作所得的页面应用到需要的地方。

https://cdn.gamma.app/uxqgoqz787tn07l/8707e5e2622d459cb9f300e9f2ffeae1/original/image.png

  1. 增加部署历史展示。

由于部署操作的结果会直接影响用户,因此这个操作理应谨慎,为便于追踪部署操作记录,协助问题的定位和解决,我们将详细记录部署操作的每位操作人员及其操作时间。

https://cdn.gamma.app/uxqgoqz787tn07l/1158cf368d564faa82761489fb366ff5/original/image.png

  1. 支持部署不同环境和不同域名。

    a. 支持部署测试环境或线上环境。

    若一个已经上线的页面在运行一段时间后,需要更换图片或文案,但更换后的页面效果还需要在内部审核或测试通过后,再上线。此时则可以先部署到测试环境,将测试链接提供给审核人员或测试同事,验证没问题后再部署到线上环境。

    b. 支持部署不同域名。

    不同的业务线可能拥有不同的域名,比如vesync业务的页面需要部署到xxx.vesync.com域名下,而pawsync业务的页面需要部署到xxx.pawsync.com域名下。    

基于以上两个场景,部署时,需要选择部署域名和部署环境,另外,也可以选择要部署的页面(所有、EN、ES,…)(支持多选),交互设计如图:

https://cdn.gamma.app/uxqgoqz787tn07l/bf16d18e33184eb9943e5f3f07ec2235/original/Pasted-image-20241009161730.png

不同的选择组合则会产生不同的部署场景,考虑为每种部署场景分配一个S3桶,每个桶配置一个域名,根据选择的不同域名、不同环境,将涉及到的所有文件(图片和html)批量从中转桶复制到目标桶,从而测试环境和正式环境的文件得以隔离,互不影响,且支持不同的域名。

【实现要点】

  1. 桶下面一个说明书创建一个目录,以page[pageID]命名,pageID是系统中说明书的ID,具有唯一性。

  2. 调用文件服务接口将生成的html文件上传到s3桶(原始桶,也可叫中转桶)后,得到一个文件访问链接,将该说明书所有文件的链接(一个语言一个链接)和所有图片链接传给文件服务接口,由文件服务接口将所有文件从原始桶复制到目标桶。
    之所以使用一个原始桶作为中转,而不是直接上传到目标桶

  • 一则文件上传接口直接上传到不同的桶会涉及到复杂的账号鉴权;
  • 二则便于确保部署到不同环境的文件保持一致;
  • 三则旧数据兼容,确保以前的旧说明书暴露出去的url(原始桶的url)继续可用且能同步到最新更新。
  1. 将步骤2中接口返回的所有页面url(目标桶的url,特定环境特定域名)保存到系统数据库和说明书绑定。

  2. 最后由云接口调用第三方接口进行cdn刷新,以确保浏览器能访问到最新修改。

注意:除了目标桶对应的cdn需要刷新,原始桶所对应的cdn也要进行刷新,这样步骤2中文件服务接口通过文件链接拉取到的文件内容才是最新。

所以前端在将生成的html文件上传到原始桶后,需要调用特定接口刷新原始桶所对应的cdn,然后再调用文件服务接口将文件从原始桶复制到目标桶。

4.1.3 支持静态H5页面的制作

多语言静态H5页面具有和说明书相似的痛点,因此可以考虑扩大需求覆盖面,使系统不再局限于说明书页面需求。

  • 解耦说明书页面和普通H5页面类型,在页面全局属性配置时,区分页面是说明书类型还是普通H5类型,对不同页面赋予不同默认属性,比如说明书页面可以设置封面图和troubleshooting链接,普通H5则没有这些设置。

  • 生成html文件时,对说明书和H5做更细化的区分,比如H5页面类型只有一个一级页面,说明书页面有目录页和多个一级页面;比如单页面的H5没有左滑右滑功能,多个二级页面的H5有左滑右滑功能,说明书页面一定有左滑右滑的功能,根据不同页面特性,去掉不必要的代码,生成更简洁的html代码。

4.1.4 支持中转页生成

通过语言码访问指定语言的页面,如果所用语言码不在说明书支持范围内,将产生404错误。为避免这种情况,我们采用中转页进行跳转。

中转页具有如下特性:

  • 根据url中的指定语言码自动跳转对应语言页面;

  • 若没有传语言码,则根据浏览器默认支持语言跳转;
    通过 navigator.language || navigator.browserLanguage 获取浏览器默认支持语言。

  • 若指定了不存在的语言码,或浏览器默认支持语言不在页面支持语言范围内,则跳默认页面(一般是英文页面)。

4.1.5 支持代码插入

支持代码插入,能为系统带来更大的灵活性和扩展潜力。

  1. 自定义插入

支持在说明书页面插入自定义代码,提供更多的灵活性。如下图:

https://cdn.gamma.app/uxqgoqz787tn07l/edf3308ecbae44bda2d82979f0546537/original/Pasted-image-20241021194057.png

  1. 支持页面神策埋点

将神策埋点代码作为自定义代码插入和保存,这里需注意上报服务器需要区分测试环境上报还是正式环境上报。

  1. 公共SDK支持从CDN引入

规范sdk来源,避免第三方sdk失效导致加载失败。

  1. 支持调用app接口更新页面数据

基于自定义代码功能,插入封装好的app api调用代码,使得页面在app内运行时,也能具有一定的动态功能。

4.1.6 HTML页面功能优化

  1. 支持troubleshooting可选,链接自定义

大多说明书都有一个跳app原生troubleshooting页的跳转链接,但也有少数说明书不需要这个跳转链接,甚至部分这个跳转链接不是跳app原生troubleshooting页,而是跳其它指定路径。

因此需要在说明书维度上配置是否显示troubleshooting跳转,以及配置其跳转链接。

https://cdn.gamma.app/uxqgoqz787tn07l/128adb9f58b44217b70fa805739b7fc5/original/image.png

  1. 支持通过url自动跳到指定子页面
  • url通过哈希指定要跳转的子页面:files.vesync.com/platform/ma…
    4-2指的是第4个一级页面下的第2个二级页面。*

  • 解析url得到传递的页码参数4和2,进行跳转。代码如下:


function goPage (pageNo, subPageNo) {

  // ...

  currentSwiper = initSwiper(`.m${ pageNo }`, subPageNo) // swiper初始化,将第4个1级页面下的第2个2级页面作为默认显示页面

  $('.menu').hide() // 隐藏目录页

  $('.mainWrap').show() // 显示主容器

  $(`.m${ pageNo } `).show() // 显示第4个一级页面

}

  1. 左上角的返回按钮支持返回目录页、顶部的页面标题支持显示每个一级页面的标题

由于说明书浏览页面中顶部的导航栏是app原生部分,因此导航栏中的返回响应事件和标题内容更换都需要app来支持。于是协调app提供了接口 goPageWithFullPath,接口接收页面路径和页面标题参数,通过调用app接口实现此效果。

  1. 支持页面内部跳转

为需要跳转的目标位置设置一个锚点,然后利用a标签的锚点跳转功能,将a标签的href设置为#锚点名target属性设置为_self来实现。具体实现:

a. 为所有组件默认生成一个组件id,在生成html文档时,每个组件dom都设置id属性,属性值为组件id。组件id生成逻辑:


function genCompId () {

  return Date.now() + '' + (random(0, 10000).toString()).padStart(4, '0')

}

b. 鼠标移到组件位置,右键 ➜ 复制锚点名称,如图:

https://cdn.gamma.app/uxqgoqz787tn07l/bd69f14cb46a44ddaa7c728da56a89e2/original/Pasted-image-20241007174835.png

c.   设置超链接,将跳转目标设置为#刚刚复制所得的锚点名称

d.   将锚点跳转类型的超链接修改为在页面内部跳转。

4.2 文案管理

prd设计如图:

https://cdn.gamma.app/uxqgoqz787tn07l/f97f1e291d7d4b77a5ece40a2e8124cd/original/Jie-Ping2024-10-20-18.24.42.png

👉 核心功能

  • 文案导入,支持解析和导入说明书多语言文案excel文档,并保持文案之间的多语言对应关系。     大多数情况下,文案的数量都不会少,并且以excel文档的方式来提供,因此最快的录入文案资源的方式就是导入。

  • 文案编辑,包括加粗、斜体、设置颜色、设置下划线、插入小图标、插入超链接等。     由于文案的展示形式并不总是纯文本,可能包含样式(如加粗、颜色等),甚至嵌入小图标。而这些样式和图标信息不能通过excel导入来获得,只能通过人工编辑的方式来实现。

  • 文案重置,恢复文案到导入时的初始状态。     文案编辑失误后,应该要允许撤销编辑结果,恢复文案到最初始的状态。

以上作为文案管理的核心功能优先实现。

关于文案导入,使用node-xlsx工具处理 Excel 文件,解析后,将文案数据整理为一个列表,其中每个元素都是一个对象,代表一行文案。对象的键为语言,值为该语言对应的文案。最终,将解析得到的数据通过接口提交到云端进行存储。

其数据格式形如:

[	{		"en": "User Manual",		"jp": "xxx",		"es": "xxx",		...	},	...]

👉 迭代和优化

4.2.1 文案导入优化

  1. 自动生成页面结构数据和绑定文案

文案和页面的关系,在文案准备阶段就能知晓和确定,因此将页面结构信息以及文案和页面的从属关系编写在Excel里,在文案导入时就能根据获得的信息自动生成页面结构,并将文案自动绑定到对应页面。

  • 文案excel模版设计成如下图的格式:

https://cdn.gamma.app/uxqgoqz787tn07l/9a94bf2a4dc140d683a5390aa234e717/original/Pasted-image-20241006021131.png

a. /page1-title/、/page2-title/等行是固定格式,表明下一行文案为第一页、第二页等页面的标题,根据页面数相应增加或删除/page[n]-title/。

b. /page1-1/、/page1-2/等行表明往下的文案为各个子页面的文案,比如page1-1表明往下的文案为第一个大页面中的第一个子页面的文案,page1-2表明往下的文案为第一个大页面中的第二个子页面的文案。根据页面数相应增加或删除/*page[n]-[n] */;可以通过page[n]-[n]-xxx这种格式为每个子页面设置页面名称,其中xxx为页面名称。

c. 如果需要增加不绑定在任何页面下的文案,可以新增/page-end/行,在该行下添加文案。

  • 解析后的数据格式

将解析所得的数据构造成一个列表,其中每个元素都是一个一级页面的数据,其结构如下:

[{
	title: { // 一级页面的页面标题
		en: 'xxx',
		jp: 'xxx',
		...
	},
	subpage: [ // 二级页面数据列表
		{ // 第一个二级页面
			title: '1-1', // 第一个二级页面标题
			texts: [ // 第一个二级页面的文案列表
				{
					en: 'xxx',
					jp: 'xxx',
					...
				},
				...
			]
		},
    ... // 更多二级页面数据
	]
},
... // 其他一级页面数据
]
  1. 支持加密文档导入

公司环境下,excel文档编辑过后都会变成加密状态,而JS代码无法解析加密状态的excel。若是每次编辑后都要找IT同事或领导解密文档,无疑增加了工作的繁琐程度和无意义的等待。

经过和IT同事的沟通,可以针对特定网站的文件上传配置特定规则,也即是说,针对本系统网站,加密系统检测到excel文档上传时会先对文档解密。 如此,就可以直接选择加密excel文档进行导入了。

  1. Excel解析优化
  • 换行符

excel文档中的文案换行,往往用的是\n符,而在html代码中换行用的是<br>,因此,为了在html页面中正确的渲染分行的文案,需要在文案导入的时候,手动把所有的\n替换为<br>

  • 自动去除文案中的序号

由于列表的序号带有特定的样式,这些特定的样式都是由列表组件的样式设置,因此如果文案中包含了序号,再对列表组件设置了序号样式,会出现重复序号的问题。因此考虑在文案导入时,自动去除文案中的序号,用于匹配的正则规则为^[\\da-zA-Z]\\.[\\s]?^[\\•\\⦁\\ꔷ][\\s]?

4.2.2 文案添加优化

如果文案总是要通过Excel文档导入,对于一些简单的只有几行文案的H5页面,或者文案导入后需要增加一两个漏掉的文案的场景来说,还是略显繁琐。因此针对“添加少量文案”,提供功能支持,不需要excel文档导入。

  1. 添加少量文案
  • 刚新建的说明书,没有导入过文案,支持语种尚未明确,那么添加文案的时候则需要知道文案是什么语种的,因而需要手动选择一个语种,然后再手动输入文案,支持一次性添加多行文案。交互如下图:

https://cdn.gamma.app/uxqgoqz787tn07l/fdb7cf28b0204a91b87e5cfb65e02661/original/image.png

  • 若是说明书已经导入过文案,且确定只支持一种语言,那么添加文案时则不需要手动选择语种,只需要手动输入文案时,同样也支持一次性添加多行文案。交互设计如下图:

https://cdn.gamma.app/uxqgoqz787tn07l/7687ec3cd4674018bf67e825dcde32b5/original/Pasted-image-20241021200506.png

  • 若是说明书已经导入过文案,且确定支持多种语言,那么添加文案时不需要手动选择语种,但需要为每个语种手动输入相应的文案。由于输入框的数量随语种数增加而增加,因而这里不考虑一次性添加多行文案,交互设计如下图:

https://cdn.gamma.app/uxqgoqz787tn07l/124ebe32d95f4fc6bca688ede5fd7a05/original/Pasted-image-20241021200241.png

  1. 添加其他语言

在说明书已上线的情况下,随着单品销售区域的增加,需要增加相应多语言的支持。

针对此场景,需要确保新添加的多语言文案与原有文案保持对应关系,同时不影响原有文案的使用。因此,原有文案的 ID 必须保持不变,并且新语种的文案应与原有文案共用同一套文案 ID。

  • 交互入口:文案管理页面 **→ **“添加其它语言”按钮
  • 交互形式:弹出框
  • 交互图:  

    https://cdn.gamma.app/uxqgoqz787tn07l/686160e83ac3416bb054502475c6d1f5/original/image.png

操作分为3个步骤:

a. 导出excel文档,文档中包含两列,第一列为所有文案id,第二列为与id对应的默认语言(假设为英文)文案。这两列设置为只读状态。

b. 将新增的多语言文案以每种语言占一列的方式贴到第3列、第4列...,每一列的第一行是语言码,每一行多语言文案都是英文(假设)文案翻译到对应语种的文案。

c. 导入excel文档。

4.2.3 文案操作优化

为了提升用户体验和工作效率,我们增加了更多更丰富的文案操作功能。

  1. 文案删除

支持单行文案删除、多行文案批量删除、删除特定语言的所有文案。

  1. 文案搜索

支持对任意语种的任一文案进行模糊匹配搜索。

  1. 文案排序

支持按特定语种的升序/降序进行字典序排序。

说明书的文案涉及到多语言的管理,例如一个说明书有6种语言,则一段文案应该有6个不同语种的字符串,但实际上不是每个语种都一定会有不为空的字符串,也即是说,一段文案,可能对应某些语种的字符串为空串。

在按特定语种对所有文案进行字典序排序时,按照传统的分页方法(根据文案主键id来进行统计和排序),会打乱原来的排序方式,且因为存在空白的语言串,在实际实践中,会导致出现重复的记录和排序混乱以及记录总数统计错误的问题。

解决方法是:如果要对某种语言进行排序,则先查询该语言存在的文案行,再进行对应的分页,其余空白行按照原本的文案顺序排列; 

  1. 支持在设计页面直接编辑文案

在设计页面时,如果发现文案需要修改,用户可以直接在当前页双击文案进行编辑,无需切换到文案管理页面。这种方式不仅提高了操作的便利性,避免了频繁切换,还确保了操作的连续性,使页面排版工作更为流畅。

  1. 更多的编辑功能

支持添加文字上下标、设置中横线、字体大小、字体背景色等

  1. 支持源代码编辑

为支持更灵活的文案编辑(比如为特定单词设置锚点以支持调用app接口更新页面数据、设置超链接跳转类型等),推出源代码编辑文案的方式。入口交互如下图:

https://cdn.gamma.app/uxqgoqz787tn07l/5766a1b102114452ac8d316f5834a4d3/original/image.png

点击后出现源代码编辑弹出框,在弹出框中的输入框内进行源代码编写和保存。

【实现要点】关键是要为富文本工具wangeditor扩展一个自定义菜单EditSourceMenu,主要使用wangeditor的getHtml和setHtml API进行编辑框内容的获取和填充。主要代码如下:


import EditCodeTextDrawer from '@/components/EditCodeTextDrawer.vue'

// EditCodeTextDrawer就是“文案编辑-源代码模式”弹出框

class EditSourceMenu {

  ...

  exec(editor, value) {

    if (!this.$root) { // 初始化弹框

      this.$ele = document.createElement('div')

      document.body.appendChild(this.$ele)

      this.$ele.id = 'EditCodeTextDrawer'

      this.$root = createApp(EditCodeTextDrawer, { // this.$root就是弹框的vue实例

        onOk (val) { // 组件ok事件的响应,用setHtml()回填编辑结果

          editor.setHtml(val)

        }

      }).mount('#EditCodeTextDrawer')

    }

    this.$root.open(editor.getHtml()) // 调用组件的open方法打开弹框并传入getHtml()的结果

  }

}

4.3 图片管理

prd设计如下图:

https://cdn.gamma.app/uxqgoqz787tn07l/b00ef367b2af45f4b8e92fc5e1d28a11/original/Jie-Ping2024-10-20-20.03.36.png

👉 核心功能

  • 批量图片导入,轻松搞定。

  • 可删除不需要的图片。

  • 支持图片大图预览。

以上作为图片管理的核心功能优先实现。

👉 迭代和优化

  • 图片管理优化:增加标题显示,重复图片处理(md5检测到重复图片则不上传)

  • 支持上传svg图片

4.4 页面管理

prd设计如下图:

https://cdn.gamma.app/uxqgoqz787tn07l/46fde19b9270413699aa3539fdeedcee/original/Jie-Ping2024-10-20-20.58.57.png

👉 核心功能

页面管理主要涉及到一级页面和二级页面的增删改,以及页面资源的分配。其核心功能点如下:

  • 一级页面的增删改,标题设置。

  • 二级页面(左滑右滑的一屏页面)的增删改。

  • 为每个二级页面绑定图片资源和文案资源,为下一步的页面设计缩小资源的选择范围。

以上作为页面管理的核心功能优先实现。

👉 迭代和优化

4.4.1 页面管理优化

  • 二级页面增加名称属性,可自定义名称,方便识别和管理;

  • 显示每个二级页面所绑定的文案数和图片数,便于资源管理;

  • 页面增加序号展示,在页面数较多时,序号能帮助更快速找到指定页面;

4.4.2 页面顺序调整

支持拖拽调整一级页面/二级页面的顺序。

4.5 页面设计

prd设计如下图:

https://cdn.gamma.app/uxqgoqz787tn07l/6b0f222878a743ce8ee4004fd2c752db/original/Pasted-image-20240928173709.png

👉 核心功能

和大多数H5页面制作工具类似,我们提供搭积木+所见即所得式的页面设计方式。

首先需要实现说明书页面需要的组件类型,包括标题、文本、图片、列表等,可设置组件样式。

页面制作完成后,支持进行页面预览。

  • 组件类型

    - 标题组件

    - 文本组件

    - 图片组件

    - 列表组件(有序列表、无序列表)

  • 样式设置

    - 列表样式

    - 字体大小和颜色

    - 对齐方式

    - 间距调整

页面设计数据设计为一个包含所有组件信息的数组,涵盖组件类型、使用的文案/图片资源、样式等细节。

  • 每个组件的数据以对象形式存储,组件对象通过 type 属性来标识其类型:1标题组件,2段落组件,3图片组件,4无序列表组件,5有序列表组件

  • 使用的文案信息或图片信息以列表形式(因为列表组件有多个文案)保存在data属性下,只需保存文案id或图片id即可,实际渲染时会根据id查找对应语种的实际文案或图片链接进行渲染。

  • 对象中还保存组件的样式信息,包含组件所使用的样式类集合,自定义样式类,以及自定义css样式等,分别保存在不同属性下。

所有数据最后转为一个JSON串在云端进行存储,每个二级页面绑定一个JSON串数据,前端页面负责构造和解析。 数据格式如下:


[{

    "type": 1,  // 1标题组件 2段落组件 3图片组件,4无序列表组件,5有序列表组件

    "data": [

      {

        "id": "TXT4577TXT"  // 文案id

      }

      // 对于列表组件来说,有多个文案

    ],

    "classes": [ // 样式列表

      "title",

      "ml-40"

    ]

},

...]

👉 迭代和优化

4.5.1 组件操作优化

  • 支持组件拖拽

  • 样式名使用更语义化的中文名

  • 支持自定义样式,即以写css代码的方式设置样式

4.5.2 图片组件优化

当图片中包含文字时,不同语言的页面则需要提供不同的图片。也即是说,一个图片组件,需要绑定多张图片,每个图片都有一个语言码属性。

组件交互设计如图:

https://cdn.gamma.app/uxqgoqz787tn07l/a06621bd73824d9c8d26a6631e8b44d2/original/Pasted-image-20241007153247.png

  • 当只有一种语言时,则不展示“高级”设置块。

  • 若需要为不同语言设置不同的图片,则点击“高级”展开设置块,点击需要设置的语言码展开图片列表,勾选要展示的图片即可。

图片组件的数据结构如下:


{

    "type": 3, // 图片组件

    "data": [

      {

        "id": "38" // 图片id,没有语言码则为组件默认显示图片,比如en、fr都显示38这个图片

      },

      {

        "id": "39",

        "lang": "de" // 语言码

      },

      ...

    ],

    "classes": [

      "bannerImg"

    ]

}

4.5.3 组件类型丰富

随着说明书编辑平台对静态H5页面制作的支持,我们迫切需要增加更多组件类型:6自定义组件,7折叠组件,8表格组件,9一行N列组件,并且支持组件嵌套。

  1. 自定义组件

当系统提供的组件类型不足以满足页面交互需求时,可以将自定义的代码段以组件的形式插入到页面任意指定的位置。这种组件类型为具备编程能力的制作者提供了更大的灵活性。

  1. 折叠组件

一个内容块默认只展示它的标题,详细内容收缩起来不展示,点击标题块会展开或收起详细内容块。

  • 组件的交互设计如下图:

https://cdn.gamma.app/uxqgoqz787tn07l/b0ef367499034609aa45828f5c8b8ad3/original/image.png

主要包含两个部分:段落标题块和详细内容块。标题块允许用户选择一个文案,而内容块则支持不断插入可嵌套的组件,包括文本、图片、列表、自定义组件和表格等。

  • 组件的数据格式:

{

    "type": 7,

    "data": [

      // 数组第一个元素存储标题文案id

      {

        "id": "TXT4600TXT"

      },

      // 从第二个元素开始为折叠框内的内容数据,每个元素为一个组件对象,结构见前面定义

      {

        "type": 4, // 无序列表组件

        "data": [

          {

            "id": "TXT4577TXT"

          },

          {

            "id": "TXT4578TXT"

          }

        ],

        ...

      },

      ...

    ]

}

data段第一个数组元素固定为折叠组件的标题信息,从第二个数组元素开始为可嵌套的组件对象数据(文本、图片、列表、自定义组件和表格等)  

  1. 表格组件

插入表格组件时,用户需手动输入行数和列数。生成的表格中,用户可以为每个单元格插入内容,包括文本、图片、列表、自定义组件以及表格等可嵌套组件。

组件的数据格式:


{

"type": 8,

"data": [ // 二维数组

[ // 表格的一行

[ // 表格的一列,即一个单元格。该行该列单元格内的数据也是一个列表,可以存储多个组件数据,组件数据格式同前面定义

  {

"type": 2, // 文本组件

"data": [

  {

"id": "TXT21347TXT"

  }

],

...

  },

      ...

],

[// 第一行第二列单元格],

...

],

[// 第二行数据],

...

],

...

}

  


data段设计为二维数组,第一维数组元素为表格的行数据,第二维数组元素为表格的列数据,即单元格数据,单元格数据本身为数组,列表存储可嵌套的组件对象数据(文本、图片、无序/有序列表等)

  1. 一行N列组件

在 H5 页面中,一行 N 列的交互设计允许用户以灵活的方式展示内容。每一行可以包含多个列,这种布局不仅优化了视觉效果,还提升了用户体验,使信息的展示更加直观和紧凑。

插入一行N列组件,用户需手动输入列数。生成的组件中,用户可以为每列插入内容,包括文本、图片、列表、自定义组件以及表格等可嵌套组件。

组件的数据结构,其实相当于一个只有一行的表格数据,data段每个数组元素为每一列所插入的组件数据列表,列表存储可嵌套的组件对象数据(文本、图片、无序/有序列表等)  

  1. 嵌套组件的渲染

前面提到的折叠组件、表格组件以及一行 N 列组件,实际上都使用了嵌套组件。可以看出,嵌套组件的组件类型和组件数都具有不确定性,因此需要遍历渲染。

考虑使用vue3的动态组件<component>进行渲染,以表格组件为例,单元格(td)内的内容是不确定类型和数量的组件集合:


<td v-for="(td, tdInd) in row">

    <comp-render v-for="(comp, cind) in td" :comp="comp" />

    ...

</td>

  


<comp-render>就是封装了动态组件<component>的渲染组件:


<template>

  <component

    :prepare="getComp(components[comp.type]?.cname, comp)"

    :is="comp.compVue"

    ...

  ></component>

</template>

<script setup>

...

const props = defineProps({

  comp: { // comp为外部传进来的组件数据

    type: Object,

    default: () => ({})

  }

})

// 获取真正要渲染的组件

function getComp (name, obj) {

  if (!name) return

  if (!loadedModules[name]) {

    loadedModules[name] = markRaw(defineAsyncComponent(modules()[`../../views/design/components/${name}.vue`]))

  }

  obj.compVue = loadedModules[name]

}

</script>

  • 解释:使用prepare指令根据组件type加载对应的Vue组件(Text、Picture、List等等),并将加载结果绑定到compVue属性上,is指令赋值为compVue属性,则可以指定渲染的组件类型。

  • 注意:为避免重复加载Vue组件,将加载好的Vue组件保存在一个全局变量loadedModules中,其结构为 * {组件名称:Vue组件} *,形如:

https://cdn.gamma.app/uxqgoqz787tn07l/e60f8864f76c45288a6be32542fa151a/original/Pasted-image-20241021205755.png

4.5.4 说明书复制问题

说明书复制操作所得的新说明书中,设计数据串(即JSON串)中的id用的是旧说明书导入的文案id和图片id。

而不同说明书的文案和图片是互相隔离的,不能共用,不然A说明书的文案修改就会影响到B说明书。

这就导致了新说明书无法渲染出正确的文案和图片。

为解决这个问题,需要把json串中的文案id和图片id替换为复制出来的属于新说明书的文案id和图片id。

考虑到存储的json串就是一个大字符串,我们选用字符串匹配和替换的方式来替换id。

为快速在文案池和图片池中查找到旧id所对应的新id,以及避免文案和图片出现相同id的情况,需要准确判断匹配到的id是文案还是图片。因此决定把json串中的文案id和图片id分别加上特定的前缀和后缀,文案id的前缀和后缀皆为TXT,图片id的前缀和后缀皆为IMG。代码匹配到TXT{{id}}TXT内容,可用识别到的文案id在文案池中查找对应的新id,然后进行替换,图片同理。

4.5.5 HTML文件生成

前面说到生成html文件时,为使生成的html代码不包含不需要的代码,达到极致简洁,对说明书和H5做了解耦,更细化的区分两种类型的页面来做代码生成。

1、首先判断是否需要加载swiper sdk


/*

 * 是否需要支持左滑右滑

 * 说明书默认支持,h5的第一个父页面下的子页面数多于1个则支持

 */

function needSwiper () {

  if (isManual()) return true // 说明书

  else { // h5页面只看第一个父页面

    let pages = store.getters['pages/data']

    if (pages.length && pages[0]?.subPageList?.length >= 2) return true

    else return false

  }

}

2、页面主体代码

  • 判断全局配置项中是否设置了页面最大宽度

  • 如果是说明书,则生成目录页,默认展示目录页,其他内容隐藏

  • 如果是其他类型H5页,则没有目录页,页面主体div块(即id为mainWrap的div块)显示


<body style="${curManual.globalConfig.pageMaxWidth ? 'max-width:' + curManual.globalConfig.pageMaxWidth + 'px;margin:0 auto;' : ''}"><!-- 页面最大宽度设置 -->

    ${isManualPage ? getMenuPage(lang) : ''} <!-- 说明书页面则生成目录页 -->

    <div id="mainWrap" style="${isManualPage ? 'display:none;' : ''}"><!-- 说明书页面则默认展示目录页,一级页面隐藏 -->

        ${getSubPageList(lang)} <!-- 根据页面结构和JSON数据生成页面 -->

    </div>

</body>

3、遍历页面结构,根据每个子页的jsonStr生成页面元素


// 若存在英文,则选定英文为默认语言,若英文不存在,则选定第一个语言为默认语言

if (supportLangsList.includes('en')) defaultLang = 'en'

else defaultLang = supportLangsList[0]

  


// 子页的页面元素List

subPageItemList = JSON.parse(subPage.jsonStr) || []

// 遍历subPageItemList,生成页面组件html

getSubPageItem(subPageItemData, lang)

  


function getSubPageItem (data, lang = defaultLang) { // data:一个组件

    const classes = getClasses(data)

    const styles = getStyles(data)

  


    switch (data.type) {

    // 1标题 2段落 3图片 4无序列表 5有序列表 6自定义组件 7折叠组件 8表格组件 9一行N列组件

    // 若遇到7、8、9中的嵌套组件则递归调用 getSubPageItem()

    }

}

4、html文件里的JS代码生成要点

  • if isManualPage,则生成 目录页和一级页面间的互相跳转代码:goMenu()、myGoPage()

  • if needSwiperFlag,则生成swiper初始化代码

  • if needTroubleshooting,则生成troubleshooting点击响应代码以及防抖处理代码

五、展望

为提高系统的使用体验并拓宽H5需求的覆盖范围,系统可进一步在以下关键领域进行扩展与优化,旨在打造更加高效、灵活且用户友好的平台:

  1. 表格组件功能全面升级

引入动态表格管理功能,允许用户轻松实现增加行、增加列、删除行、删除列等操作,以满足复杂数据展示与编辑需求,提升数据处理效率。

  1. 组件复用机制优化

增设“我的组件库”,用户可自定义并保存常用组件,实现一键复用于多个页面,减少重复劳动,加速页面构建流程。

  1. 文档导入功能拓展

支持Document文档直接导入为H5文案,自动解析文本与格式,简化内容迁移与编辑步骤,提升内容创作效率。

  1. 邮件模板定制服务

引入邮件模板编辑器,支持用户根据需求定制个性化邮件模板,包括布局、样式及内容,强化品牌传播效果,提升邮件营销的专业性与互动性。

  1. 年度报告自动生成工具

开发年度报告制作模块,提供丰富的图表、模板与数据整合功能,帮助用户快速生成专业、美观的年度总结报告,展现企业发展成就。

  1. 项目开源策略实施

将系统核心组件与框架开源,不仅有助于吸引更多开发者参与贡献,共同推动技术进步,还能显著提升企业的技术影响力与品牌形象,构建更加开放的开发者生态。

通过上述扩展与优化措施,系统将在提升用户体验、增强功能灵活性的同时,进一步拓宽应用场景,为各类用户提供更加全面、高效的H5创作与管理工具。