import { Button, Input, message, Modal, Popover, Radio, Space, Tooltip } from 'antd' import React, { useCallback, useEffect, useRef, useState } from 'react' import Expression from '@/components/Expression' import style from './text.less' import sanitizeHtml from 'sanitize-html'; import { RadioChangeEvent } from 'antd/lib/radio'; type Props = { visible: boolean, onCancel: () => void, onOK: (props: any, type?: number) => void defaultData?: any, hdLink?: boolean, fansName?: boolean } const sanitizeConf: sanitizeHtml.IOptions = { allowedTags: ['br', 'a'], allowedAttributes: { a: ["href", "_href", "data-miniprogram-appid", "data-miniprogram-path"] }, allowedSchemes: ['http', 'https', 'weixin'] }; /**文本弹窗 */ const WxTextModal = React.memo((props: Props) => { const { visible, onCancel, onOK, fansName = true, hdLink = true } = props const [value, setValue] = useState('')//存放连接文字 const [content, setContent] = useState('')//存放发送文字 const [isShow, setIsShow] = useState(false)//互动弹窗 const [range, setRange] = useState()//丢失焦点存放焦点位置 const [ref, setRef] = useState(null)//存放编辑框的实例 const [aLink, setAlink] = useState('')//存放页面链接 const [appID, setAppID] = useState('')//存放小程序APPID const [path, setPath] = useState('')//存放小程序路径 const [userId, setUserId] = useState(2)//小程序路径插入userID const [textData, setTextData] = useState({ range: null, left: -100, top: -100, text: '' }) const [isLink, setIsLink] = useState(false) const [isWxLink, setIsWxLink] = useState(false) const [isHtml, setIsHtml] = useState(false) const text = useRef(''); const [phoneType, setPhoneType] = useState<1 | 2 | 3|4>(3) /** * 发送处理 */ const callback = useCallback(() => { if (ref.innerHTML) { console.log(ref.innerHTML) let textContent: any = sanitizeHtml(ref.innerHTML, sanitizeConf); console.log(textContent) textContent = textContent.replace(/"/ig, '"') .replace(/&/ig, '&') .replace(/</ig, '<') .replace(/>/ig, '>') .replace(/<\s+/g, '<') .replace(/[\f\n\r\t\v]/g, '
')//将回车变成br .replace(/\ (
)?/g, '') .replace(/<[/]?myspan[^>]*>/ig, '#') .replace(/#
/g, '#') .replace(/<([a-z]+?)(?:\s+?[^>]*?)?>[\s(
)]*?<\/\1>/ig, '
')//替换所有空标签或空标签带
的标签为
.replace(/(\b
(
)?)|((
)?
)/ig, '
')//
or
or

转br .replace(/<\/div>/ig, '')//
转‘’ .replace(/_href="\s+/ig, '_href="')//清除href头部空格 .replace(/href=weixin/ig,'href="weixin') .replace(/msgmenuid="/ig,'msgmenuid= "') .replace(/"=""/ig,'') // .replace(/\s+"/ig, '"')//清除href尾部空格 .split('
') onOK({ textContent: textContent, indexId: props.defaultData?.indexId //textContent.filter((str)=> str!=='') 注释保留空格和换行 }, 4) } else { alert('元素获取失败请复制内容刷新页面') message.error('请输入文字') } handleClose() }, [ref]) /** * 互动连接文字 */ const textChange = useCallback((e: React.ChangeEvent) => { let v = e.target.value setValue(v) }, []) /** * 互动发送文本 */ const contentChange = useCallback((e: React.ChangeEvent) => { let v = e.target.value setContent(v) }, []) /** * 网页小程序连接地址 */ const aLinkChange = useCallback((e: React.ChangeEvent) => { let v = e.target.value setAlink(v) }, []) /** * 小程序路径 */ const pathChange = useCallback((e: React.ChangeEvent) => { let v = e.target.value setPath(v) }, []) /** * 小程序ID */ const appIDChange = useCallback((e: React.ChangeEvent) => { let v = e.target.value setAppID(v) }, []) /** * 插入表情 */ const getStr = useCallback((str: string, range: Range) => { if (range) { let selection = window.getSelection() selection?.empty()//清空选择range selection?.addRange(range)//插入新的range document.execCommand('insertHtml', false, str + ' '); } }, []) /** * 光标丢失记录位置 */ const onBlur = useCallback(() => { try { let selection = window.getSelection() let range = selection?.getRangeAt(0) setRange(range) } catch (err) { } }, [text]) /** * 插入粉丝昵称 */ const pushName = useCallback(() => { let selection = window.getSelection() selection?.empty()//清空选择range selection?.addRange(range as Range)//插入新的range document.execCommand('insertHtml', false, `#粉丝昵称#`); selection?.collapseToEnd()//光标插入到末尾 }, [range]) /** * 插入互动链 */ const pushLink = useCallback(() => { if (!value || !content) { setIsShow(false) return } let selection = window.getSelection() selection?.empty()//清空选择range selection?.addRange(range as Range)//插入新的range if(phoneType === 4){ document.execCommand('insertHtml', false, `${value}`); }else{ document.execCommand('insertHtml', false, `${value}`); } selection?.collapseToEnd()//光标插入到末尾 setValue('') setContent('') setIsShow(false) }, [range, value, content, phoneType]) /** * 初始光标选中 */ useEffect(() => { console.log('ref') if (ref) { ref?.focus() } }, [ref]) //复制只取纯文本 function textPaste(event: any) { event.preventDefault(); let text; let clp = (event.originalEvent || event).clipboardData; // 兼容chorme或hotfire text = (clp.getData('text/plain') || clp.getData('text')) .replace(/"/ig, '"') .replace(/&/ig, '&') .replace(/</ig, '<') .replace(/>/ig, '>') .replace(/<\s+/g, '<') .replace(/href="weixin/ig,'href=weixin') text = sanitizeHtml(text, sanitizeConf) || ""; if (text !== "") { document.execCommand('insertHtml', false, text); } } /** * 编辑默认内容写入 */ useEffect(() => { console.log('编辑默认内容写入', props?.defaultData?.textContent) if (props?.defaultData?.textContent && ref) { let text = ''; if (Array.isArray(props?.defaultData?.textContent)) { props?.defaultData?.textContent?.map((key: any, index: number) => { key = key.replace(/href="weixin/ig,'href=weixin') if (key.indexOf('<') !== -1) {//假如存在就是原文 setIsHtml(true) } text += index !== props?.defaultData?.textContent?.length - 1 ? `${key}
` : key }) } else { text = `${props?.defaultData?.textContent}` } document.execCommand('insertHtml', true, text); } }, [ref]) /**切换文本原文 */ const onHtml = useCallback(() => { let str: string = ref.innerHTML str = str.replace(/"/ig, '"').replace(/&/ig, '&').replace(/</ig, '<').replace(/>/ig, '>').replace(/<\s+/g, '<').replace(/""/ig,'"').replace(/"="/ig,'') if (isHtml) { console.log('str1',str) setIsHtml(false) ref.innerHTML = '' ref.innerHTML = str } else { console.log('str2',str) setIsHtml(true) ref.innerHTML = '' ref.innerText = str.replace(//ig, '\n').replace(/ /ig, ' ') } }, [ref, text, isHtml]) //文本选中 let handleSelectText = useCallback((event: React.SyntheticEvent) => { let selection = window.getSelection ? window.getSelection() : (document.getSelection ? document.getSelection() : (document?.selection ? document?.selection.createRange().text : "")) let text = selection.toString() || selection.text if (text) {//存在文本弹窗 let range = selection?.getRangeAt(0) let { top, left } = range?.getBoundingClientRect() setTextData({ range, top, left, text }) } else { handleClose()//关闭弹窗 } }, []) //点击设置连接转换输入,清空连接处理 let handleLink = useCallback((type: number) => { console.log('点击设置连接转换输入,清空连接处理') switch (type) { case 1: setIsLink(true) break; case 2: setIsWxLink(true) break; default: let selection = window.getSelection() selection?.empty()//清空选择range selection?.addRange(textData?.range)//插入新的range document.execCommand('unlink')//清除连接 selection?.collapseToEnd()//光标插入到末尾 handleClose()//关闭弹窗 break } }, [textData]) //清空数据并关闭弹窗 let handleClose = useCallback(() => { console.log('清空数据并关闭弹窗') setTextData({ range: null, left: -100, top: -100, text: '' }) setIsWxLink(false) setIsLink(false) setAppID('') setAlink('') setPath('') }, []) //ok插入连接数据 let handleOk = useCallback(() => { let selection = window.getSelection() selection?.empty()//清空选择range selection?.addRange(textData?.range)//插入新的range if (isWxLink) { if (aLink && appID && path) { let Apath = path if (userId) { if (userId === 1 && path && !path.includes('#USER_ID#')) { Apath = Apath + '#USER_ID#' } else if (userId === 2 && path && !path.includes('#QC_USER_ID#')) { Apath = Apath + '#QC_USER_ID#' } } document.execCommand('insertHTML', true, `${textData?.text}`);//插入连接 selection?.collapseToEnd() handleClose() } else { message.error('请填写完整') } } else { if (aLink && aLink.search(/http[s]?:\/\//ig) !== -1) {// document.execCommand('createLink', false, aLink);//插入连接 selection?.collapseToEnd() handleClose() } else { message.error('请填入正确的连接') } } }, [textData, aLink, appID, path, isWxLink, range, userId]) //处理选中的文本 return { handleClose() text.current = '' onCancel() }} onOk={callback} destroyOnClose >
{ // fansName && } { hdLink && { setIsShow(visible) }} content={
{ setPhoneType(e.target.value) }} value={phoneType} > 通用 安卓1 安卓2 苹果
{ phoneType === 1 ? 8.0.9——8.0.11(最新版本),共3个版本均可激活48小时互动!
8.0.7及以下版本,点击新蓝链会跳转空页面,无法激活!
iOS用户点击新蓝链,系统无法模拟用户回复消息!!!
: phoneType === 3 ? 原来的篮字方式无法激活48小时互动但所有机型通用 :phoneType === 4 ? 安卓用户可以看到,ios看不到 : 只对IOS有效 }
} trigger="click" >
}
 { setRef(ref) }}
                onKeyDown={(e: React.KeyboardEvent) => {
                    if (e.key === 'Enter') {
                        document.execCommand('insertHTML', false, '\n')
                        e.preventDefault()
                    }
                }}
                onKeyUp={(e: React.KeyboardEvent) => {
                    if (e.key === 'Enter') {
                        e.preventDefault()
                    }
                }}
                onPaste={textPaste}
            />
        
0 ? 'flex' : '' }} > <> {isLink && } { isWxLink &&
路径插入用户: { setUserId(Number(e.target.value)) }} value={userId} > 普通用户 趣程用户
×
} {!isLink && !isWxLink && <> { handleLink(1) }}>设置连接 { handleLink(2) }}>设置小程序 { handleLink(3) }}>清空连接 }
}) export default WxTextModal