参考文章,非常感谢大佬的分享
实现可高亮的输入框 — HighlightTextarea
GitHub:highlight-textarea
可看作者上一篇文章
若依 ruoyi-vue SpringBoot聊天敏感词过滤sensitive-word(一)
效果图
审核时,输入框高亮敏感词,并且能编辑
前端代码
列表高亮
<el-table-columnlabel="标题"align="center"prop="publishTitle":show-overflow-tooltip="true"><template slot-scope="scope"><span v-html="highlightPublishTitle(scope.row)"></span></template></el-table-column>
highlightPublishTitle(row){let text = row.publishTitlefor (const word of row.titleSensitiveWord) {const regex = new RegExp(word, 'g')text = text.replace(regex, `<span style="color: red;">${word}</span>`)}return text},
编辑表单输入框高亮
<el-form-item label="标题" prop="publishTitle"><highlight-textareav-if="open"style="width: 100%;"placeholder="请输入":text="form.publishTitle"type="input"@change="(value) => {form.publishTitle = value}":highlightKey="form.titleSensitiveWord"></highlight-textarea></el-form-item>
引用组件
import HighlightTextarea from '@/components/HighlightTextarea/index.vue';components: { HighlightTextarea },
npm安装
npm install less@^4.2.0 less-loader@^7.3.0
组件
<template><div class="highlight-box"><template v-if="type === 'textarea'"><div v-if="value"class="textarea-outer"ref="textareaOuter":style="{'height': `${maxHeight}px`}"><div ref="outerInner" class="outer-inner"v-html="highlightHtml(value)"></div></div><textarearef="textareaBox":style="{'height': `${maxHeight}px`}":placeholder="placeholder"@keyup.enter="syncScrollTop"v-model.trim="value"></textarea></template><template v-if="type === 'input'"><div v-if="value"class="input-outer"v-html="highlightHtml(value)"></div><input type="text":placeholder="placeholder"v-model.trim="value"/></template></div>
</template><script>
export default {name: 'HighlightTextarea',data() {return {value: ''}},props: {placeholder: {type: String,required: false,default: '请输入'},text: {type: String,required: false,default: ''},highlightKey: {type: Array,require: false,default: () => []},type: {type: String,required: true,default: 'textarea'},maxHeight: {type: Number,required: false,default: 220}},created() {this.value = this.text.replace(/(^\s*)|(\s*$)/g, '').replace(/<br \/>|<br\/>|<br>/g, '\n')},mounted() {this.scrollMousewheel()},computed: {},watch: {value(newValue) {this.$emit('change', newValue)}},methods: {highlightHtml(str) {if ((!str || !this.highlightKey || this.highlightKey.length === 0) && this.type !== 'textarea') {return str}let rebuild = strif (this.highlightKey.filter(item => ~str.indexOf(item)).length) {let regStr = ''let regExp = nullthis.highlightKey.forEach(list => {regStr = this.escapeString(list)regExp = new RegExp(regStr, 'g')rebuild = rebuild.replace(regExp, `<span>${list}</span>`)})}if (this.type === 'textarea') {rebuild = rebuild.replace(/\n/g, '<br/>').replace(/\s/g, ' ')// textarea有滚动条时,div底部不能和textarea重合,故加一个<br/>const wrap = this.$refs.textareaBoxif (wrap && wrap.scrollHeight > this.maxHeight) {rebuild = rebuild + '<br/>'}}return rebuild},syncScrollTop() {const wrap = this.$refs.textareaBoxconst outerWrap = this.$refs.textareaOuterconst outerInner = this.$refs.outerInnerif (wrap.scrollHeight > this.maxHeight && outerInner.scrollHeight !== wrap.scrollHeight) {outerInner.style.height = `${wrap.scrollHeight}px`}if (wrap.scrollTop !== outerWrap.scrollTop) {outerWrap.scrollTop = wrap.scrollTop}},scrollMousewheel() {if (this.type === 'input') {return}this.$nextTick(() => {this.eventHandler('add')})},// 处理字符串中可能对正则有影响的字符escapeString(value) {const characterss = ['(', ')', '[', ']', '{', '}', '^', '$', '|', '?', '*', '+', '.']let str = value.replace(new RegExp('\\\\', 'g'), '\\\\')characterss.forEach(function(characters) {let r = new RegExp('\\' + characters, 'g')str = str.replace(r, '\\' + characters)})return str},eventHandler(type) {const wrap = this.$refs.textareaBoxif (wrap) {let mousewheelevt = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel'wrap[`${type}EventListener`](mousewheelevt, this.syncScrollTop)wrap[`${type}EventListener`]('scroll', this.syncScrollTop)}}},destroyed() {this.eventHandler('remove')}
}
</script><style lang="less">
@width: 500px;
.highlight-box {font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;position: relative;display: flex;font-size: 12px;width: @width;position: relative;color: #333333;background: #ffffff;border-radius: 5px;overflow: hidden;.textarea-outer,.input-outer {box-sizing: border-box;width: @width;position: absolute;top: 0;left: 0;right: 0;border: 1px solid transparent;border-top: 0;// 鼠标事件失效 ie6-10不支持pointer-events: none;cursor: text;span {color: #F27C49;}&:hover {border-color: #4C84FF;}}.textarea-outer {overflow-y: auto;line-height: 20px;word-break: break-all;.outer-inner {padding: 5px 8px;width: 100%;box-sizing: border-box;}}textarea {width: @width;line-height: 20px;resize: none;}.input-outer,input {width: @width;height: 28px;line-height: 28px;}.input-outer {bottom: 0;padding: 0 8px;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;}textarea,input {font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;font-size: 12px;// position: relative;// z-index: 2;// 光标的颜色color: #333333;// 文本颜色text-shadow: 0 0 0 rgba(0, 0, 0, 0);-webkit-text-fill-color: transparent;background: transparent;border-radius: 5px;border: 1px solid #E0E0E0;padding: 4px 8px;box-sizing: border-box;&::placeholder {-webkit-text-fill-color: #999999;}&:hover {border-color: #4C84FF;}&:focus {border-color: #4C84FF;box-shadow: 0 0 0 2px #DBE4FF;outline: none;}}
}
</style>
后端代码
触发敏感词记录
/*** 敏感词检查**/public void sensitiveWordCheck(SubPublishing subPublishing) {List<TzLySensitiveWordRecord> recordList = new ArrayList<>();Date nowDate = DateUtils.getNowDate();//找出标题的敏感词List<String> titleList = SensitiveWordHelper.findAll(subPublishing.getPublishTitle());TzLySensitiveWordRecord dbTitle = sensitiveWordRecordService.selectTzLySensitiveWordRecordByMessageIdAndSource(subPublishing.getId(), HYZR_TITLE);//插入if (ObjectUtil.isNull(dbTitle) && CollectionUtils.isNotEmpty(titleList)) {TzLySensitiveWordRecord record = new TzLySensitiveWordRecord();record.setWordId(null);// 敏感词IDrecord.setWord(StrUtil.join(",", titleList)); //敏感词record.setMessageId(subPublishing.getId());// 主键IDrecord.setUserId(Long.valueOf(subPublishing.getCreatorId()));// 发送人IDrecord.setUserName(subPublishing.getCreatorName());// 发送人姓名record.setMobile(subPublishing.getCreatorTel());// 发送人手机号record.setSendTime(nowDate);// 发送时间record.setIsDeleted(0);// 是否删除record.setCreatedDate(nowDate);// 创建时间record.setCreatedBy(Long.valueOf(subPublishing.getCreatorId()));// 创建人record.setSource(HYZR_TITLE);//消息来源 会员值日record.setMessage(subPublishing.getPublishTitle()); //记录实际发送消息record.setReplaceMessage(subPublishing.getPublishTitle()); //记录替换的消息recordList.add(record);//更新} else if (ObjectUtil.isNotNull(dbTitle)) {dbTitle.setWord(StrUtil.join(",", titleList)); //敏感词dbTitle.setLastModifiedDate(nowDate);// 修改时间dbTitle.setLastModifiedBy(Long.valueOf(subPublishing.getCreatorId()));// 修改人dbTitle.setMessage(subPublishing.getPublishTitle()); //记录实际发送消息dbTitle.setReplaceMessage(subPublishing.getPublishTitle()); //记录替换的消息recordList.add( dbTitle);}TzLySensitiveWordRecord dbContent = sensitiveWordRecordService.selectTzLySensitiveWordRecordByMessageIdAndSource(subPublishing.getId(), HYZR_CONTENT);if (CollectionUtils.isNotEmpty(recordList)) {sensitiveWordRecordService.saveOrUpdateBatch(recordList);}}
回显敏感词记录
SubPublishing subPublishing = this.getById(id);//查询是否触发敏感词Map<String, TzLySensitiveWordRecord> sensitiveWordRecordMap = sensitiveWordRecordService.selectListByMessageId(id).stream().collect(Collectors.toMap(TzLySensitiveWordRecord::getSource, o -> o));if (!sensitiveWordRecordMap.isEmpty()) {TzLySensitiveWordRecord title = sensitiveWordRecordMap.get(HYZR_TITLE);if (ObjectUtil.isNotNull(title)) {subPublishing.setTitleSensitiveWord(StrUtil.split(title.getWord(), ","));}