# SensitiveWordScan 敏感词检测

- 来源：[E10 UI 组件库](https://www.e-cology.com.cn/ui/sensitive-word-scan/freepass)
- 平台：PC
- 维护者：蒋贝贝
- 设计稿：http://pd.e-cology.com.cn/pd/E9/interface/xiaoguotu/wendangmingancichakan/#artboard1
- Issue：http://gitlab.ecology10.cn/FRONTEND/weapp-ui/-/issues?state=all&search=[SensitiveWordScan]

## 代码演示

### 基础用法

直接引入组件进行使用

```jsx
import React from "react";
import { Input, Textarea, SensitiveWordScan, SensitiveWordScanType, Layout, Icon, constants, AnyObj, RichText, Comment, FormStore, Form, utils } from "@weapp/ui";
const prefixCls = `${constants.uiPcClsPrefix}-demo-sensitiveWordScan-basic`;

//@ts-ignore
const { getHd } = utils;
const commonValue = "测试我发送法轮功的返回值法伦，现金网，现金网，现金网现金网现金网";
export default class SensitiveWordScanDemo extends React.Component<any, any> {
  scanRef = React.createRef<SensitiveWordScanType>();
  constructor(props: any) {
    super(props);
    this.state = {
      inputValue: commonValue,
      textareaValue: commonValue,
      richtextValue: commonValue,
    }
  }
  formStore = new FormStore();
  componentDidMount() {
    this.formStore.initForm({
      data: {
        input: commonValue,
        inputReadOnly: commonValue,
        textarea: commonValue,
        textareaReadOnly: commonValue,
        richtext: commonValue,
        richtextReadOnly: commonValue,
      },
      items: {
        input: {
          itemType: 'INPUT',
          otherParams: {
            highLightProps: this.highLightInputProps,
          }
        },
        inputReadOnly: {
          itemType: 'INPUT',
          readOnly: true,
          otherParams: {
            highLightProps: this.highLightInputProps,
          }
        },
        textarea: {
          itemType: 'TEXTAREA',
          otherParams: {
            highLightProps: this.highLightTextareaProps,
          }
        },
        textareaReadOnly: {
          itemType: 'TEXTAREA',
          readOnly: true,
          otherParams: {
            highLightProps: this.highLightTextareaProps,
          }
        },
        richtext: {
          itemType: 'RICHTEXT',
          otherParams: {
            highLightProps: this.highLightRichTextProps,
          }
        },
        richtextReadOnly: {
          itemType: 'RICHTEXT',
          readOnly: true,
          otherParams: {
            highLightProps: this.highLightRichTextProps,
          }
        }
      },
      layout: [
        [ {id: 'input', items: ['input'], hide: false, label: '单行文本', labelSpan: 6 } ],
        [ {id: 'input', items: ['inputReadOnly'], hide: false, label: '单行文本(只读)', labelSpan: 6  } ],
        [ {id: 'input', items: ['textarea'], hide: false, label: '多行文本', labelSpan: 6  } ],
        [ {id: 'input', items: ['textareaReadOnly'], hide: false, label: '多行文本（只读）', labelSpan: 6  } ],
        [ {id: 'input', items: ['richtext'], hide: false, label: '富文本', labelSpan: 6  } ],
        [ {id: 'input', items: ['richtextReadOnly'], hide: false, label: '富文本（只读）', labelSpan: 6  } ],
      ],
      groups: []
    })
  }
  onChangeInputValue = (value: any) => {
    this.setState({ inputValue: value });
  }
  onChangeTextareaValue = (value: any) => this.setState({ textareaValue: value });
  onChangRichtextValue = (value: any) => this.setState({ richtextValue: value });
  scan = () => {
    const { inputValue, richtextValue, textareaValue } = this.state;
    const content = `${inputValue}${textareaValue}${richtextValue}`;
    this.scanRef?.current?.scan?.(content);
  }
  style = { height: 1000 * (getHd?.() || 1)}
  onChooseSensitiveWord = (data: AnyObj) => console.log('onChooseSensitiveWord', data);
  unChooseSensitiveWord = () => console.log('unChooseSensitiveWord');
  highLightInputProps = {
    type: 'input',
  }
  highLightTextareaProps = {
    type: 'textarea',
  }
  highLightRichTextProps = {
    type: 'richtext'
  }
  textareaProps = { enableHighLight: true, highLightProps: this.highLightTextareaProps }
  richTextProps = { enableHighLight: true, highLightProps: this.highLightRichTextProps }
  render() {
    const { inputValue, textareaValue, richtextValue } = this.state;
    return (<Layout style={this.style} className={prefixCls}>
      <Layout.Box type="side" width={45 * (getHd?.() || 1)} className={`${prefixCls}-left`}>
        <div className={`${prefixCls}-icon`}>
          <Icon name="Icon-Sensitive-words" onClick={this.scan} size="lg" />
        </div>
      </Layout.Box>
      <Layout.Box type="content">
        <SensitiveWordScan
         
          ref={this.scanRef}
          tip="自动识别正文中的敏感词"
          onChooseSensitiveWord={this.onChooseSensitiveWord}
          unChooseSensitiveWord={this.unChooseSensitiveWord}
        >
          <div className={`${prefixCls}-content-container`}>
            <div className={`${prefixCls}-content`}>
              <div className={`${prefixCls}-title`}>单行文本</div>
              <Input value={inputValue} onChange={this.onChangeInputValue} enableHighLight highLightProps={this.highLightInputProps} />
            </div>
            <div className={`${prefixCls}-content`}>
              <div className={`${prefixCls}-title`}>单行文本(只读)</div>
              <Input value={inputValue} onChange={this.onChangeInputValue} readOnly enableHighLight highLightProps={this.highLightInputProps}  />
            </div>
            <div className={`${prefixCls}-content`}>
              <div className={`${prefixCls}-title`}>单行文本-可清空</div>
              <Input value={inputValue} onChange={this.onChangeInputValue} enableHighLight allowClear highLightProps={this.highLightInputProps}  />
            </div>
            <div className={`${prefixCls}-content`}>
              <div className={`${prefixCls}-title`}>单行文本-可清空(只读)</div>
              <Input value={inputValue} onChange={this.onChangeInputValue} readOnly enableHighLight allowClear highLightProps={this.highLightInputProps}  />
            </div>
            <div className={`${prefixCls}-content`}>
              <div className={`${prefixCls}-title`}>多行文本</div>
              <Textarea  value={textareaValue} onChange={this.onChangeTextareaValue} enableHighLight highLightProps={this.highLightTextareaProps} />
            </div>
            <div className={`${prefixCls}-content`}>
              <div className={`${prefixCls}-title`}>多行文本(只读)</div>
              <Textarea value={textareaValue} onChange={this.onChangeTextareaValue} readOnly enableHighLight highLightProps={this.highLightTextareaProps} />
            </div>
            <div className={`${prefixCls}-content`}>
              <div className={`${prefixCls}-title`}>多行文本-可清空</div>
              <Textarea  value={textareaValue} onChange={this.onChangeTextareaValue} enableHighLight allowClear highLightProps={this.highLightTextareaProps}/>
            </div>
            <div className={`${prefixCls}-content`}>
              <div className={`${prefixCls}-title`}>多行文本-可清空(只读)</div>
              <Textarea value={textareaValue} onChange={this.onChangeTextareaValue} readOnly enableHighLight allowClear highLightProps={this.highLightTextareaProps}/>
            </div>
            <div className={`${prefixCls}-content`}>
              <div className={`${prefixCls}-title`}>富文本</div>
              <RichText value={richtextValue} onChange={this.onChangRichtextValue}
              //@ts-ignore
              enableHighLight highLightProps={this.highLightRichTextProps} />
            </div>
            <div className={`${prefixCls}-content`}>
              <div className={`${prefixCls}-title`}>富文本(只读)</div>
              <RichText value={richtextValue} onChange={this.onChangRichtextValue} readOnly 
              //@ts-ignore
              enableHighLight highLightProps={this.highLightRichTextProps} />
            </div>
          </div>
        </SensitiveWordScan>
      </Layout.Box>
      <Layout.Box type="side" right weight={8}>
        <div className={`${prefixCls}-content-container`}>
          <Form store={this.formStore} enableHighLight />
          <div className={`${prefixCls}-content`}>
              <div className={`${prefixCls}-title`}>评论</div>
              <Comment
                module={"share"}
                objectModule="bcw"
                targetId="752714034012356610"
                textareaProps={this.textareaProps}
              />
              <br/>
              <Comment
                module={"share"}
                objectModule="bcw"
                targetId="752714034012356610"
                useRichText
                //@ts-ignore
                richTextProps={this.richTextProps}
              />
            </div>
          </div>
      </Layout.Box>
    </Layout>)
  }
}
```

### 浮层模式

直接引入组件进行使用

```jsx
import React from "react";
import { AnyObj, Button, FormItem, Select, SelectValueType, SensitiveWordScan, SensitiveWordScanType } from "@weapp/ui";
export default class SensitiveWordScanDemo extends React.Component<any, any> {
  constructor(props: any) {
    super(props);
    this.state = {
      componentType: 'all',
      layoutType: 'trigger',
      draggable: 'n',
    }
  }
  scanRef = React.createRef<SensitiveWordScanType>();
  scan = () => {
    this.scanRef?.current?.scan?.({ "key": "测试我发送法轮功的返回值法伦，现金网，现金网，现金网现金网现金网" });
  }
  onChooseSensitiveWord = (data: AnyObj) => console.log('onChooseSensitiveWord', data);
  unChooseSensitiveWord = () => console.log('unChooseSensitiveWord');
  beforeHighLight = (config: AnyObj) => {
    console.error('beforeHighLight-float', config);
    if (this.state.componentType !== 'all') {
      return config?.type === this.state.componentType;
    }
    return true;
  }
  options = [
    { id: 'all', content: '全部' },
    { id: 'input', content: '单行文本' },
    { id: 'textarea', content: '多行文本' },
    { id: 'richtext', content: '富文本' }
  ]
  optionsFloat = [
    { id: 'float', content: 'Dialog浮层模式' },
    { id: 'trigger', content: 'Trigger浮层模式' },
  ]
  iconOption = [
    { id: 'n', content: '否' },
    { id: 'y', content: '是' }
  ]
  onChangeSelect = (value: SelectValueType) => this.setState({ componentType: value })
  onChangeLayoutType = (value: SelectValueType) => this.setState({ layoutType: value })
  onChangeDraggable = (value: SelectValueType) => this.setState({ draggable: value });
  render() {
    return (<div>
      <FormItem label="浮层效果" labelSpan={6}>
        <Select data={this.optionsFloat} value={this.state.layoutType} onChange={this.onChangeLayoutType}/>
      </FormItem>
      <FormItem label="生效范围" labelSpan={6}>
        <Select data={this.options} value={this.state.componentType} onChange={this.onChangeSelect}/>
      </FormItem>
      <FormItem label="图标拖拽" labelSpan={6}>
        <Select data={this.iconOption} value={this.state.draggable} onChange={this.onChangeDraggable}/>
      </FormItem>
      <Button type="primary" onClick={this.scan}>敏感词检测</Button>
      <SensitiveWordScan
       
        ref={this.scanRef}
        tip="自动识别正文中的敏感词"
        layoutType={this.state.layoutType}
        onIconClick={this.scan}
        onChooseSensitiveWord={this.onChooseSensitiveWord}
        unChooseSensitiveWord={this.unChooseSensitiveWord}
        beforeHighLight={this.beforeHighLight}
        draggable={this.state.draggable === 'y'}
      />
    </div>)
  }
}
```

### 单独使用组件

直接引入组件进行使用

```jsx
import React from "react";
import { SensitiveWordScan, SensitiveWordScanContentRef, Layout, Icon, constants, AnyObj, sensitiveWordScanUtils, utils } from "@weapp/ui";
import { classnames } from "@weapp/utils";
const prefixCls = `${constants.uiPcClsPrefix}-demo-sensitiveWordScan-single`;
const { closeHighLight } = sensitiveWordScanUtils;
const { getHd } = utils;
export default class SensitiveWordScanDemo extends React.Component<any, any> {
  constructor(props: any) {
    super(props);
    this.state = {
      visible: false,
    }
  }
  scanRef = React.createRef<SensitiveWordScanContentRef>();
  beforeHighLight = (config: AnyObj) => {
    console.log('beforeHighLight-single', config);
    return true;
  }
  scan = () => {
    this.scanRef?.current?.scan?.("测试我发送法轮功的返回值法伦，现金网，现金网，现金网现金网现金网");
    this.setState({ visible: true });
  }
  style = { height: 600 }
  onClose = () => {
    closeHighLight();
    this.setState({ visible: false });
  }
  onChooseSensitiveWord = (data: AnyObj) => console.error('onChooseSensitiveWord', data);
  render() {
    const { visible } = this.state;
    const leftCls = classnames(`${prefixCls}-left`, {
      [`${prefixCls}-left-hidden`]: !visible,
    })
    const hd = getHd?.() || 1;
    return (<Layout style={this.style} className={prefixCls}>
      <Layout.Box type="side" width={45 * hd} outer className={leftCls}>
        <div className={`${prefixCls}-icon`}>
          <Icon name="Icon-Sensitive-words" onClick={this.scan} size="lg" />
        </div>
      </Layout.Box>
      <Layout.Box type="side" width={400 * hd} hidden={!visible} hiddenControlled className={`${prefixCls}-left`}>
        <SensitiveWordScan.Title
         
          onClose={this.onClose}
          tip="自动识别正文中的敏感词"
        />
        <SensitiveWordScan.Content
          ref={this.scanRef}
          onChooseSensitiveWord={this.onChooseSensitiveWord}
        />
      </Layout.Box>
      <Layout.Box type="content"></Layout.Box>
    </Layout>)
  }
}
```

## API

### 参数说明如下：

| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
| --- | --- | --- | --- | --- |
| layoutType | 布局类型（浮层、套用布局组件） | 'float' \| 'layout' \| 'trigger' | 否 |  |
| title | 标题 | string | 否 |  |
| tip | 提示信息 | string | 否 |  |
| layoutBoxType | 布局位置(layoutType='layout'时生效) | 'left' \| 'right' | 否 |  |
| dialogProps | Dialog其他属性(layoutType='float'时生效，详细见Dialog文档) | DialogProps | 否 |  |
| onIconClick | 图标点击触发事件(浮层模式使用) | () => void | 否 |  |
| hideIcon | 隐藏图标(浮层模式使用) | boolean | 否 |  |
| onChooseSensitiveWord | 选中敏感词回调事件 | (data: AnyObj) => void | 否 |  |
| unChooseSensitiveWord | 取消选中敏感词回调事件 | () => void | 否 |  |
| beforeRetest | 重新检测按钮触发前回调 | (content?: string) => string \| Promise<string> | 否 |  |
| onRetest | 重新检测按钮回调 | (result: any) => void | 否 |  |
| url | 检测敏感词接口自定义url | string | 否 |  |
| params | 检测敏感词接口自定义参数 | AnyObj | 否 |  |
| reCheckSelectedWord | 重新检测选中的敏感词 | boolean | 否 |  |
| beforeHighLight | 高亮前事件 ｜ (config: AnyObj) => boolean \| 'disabled' | 否 |  |  |
| draggable | 图标拖拽(浮层/Trigger模式) | boolean | 否 |  |
| quickMenuProps | 悬浮按钮特殊配置 | QuickMenuProps | 否 |  |
#### ✳️ 浮层模式，hideIcon为false时，必须设置onIconClick，用于触发点击图标敏感词检测的功能

### scan 敏感词扫描方法

```
1.通过ref调用
2.内部处理敏感词检测及高亮效果
3.scan: (content?: string | AnyObj, otherParams?: AnyObj, otherAction?: AnyObj) => Promise<any>; // content为所有需要检测敏感词的文本
```

### SensitiveWordScan.Icon 组件说明

| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
| --- | --- | --- | --- | --- |
| onClick | 图标点击事件 | () => void | 否 |  |
| iconProps | icon组件配置 | IconProps | 否 |  |
| beforeHighLight | 高亮前事件 ｜ (config: AnyObj) => boolean \| 'disabled' | 否 |  |  |
| draggable | 图标拖拽(浮层/Trigger模式) | boolean | 否 |  |
| quickMenuProps | 悬浮按钮特殊配置 | QuickMenuProps | 否 |  |
### SensitiveWordScan.Title 组件说明

| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
| --- | --- | --- | --- | --- |
| title | 标题 | string | 否 |  |
| tip | 提示信息 | string | 否 |  |
| onClose | 提示信息 | () => void | 否 |  |
| titleProps | Title 组件自定义属性（详细见Title组件文档） | TitleProps | 否 |  |
### SensitiveWordScan.Content 组件说明

| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
| --- | --- | --- | --- | --- |
| onChooseSensitiveWord | 选中敏感词回调事件 | (data: AnyObj) => void | 否 |  |
| beforeRetest | 重新检测按钮触发前回调 | (content?: string) => string \| Promise<string> | 否 |  |
| onRetest | 重新检测按钮回调 | (result: any) => void | 否 |  |
| url | 检测敏感词接口自定义url | string | 否 |  |
| params | 检测敏感词接口自定义参数 | AnyObj | 否 |  |
| reCheckSelectedWord | 重新检测选中的敏感词 | boolean | 否 |  |
| beforeHighLight | 高亮前事件 | (config: AnyObj) => boolean \| 'disabled' | 否 |  |
```
1.通过ref调用scan 敏感词扫描方法
2.内部处理敏感词检测及高亮效果
3.scan: (content?: string | AnyObj, otherParams?: AnyObj, otherAction?: AnyObj) => Promise<any>;
 (1) content为所有需要检测敏感词的文本
 (2) otherParams 传入给到接口的自定义参数
 (3) otherAction 传入自定义方法，目前仅支持传入beforeHighLight，用作高亮拦截
```

### SensitiveWordScan.HighLight 组件说明

##### 用于处理Input、Textarea、RichText组件高亮效果

| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
| --- | --- | --- | --- | --- |
| value | 被检测的数据 | string | 否 |  |
| type | 高量组件类型 | 'input' \| 'textarea' | 否 |  |
| wrapClassName | 容器类名 | string | 否 |  |
| children | 高亮区内容 | ( props: { hightLightId: string; addListenerForResize: any; key: string: any; },) => React.ReactElement | 否 |  |
| componentId | 自定义组件标识 | string | 否 |  |
⚠️ HighLight 组件接受的value，为被检测的数据，为组件原始value数据，并非带了高亮标记的数据

##### children 说明

```
1.hightLightId : HighLight组件内部生成，确保唯一，使用方拼在weId上，避免weId冲突
2.addListenerForResize : 传入ref
3. 1、2亮点用于HighLight组件内部处理高亮区与Input/Textarea组件内容同步效果
```

### 高亮范围限制方案

```
1.HighLight组件支持配置自定义组件标识（componentId）
2.scan方法里面判断，不满足条件则不做高亮处理
  通过beforeHighLight方法判断，具体见Demo代码示例
```
