通用评论
narrowizard 547f5348e8 Merge branch 'master-links123' 6 gadus atpakaļ
assets Merge branch 'master-links123' 6 gadus atpakaļ
lib Merge branch 'master-links123' 6 gadus atpakaļ
public add: 增加语言文件 6 gadus atpakaļ
src Merge branch 'master-links123' 6 gadus atpakaļ
.babelrc publish comment@0.0.1 6 gadus atpakaļ
.eslintrc publish comment@0.0.1 6 gadus atpakaļ
.gitignore publish comment@0.0.1 6 gadus atpakaļ
CHANGELOG.md Merge branch 'master-links123' 6 gadus atpakaļ
README.md Merge branch 'master-links123' 6 gadus atpakaļ
config-overrides.js fix: 对评论的样式名称做了修改,改为了 `comment-image-preview-container` 6 gadus atpakaļ
package.json feat: publish comment@1.0.0 6 gadus atpakaļ

README.md

Comment

通用评论系统及编辑器

version 0.5.14

import Comment, { Editor, RenderText } from 'comment';

使用

作为组件使用

# npm 安装
$ npm install git+https://git.links123.net/npm/comment.git --save
# yarn 安装
$ yarn add git+https://git.links123.net/npm/comment.git

然后在代码里面引入 comment 组件:

import Comment, { Editor } from 'comment'

// ...

render() {
  return (
    <Comment type={1} businessId="test">
      <Editor />
    </Comment>
  )
}

注意:最后,还需要在 HTML 文件里面引入阿里云 OSS SDK <script src="http://gosspublic.alicdn.com/aliyun-oss-sdk.min.js"></script>

在非 React.js 项目中使用 作为静态文件引入

使用assets 分支的 ./assets/static 目录https://git.links123.net/npm/comment/src/branch/assets/assets/static的静态文件

  1. 引入通用评论的 css 和 js 文件。在 assets 分支的 ./assets/static 目录: https://git.links123.net/npm/comment/src/branch/assets/assets/static
  2. 调用 window.renderComment 方法并传入对应参数来渲染通用评论。参数列表:
    • id: string, 必填, 渲染评论的节点的 ID
    • type: number, 必填, 评论的 type
    • businessId: string, 选填, 评论的 businessId,默认 test
    • API: string, 选填, 评论的 businessId,默认 http://api.links123.net/comment/v1

具体可参考 ./assets/example.html

例子如下:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>通用评论 demo</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" type="text/css" media="screen" href="./static/css/comment.0.5.13.css" />
</head>
<body>
  <div>
    <h1>在非 React.js 项目中使用通用评论</h1>
    <p>将其渲染到 id 为 `comment` 的节点上</p>
    <div id="comment"></div>
  </div>
  
  <script src="http://gosspublic.alicdn.com/aliyun-oss-sdk.min.js"></script>
  <script type="text/javascript" src="./static/js/comment.0.5.13.js"></script>
  <script type="text/javascript">
    // 调用 renderComment 这个方法,渲染通用评论
    window.renderComment({
      id: 'comment', // required, 渲染通用评论到 comment 这个节点,id 名称可 自定义
      type: 1, // required, 评论的 type
      businessId: 'test' // 评论的 businessId, 默认 test
      API: 'http://api.links123.net/comment/v1', // API 前缀, 默认 http://api.links123.net/comment/v1 
    });
  </script>
</body>
</html>

一个页面多次渲染评论

有的场景可能需要在一个页面上根据不同 bid 多次渲染评论,可以参考 ./assets/example/rerender.html

其方案就是,每次渲染之前,都清空之前渲染的节点内容,再重新渲染。否则 React 会任务两次 render 是同样的内容,就不渲染。

详解:

  1. 首先清空父标签的内容
  2. 动态创建渲染评论的标签
  3. 渲染评论
<!-- ... -->
  <link rel="stylesheet" type="text/css" media="screen" href="./static/css/xxx.css" />
<!-- ... -->
  <div id="RENDER_ELEMENT_WRAPPER"></div>
<!-- ... -->
  <script type="text/javascript" src="./static/js/xxx.js"></script>
<!-- ... -->
var RENDER_ELEMENT_WRAPPER = 'RENDER_ELEMENT_WRAPPER'; // 渲染评论的父标签
var RENDER_ELEMENT_COMMENT = 'comment'; // 渲染评论的标签的 id

/**
 * 根据 id 创建节点
 * @param {string} id id
 */
function createElement(id) {
  var node = document.createElement('div');
  node.setAttribute("id", id);
  document.getElementById(RENDER_ELEMENT_WRAPPER).appendChild(node); 
}

/**
 * 根据 id 删除子节点
 * @param {string} id id
 */
function removeChild(id) {
  var myNode = document.getElementById(id);
  while (myNode.firstChild) {
    myNode.removeChild(myNode.firstChild);
  }
}

function main() {
  document.getElementById('render').addEventListener('click', function() {
    // 首先清空父标签的内容
    removeChild(RENDER_ELEMENT_WRAPPER);

    var id = RENDER_ELEMENT_COMMENT;
    // 动态创建渲染评论的标签
    createElement(id);
    // 渲染评论
    window.renderComment({
      id: id,
      type: 1,
      businessId: 'test',
      API: 'http://api.links123.net/comment/v1',
    })
  })
  
  document.getElementById('re-render').addEventListener('click', function() {
    removeChild(RENDER_ELEMENT_WRAPPER)
    var id = RENDER_ELEMENT_COMMENT;
    createElement(id)
    window.renderComment({
      id: id,
      type: 1,
      businessId: 'test1',
      API: 'http://api.links123.net/comment/v1',
    })
  })
}

main();

class 样式冲突

assets 分支已修复。

master 分支的图片预览 container 样式和 boostrap 有冲突。在 fix/classname 分支对评论的样式名称做了修改,改为了 comment-image-preview-container

解决办法就是使用 fix/classname 分支中的静态文件:

尝试过使用 CSS Module 进行修改,不过改造量较大,改动的过程中影响了其他样式,所以暂时放弃了。

Comment

  • 标记了deprecated的配置项表示不推荐使用,并且可能在将来版本中不再受支持。
props type default required description
type number true 评论的 type
businessId string true 评论的 business id
API string http://api.links123.net/comment/v1 false API 前缀
showList boolean true false 是否显示评论列表
showEditor boolean true false 是否显示评论输入框
showAlertComment boolean false false 评论成功之后,是否通过 Antd 的 Message 组件进行提示
showAlertReply boolean false false 回复成功之后,是否通过 Antd 的 Message 组件进行提示
showAlertFavor boolean false false 点赞/取消点赞成功之后,是否通过 Antd 的 Message 组件进行提示
showError boolean true false 是否使用Antd的Message组件提示错误信息
onError function(msg) false 错误回调, 出错了会被调用
userId number false 用户id, comment内部不维护用户id, 调用组件时传递过来, 目前用于判断是否显示删除按钮
token string false [deprecated] token,用于身份认证,非必须。默认使用 cookie
pageType string more false 分页类别, more-加载更多 pagination-页码
page number false 页码受控模式,如果传递了此参数,则需要通过onPageChange回调手动维护page.
limit number 10 false 一次加载的评论数量
onGetMoreBtnClick function() false 点击查看更多按钮的回调
onPageChange function(page) false 页码发生变化时的回调,注意:分页数据获取还是在组件内部处理的
onDelete function(type) false 评论或回复删除成功后的回调, type: “comment” | “reply” , 用于区分删除的是评论还是回复
locales string 语言 false 语言,zh-CN/en-US。默认根据 url 中的 lang 参数来获取。所以一般情况可不传入

Editor

props type default required description
rows number 5 false 编辑器的高度。默认情况下,回复评论/回复回复的编辑器会比评论的编辑器高度小一行
placeholder string 说点什么吧… false 评论的中的提示文字
showEmoji boolean true false 是否显示 Toolbar 中表情工具
showUpload boolean true false 是否显示 Toolbar 中 上传图片工具
maxUpload number 1 false 最大能够上传的图片数量
value string false 编辑器的值。如果设置了该属性,则编辑器变为受控组件,需要父组件来维护 value
onChange function(value) false 编辑器内容改变的回调函数
onSubmit function({ text, files }) false 点击提交按钮的回调函数,text 为编辑器的文本,files 为上传的文件列表
beforeSubmit function ({text,files}):Promise false 点击提交按钮后的钩子, 若Promise resolve false则不触发onSubmit函数, 主要用于控制默认的submit行为
onCommentSuccess function() false 提交评论成功后的回调
btnSubmitText string 发表 false 提交按钮的文字
btnLoading boolean false false 按钮是否正在加载中
btnDisable boolean false false 按钮是否禁用
button ReactNode false 按钮组件。如果上面几个 btn 相关的属性都无法满足要求,则可以使用 button 来自定义提交编辑器值的按钮
emojiToolIcon ReactNode false Toolbar 中表情的图标
imageToolIcon ReactNode false Toolbar 中上传文件的图标
onRef function false 传递子组件的引用
closeUploadWhenBlur boolean false 当 upload 失去焦点(鼠标点击非 Upload 的区域)的时候,是否自动关闭 Popover
showError boolean true false 是否使用Antd的Message组件提示错误信息, 主要是上传图片出错的情况
onError function(msg,{response}) false 错误回调, 出错了会被调用, 主要是上传图片出错的情况
maxLength number 140 false 限制最大输入字数
autoFocus boolean false false 编辑器自动聚焦

什么时候不要使用 value/onChange/onSubmit

如果将 comment 作为通用评论组件,则不要使用 value onChange onSubmit。因为组件内部,实现了通用评论的业务逻辑。

可以使用 value/onChange/onSubmit 的情况:

  • 单独使用其中的 Editor。即 import { Editor } from 'comment'
  • 不需要展示评论列表,即设置 showList: false
// 单独使用 Editor
<Editor value={this.state.value} onChange={(v) => this.setState({ value })} />

// 不需要展示评论列表

<Comment type={1}  showList={false}>
  <Editor value={this.state.value} onChange={(v) => this.setState({ value })} />
</Comment>

button

如果使用 button,则 btnLoading btnDisable btnSubmitText 都会失效。因为这些属性是针对于编辑器默认的提交按钮设置的。

所以如果要提交编辑器的值,需要自己在 Button 组件上实现提交功能。编辑器的值,可以通过 onChange 方法获取到。

如果使用了 button 属性,并且没有为其设置 onClick 方法,则 onClick 默认为发布评论,即点击按钮会发表评论:

// 如下代码所示
// 点击“自定义按钮”的时候,会发表评论。这是由 Comment 组件内部实现的业务逻辑
<Comment type={1} businessId="test" showList={false}>
  <Editor 
    button={(
      <Button
        type="primary"
        ghost
      >
        自定义按钮
      </Button>
    )}
  />
</Comment>

如果使用了 button 属性,并且设置了 onClick 方法,则会覆盖默认的 onClick 方法:

// 下面的代码,点击的时候,不会提交评论
// 而是会输出 state 的值(
// 即编辑器的值,因为 onChange 将编辑器输入的值通过回调函数传递给了父组件)
<Comment type={1} businessId="test" showList={false}>
  <Editor 
    button={(
      <Button
        type="primary"
        ghost
        onChange={(value) => this.setState({ value })}
        onClick={() => console.log(this.state.value)}
      >
        自定义按钮
      </Button>
    )}
    value={this.state.value}
  />
</Comment>

onRef

如果你需要在父组件里面调用子组件的方法,就可以使用 onRef。主要是用于调用 Editor 组件内部的 resetState 方法。

如果你需要在父组件里面,手动清空 Editor 里面的数据,则可以使用 resetState。因为即使父组件传入了 value, Editor 内部也会保存一份 value 的值,用于 onSubmit 回调将值传递给父组件。所以可能存在,当子组件值没有清除,而父组件的 value 为空的情况,导致编辑器中文字和实际预想的不一致。所以当在父组件里面使用了 value 并重置 value 的时候,最好清空 Editor 的 state。

具体使用方式如下:


handleChangeSubmit() {
  // 点击按钮,调用 onSubmit 的时候,清空 Editor 的数据
  this.editor.resetState();
}


<Comment type={1} businessId="test">
  <Editor 
    // 使用 onRef 方法创建一个 Editor 组件的引用,并添加到 this 上面
    onRef={ref => this.editor = ref } 
    onSubmit={() => this.handleChangeSubmit()}
  />
</Comment>

注意上面的 onRef={ref => this.editor = ref },这是实现父组件调用子组件方法的关键。**

RenderText


import { RenderText } from 'comment';

render() {
  return RenderText('test [呲牙]')
}

开发

$ git clone https://git.links123.net/npm/comment
$ cd comment
$ yarn
$ yarn start
  • yarn build 将项目打包成一个单页应用
  • yarn lib 将项目打包成一个 es5 组件
  • yarn prettier 优化代码格式

TODO

  • 前后端统一错误码
  • type 和 businessID 的定义
  • Editor onSubmit 回调
  • 对评论的回复点赞,报错
  • oss/sts 接口报错
  • 头像 404 https://links123-images.oss-cn-hangzhou.aliyuncs.com/avatar/
  • 上传图片的时候偶尔会出现InvalidPart
  • 上传图片失败时, 提示并且不显示缩略图