- 1 import React, { useState, useRef, useEffect, forwardRef, useImperativeHandle } from 'react';
- 2 import { SmileOutlined } from '@ant-design/icons';
- 3 import { Row, Col, Button, Tooltip, message } from 'antd';
- 4
- 5 import styles from './index.less';
- 6
- 7 import {setCursorPostionEnd} from "./util";
- 8
- 9 const emojiPath = '/emojiImages/';
- 10 const emojiSuffix = '.png';
- 11 const emojiList = [...Array(15).keys()].map((_, index: number) => {
- 12 return { id: index + 1, path: emojiPath + (index + 1) + emojiSuffix };
- 13 });
- 14
- 15 type Props = {
- 16 uniqueId: string; // 唯一键
- 17 item?: object; // 携带参数
- 18 okClick: Function; // 发布
- 19 okText?: string;
- 20 };
- 21
- 22 const InputComment = forwardRef((props: Props, ref) => {
- 23 const { uniqueId: id, okClick, okText } = props;
- 24 const inputBoxRef = useRef<any>(null);
- 25 const [textCount, setTextCount] = useState(0);
- 26 let rangeOfInputBox: any;
- 27 const uniqueId = 'uniqueId_' + id;
- 28
- 29 const setCaretForEmoji = (target: any) => {
- 30 if (target?.tagName?.toLowerCase() === 'img') {
- 31 const range = new Range();
- 32 range.setStartBefore(target);
- 33 range.collapse(true);
- 34 // inputBoxRef?.current?.removeAllRanges();
- 35 // inputBoxRef?.current?.addRange(range);
- 36 const sel = window.getSelection();
- 37 sel?.removeAllRanges();
- 38 sel?.addRange(range);
- 39 }
- 40 };
- 41
- 42 /**
- 43 * 输入框点击
- 44 */
- 45 const inputBoxClick = (event: any) => {
- 46 const target = event.target;
- 47 setCaretForEmoji(target);
- 48 };
- 49
- 50 /**
- 51 * emoji点击
- 52 */
- 53 const emojiClick = (item: any) => {
- 54 const emojiEl = document.createElement('img');
- 55 emojiEl.src = item.path;
- 56 const dom = document.getElementById(uniqueId);
- 57 const html = dom?.innerHTML;
- 58
- 59 // rangeOfInputBox未定义并且存在内容时,将光标移动到末尾
- 60 if (!rangeOfInputBox && !!html) {
- 61 dom.innerHTML = html + `<img src="${item.path}"/>`;
- 62 setCursorPostionEnd(dom)
- 63 } else {
- 64 if (!rangeOfInputBox) {
- 65 rangeOfInputBox = new Range();
- 66 rangeOfInputBox.selectNodeContents(inputBoxRef.current);
- 67 }
- 68
- 69 if (rangeOfInputBox.collapsed) {
- 70 rangeOfInputBox.insertNode(emojiEl);
- 71 } else {
- 72 rangeOfInputBox.deleteContents();
- 73 rangeOfInputBox.insertNode(emojiEl);
- 74 }
- 75 rangeOfInputBox.collapse(false);
- 76
- 77 const sel = window.getSelection();
- 78 sel?.removeAllRanges();
- 79 sel?.addRange(rangeOfInputBox);
- 80 }
- 81 };
- 82
- 83 /**
- 84 * 选择变化事件
- 85 */
- 86 document.onselectionchange = (e) => {
- 87 if (inputBoxRef?.current) {
- 88 const element = inputBoxRef?.current;
- 89 const doc = element.ownerDocument || element.document;
- 90 const win = doc.defaultView || doc.parentWindow;
- 91 const selection = win.getSelection();
- 92
- 93 if (selection?.rangeCount > 0) {
- 94 const range = selection?.getRangeAt(0);
- 95 if (inputBoxRef?.current?.contains(range?.commonAncestorContainer)) {
- 96 rangeOfInputBox = range;
- 97 }
- 98 }
- 99 }
- 100 };
- 101
- 102 /**
- 103 * 获取内容长度
- 104 */
- 105 const getContentCount = (content: string) => {
- 106 return content
- 107 .replace(/ /g, ' ')
- 108 .replace(/<br>/g, '')
- 109 .replace(/<\/?[^>]*>/g, '占位').length;
- 110 };
- 111
- 112 /**
- 113 * 发送
- 114 */
- 115 const okSubmit = () => {
- 116 const content = inputBoxRef.current.innerHTML;
- 117 if (!content) {
- 118 return message.warning('温馨提示:请填写评论内容!');
- 119 } else if (getContentCount(content) > 1000) {
- 120 return message.warning(`温馨提示:评论或回复内容小于1000字!`);
- 121 }
- 122
- 123 okClick(content);
- 124 };
- 125
- 126 /**
- 127 * 清空输入框内容
- 128 */
- 129 const clearInputBoxContent = () => {
- 130 inputBoxRef.current.innerHTML = '';
- 131 };
- 132
- 133 // 将子组件的方法 暴露给父组件
- 134 useImperativeHandle(ref, () => ({
- 135 clearInputBoxContent,
- 136 }));
- 137
- 138 // 监听变化
- 139 useEffect(() => {
- 140 const dom = document.getElementById(uniqueId);
- 141 const observer = new MutationObserver(() => {
- 142 const content = dom?.innerHTML ?? '';
- 143 // console.log('Content changed:', content);
- 144 setTextCount(getContentCount(content));
- 145 });
- 146
- 147 if (dom) {
- 148 observer.observe(dom, {
- 149 attributes: true,
- 150 childList: true,
- 151 characterData: true,
- 152 subtree: true,
- 153 });
- 154 }
- 155 }, []);
- 156
- 157 return (
- 158 <div style={{ marginTop: 10, marginBottom: 10 }} className={styles.inputComment}>
- 159 {textCount === 0 ? (
- 160 <div className="input-placeholder">
- 161 {okText === '确认' ? '回复' : '发布'}评论,内容小于1000字!
- 162 </div>
- 163 ) : null}
- 164
- 165 <div
- 166 ref={inputBoxRef}
- 167 id={uniqueId}
- 168 contentEditable={true}
- 169 placeholder="adsadadsa"
- 170 className="ant-input input-box"
- 171 onClick={inputBoxClick}
- 172 />
- 173 <div className="input-emojis">
- 174 <div className="input-count">{textCount}/1000</div>
- 175
- 176 <Row wrap={false}>
- 177 <Col flex="auto">
- 178 <Row wrap={true} gutter={[0, 10]} align="middle" style={{ userSelect: 'none' }}>
- 179 {emojiList.map((item, index: number) => {
- 180 return (
- 181 <Col
- 182 flex="none"
- 183 onClick={() => {
- 184 emojiClick(item);
- 185 }}
- 186
- 187 <Col flex="none" style={{ marginTop: 5 }}>
- 188 <Button
- 189 type="primary"
- 190 disabled={textCount === 0}
- 191 onClick={() => {
- 192 okSubmit();
- 193 }}
- 194 >
- 195 {okText || '发布'}
- 196 </Button>
- 197 </Col>
- 198 </Row>
- 199 </div>
- 200 </div>
- 201 );
- 202 });
- 203
- 204 export default InputComment;
- 1 /**
- 2 * 光标放到文字末尾(获取焦点时)
- 3 * @param el
- 4 */
- 5 export function setCursorPostionEnd(el:any) {
- 6 if (window.getSelection) {
- 7 // ie11 10 9 ff safari
- 8 el.focus() // 解决ff不获取焦点无法定位问题
- 9 const range = window.getSelection() // 创建range
- 10 range?.selectAllChildren(el) // range 选择obj下所有子内容
- 11 range?.collapseToEnd() // 光标移至最后
- 12 } else if (document?.selection) {
- 13 // ie10 9 8 7 6 5
- 14 const range = document?.selection?.createRange() // 创建选择对象
- 15 // var range = document.body.createTextRange();
- 16 range.moveToElementText(el) // range定位到obj
- 17 range.collapse(false) // 光标移至最后
- 18 range.select()
- 19 }
- 20 }