Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
wisdom-components
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ReactWeb5
wisdom-components
Commits
3649ea48
Commit
3649ea48
authored
Jun 27, 2022
by
周宏民
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: 修改
parent
3437a61e
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
473 additions
and
1038 deletions
+473
-1038
package.json
package.json
+2
-2
index copy.js
packages/base-components/RichText/src/index copy.js
+0
-558
index.js
packages/base-components/RichText/src/index.js
+471
-478
No files found.
package.json
View file @
3649ea48
...
...
@@ -308,13 +308,13 @@
},
{
"path"
:
"packages/base-components/RichText/lib/**/*.js"
,
"limit"
:
"
6
s"
,
"limit"
:
"
2
s"
,
"webpack"
:
false
,
"running"
:
false
},
{
"path"
:
"packages/base-components/RichText/es/**/*.js"
,
"limit"
:
"
6
s"
,
"limit"
:
"
2
s"
,
"webpack"
:
false
,
"running"
:
false
}
...
...
packages/base-components/RichText/src/index copy.js
deleted
100644 → 0
View file @
3437a61e
/*
* @Title:富文本编辑器
* @Author: hongmye
* @Date: 2022-03-01 14:23:55
*/
/**
* 1.引入组件 import RichText from '@/components/RichText';
* 示例:<RichText
content={this.state.content}
personList={this.state.personList}
placeholder={'placeholder属性值'}
onChange={val => {
this.setState({ content: val });
}}
onChangeFile={arr => {
this.setState({ fileList: arr });
}}
fileList={this.state.fileList}
projectId={19}
ref={this.myRichText}
/>
*
* 2.传递方法 onChange 每次更改内容回调
*
* 3.传值接收 可选值 projectId 项目id,根据项目id获取项目参与人员,
* 可选值 personList 人员列表 示例:[{userId:1,userName:'xxx'}]
* 可选值 config 框架wangEditor的配置参数
*
* 4.注意事项 projectId和personList只用传一个,projectId优先级高于personList
* content内容如果不是初始有的,可调用setHtml设置内容
*
* 2022-03-21新增图片预览,附件上传功能
* 新增方法:onChangeFile 每次附件更改回调 若不传则不显示附件上传按钮
* fileList 附件列表 示例:[{name:'xxx.jpg',type:'image/jpg',size:8192,path:'xxxx'}]
* 其中name和path是必传的,type为图片可以预览,其它类型文件直接下载
*
* 2022-04-29 修改@人员列表逻辑
* personList 传任务相关人员列表(如 创建、负责、跟进人),同时传入projectId,personList
* 下拉列表默认显示为任务相关人员,加项目人员(做了去重,任务相关人员在最上面)
* @搜索时,搜索全部人员
*/
import
{
Image
,
message
,
Spin
}
from
'antd'
;
import
classNames
from
'classnames'
;
import
{
forwardRef
,
useEffect
,
useImperativeHandle
,
useRef
,
useState
}
from
'react'
;
// import { request } from '@wisdom-utils/utils';
import
request
from
'umi-request'
;
import
WangEditor
from
'./wangEditor.js'
;
// import { API } from '@/api/service/workflow';
const
API
=
{};
// import { appWork, projectManageService } from '@/api';
import
FileListItem
from
'./fileListItem'
;
import
styles
from
'./index.less'
;
let
editor
=
null
;
let
range
;
const
selection
=
window
.
getSelection
();
let
startOffset
;
let
tempList
=
[];
let
allPeople
=
[];
// 全部人员
let
selectPersonIndex
;
let
selectPerson
=
[];
const
RichText
=
forwardRef
((
props
,
ref
)
=>
{
const
[
loading
,
setLoading
]
=
useState
(
false
);
const
[
zIndex
,
setZIndex
]
=
useState
(
500
);
// const [inputValue, setInputValue] = useState('');
const
[
selectIndex
,
setSelectIndex
]
=
useState
(
null
);
const
[
selectList
,
setSelectList
]
=
useState
([]);
const
[
selectSearchList
,
setSelectSearchList
]
=
useState
([]);
const
[
fileList
,
setFileList
]
=
useState
([]);
const
[
imgVisible
,
setImgVisible
]
=
useState
(
false
);
const
[
imgPreviewSrc
,
setImgPreviewSrc
]
=
useState
(
''
);
const
richTextRef
=
useRef
();
const
selectBoxRef
=
useRef
();
const
fileInputRef
=
useRef
();
const
getData
=
async
()
=>
{
// const { data } = await projectManageService.GetWorkHourUserList({ projectId: props.projectId });
// setSelectList(data);
};
// 获取全部人员信息
const
getAllPeople
=
async
()
=>
{
// const res = await appWork.GetAllPersonnels();
// allPeople = res?.data?.data || [];
};
// 图片上传
const
uploadImg
=
(
file
)
=>
{
const
formData
=
new
FormData
();
formData
.
append
(
'file'
,
file
);
setLoading
(
true
);
request
({
url
:
API
.
POST_UPLOADERFILES
,
method
:
'POST'
,
data
:
formData
,
})
.
then
((
res
)
=>
{
if
(
!
res
.
data
)
return
;
const
img
=
res
.
data
.
replace
(
/
[\\
\/
=
]
/g
,
'/'
);
const
imgHtml
=
`
<img contenteditable="false" style="display: block;" width="50%" src="
${
API
.
GET_DOWNLOADFILES
}
?filePath=
${
img
}
" >
`
;
// 已弃用(会出现插入图片后,光标消失的问题)
// const imgHtml = `
// <div class="RichText-image" contenteditable="false">
// <img class="RichText-image-img" src="${API.GET_DOWNLOADFILES}?filePath=${img}">
// <div class="RichText-image-mask" type="preview" >
// <div class="RichText-image-mask-info" type="preview">
// <span role="img" aria-label="eye" class="anticon anticon-eye">
// <svg viewBox="64 64 896 896" focusable="false" data-icon="eye" width="1em" height="1em" fill="currentColor" aria-hidden="true">
// <path d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"></path>
// </svg>
// </span>
// 预览
// </div>
// </div>
// </div>`;
editor
.
cmd
.
do
(
'insertHTML'
,
imgHtml
);
setLoading
(
false
);
})
.
catch
((
err
)
=>
{
setLoading
(
false
);
});
};
const
init
=
()
=>
{
const
{
BtnMenu
}
=
WangEditor
;
editor
=
new
WangEditor
(
'#RichTextToolbar'
,
'#RichTextContainer'
);
// 自定义菜单
const
menuKey
=
'fileMenuKey'
;
if
(
props
.
onChangeFile
)
{
class
InsertABCMenu
extends
BtnMenu
{
constructor
(
editor
)
{
const
$elem
=
WangEditor
.
$
(
`<div class="w-e-menu">
<i class="w-e-icon-link">
</i>
</div>`
,
);
super
(
$elem
,
editor
);
}
// 菜单点击事件
clickHandler
()
{
// 触发选择文件
fileInputRef
.
current
.
click
();
}
// 菜单激活状态
tryChangeActive
()
{
// this.active(); // 菜单激活
}
}
editor
.
menus
.
extend
(
menuKey
,
InsertABCMenu
);
}
editor
.
config
=
Object
.
assign
(
{},
editor
.
config
,
{
placeholder
:
props
.
placeholder
??
''
,
focus
:
false
,
pasteFilterStyle
:
true
,
// 忽略粘贴样式
pasteIgnoreImg
:
true
,
// 忽略粘贴的图片
styleWithCSS
:
false
,
zIndex
:
500
,
menus
:
[
'bold'
,
'fontSize'
,
'italic'
,
'underline'
,
'strikeThrough'
,
'foreColor'
,
'backColor'
,
'list'
,
'justify'
,
'table'
,
menuKey
,
'undo'
,
'redo'
,
'image'
,
],
},
props
.
config
||
{},
);
setZIndex
(
Number
(
editor
.
config
.
zIndex
));
// 内容变更
editor
.
config
.
onchange
=
(
newHtml
,
e
)
=>
{
props
.
onChange
(
newHtml
);
};
// 粘贴前置处理
editor
.
config
.
pasteTextHandle
=
(
pasteStr
)
=>
pasteStr
;
editor
.
config
.
onblur
=
(
newHtml
)
=>
{
selectBoxRef
.
current
.
style
.
display
=
'none'
;
};
// 点击事件
editor
.
txt
.
eventHooks
.
clickEvents
.
push
((
e
)
=>
{
// 图片预览
// 已弃用
if
(
e
.
target
.
getAttribute
(
'type'
)
===
'preview'
)
{
const
imgSrc
=
e
.
target
?.
parentNode
?.
parentNode
?.
getElementsByTagName
(
'img'
)?.[
0
]
?.
getAttribute
(
'src'
);
setImgPreviewSrc
(
imgSrc
);
setImgVisible
(
true
);
}
// 关闭选人的下拉框
selectBoxRef
.
current
.
style
.
display
=
'none'
;
});
editor
.
txt
.
eventHooks
.
onPreviewEvents
.
push
((
link
)
=>
{
// 图片预览
if
(
link
)
{
setImgPreviewSrc
(
link
);
setImgVisible
(
true
);
}
});
editor
.
txt
.
eventHooks
.
imgClickEvents
.
push
((
e
)
=>
{
// 图片预览
// 已弃用
// let img = e?.selector?.getAttribute('src') || '';
// if (img) {
// setImgPreviewSrc(img);
// setImgVisible(true);
// }
});
// 粘贴图片上传
editor
.
txt
.
eventHooks
.
pasteEvents
.
push
((
e
)
=>
{
const
file
=
e
?.
clipboardData
?.
items
[
0
]?.
getAsFile
()
||
null
;
if
(
!
file
)
return
;
uploadImg
(
file
);
});
editor
.
create
();
editor
.
txt
.
html
(
props
.
content
||
''
);
richTextRef
.
current
.
onkeydown
=
keyDownEvent
;
richTextRef
.
current
.
addEventListener
(
'input'
,
(
e
)
=>
{
if
(
range
)
{
// 判断节点是否在选区及光标是否在@后面
const
type
=
selection
.
containsNode
(
selection
.
getRangeAt
(
0
).
commonAncestorContainer
,
false
);
if
(
!
type
||
startOffset
>
selection
.
focusOffset
)
{
closeList
();
return
;
}
range
.
setEnd
(
selection
.
getRangeAt
(
0
).
commonAncestorContainer
,
selection
.
focusOffset
);
// console.log('🚀 ~ range', range.toString(), tempList, selectList);
const
str
=
range
.
toString
()
||
''
;
moveListBox
();
handleChange
(
str
,
tempList
);
}
if
(
e
.
data
!==
'@'
)
return
;
if
(
range
)
{
closeList
();
}
range
=
document
.
createRange
();
startOffset
=
selection
.
focusOffset
;
range
.
setStart
(
selection
.
getRangeAt
(
0
).
commonAncestorContainer
,
selection
.
focusOffset
);
selection
.
addRange
(
range
);
moveListBox
();
// 清空搜索
handleChange
(
''
,
tempList
);
});
};
// 跟据光标位置移动下拉框
const
moveListBox
=
()
=>
{
// 获取光标位置
const
cursor
=
window
?.
getSelection
()?.
getRangeAt
(
0
)?.
getBoundingClientRect
()
||
null
;
const
containerRect
=
document
.
querySelector
(
'#RichText'
).
getBoundingClientRect
();
selectBoxRef
.
current
.
style
.
display
=
'block'
;
selectBoxRef
.
current
.
style
.
left
=
`
${
parseInt
(
cursor
.
x
-
containerRect
.
x
,
10
)
+
5
}
px`
;
selectBoxRef
.
current
.
style
.
top
=
`
${
parseInt
(
cursor
.
y
-
containerRect
.
y
,
10
)
+
25
}
px`
;
};
// 键盘事件
const
keyDownEvent
=
(
evet
)
=>
{
// 上下方向键
if
(
evet
.
key
===
'ArrowDown'
||
evet
.
key
===
'ArrowUp'
)
{
evet
.
preventDefault
();
if
(
selectBoxRef
.
current
?.
style
?.
display
===
'block'
)
{
const
max
=
selectBoxRef
.
current
.
querySelectorAll
(
'.selectItem'
)?.
length
||
1000
;
let
val
=
selectPersonIndex
;
if
(
evet
.
key
===
'ArrowDown'
)
{
if
(
!
val
&&
val
!=
0
)
{
val
=
0
;
}
else
{
val
+=
1
;
}
}
if
(
evet
.
key
===
'ArrowUp'
)
val
-=
1
;
if
(
isNaN
(
val
)
||
!
val
||
val
<
0
)
val
=
0
;
if
(
val
>
max
-
1
)
val
=
max
-
1
;
selectPersonIndex
=
val
;
setSelectIndex
(
selectPersonIndex
);
}
}
if
(
evet
.
key
===
'Enter'
)
{
// 解决无法回车换行的bug
if
(
selectBoxRef
.
current
.
style
.
display
===
'block'
&&
selectSearchList
.
length
)
{
evet
.
preventDefault
();
if
(
selectPerson
[
selectPersonIndex
])
{
onSelect
(
selectPerson
[
selectPersonIndex
]);
}
return
false
;
}
}
};
useEffect
(()
=>
{
richTextRef
.
current
&&
richTextRef
.
current
.
removeEventListener
(
'input'
,
(
e
)
=>
{});
init
();
getAllPeople
();
return
()
=>
{
richTextRef
.
current
&&
richTextRef
.
current
.
removeEventListener
(
'input'
,
(
e
)
=>
{});
editor
&&
editor
.
destroy
();
editor
=
null
;
};
},
[]);
useEffect
(()
=>
{
selectPersonIndex
=
null
;
setSelectIndex
(
null
);
selectPerson
=
selectSearchList
||
[];
},
[
selectSearchList
]);
useEffect
(()
=>
{
if
(
props
.
projectId
)
getData
();
},
[
props
.
projectId
]);
useEffect
(()
=>
{
const
keys
=
[];
const
arr
=
[];
if
(
props
.
personList
)
{
props
.
personList
.
forEach
((
i
)
=>
{
i
.
userId
=
Number
(
i
.
userId
);
if
(
!
keys
.
includes
(
i
.
userId
))
{
keys
.
push
(
i
.
userId
);
arr
.
push
(
i
);
}
});
}
if
(
selectList
)
{
selectList
.
forEach
((
i
)
=>
{
i
.
userId
=
Number
(
i
.
userId
);
if
(
!
keys
.
includes
(
i
.
userId
))
{
arr
.
push
(
i
);
keys
.
push
(
i
.
userId
);
}
});
}
tempList
=
arr
;
setSelectSearchList
(
arr
);
},
[
selectList
,
props
.
personList
]);
// useEffect(() => {
// if (!props.projectId) {
// setSelectList(props.personList || []);
// }
// }, [props.personList]);
useEffect
(()
=>
{
setFileList
(
props
.
fileList
);
},
[
props
.
fileList
]);
const
getHtml
=
(
val
)
=>
editor
.
txt
.
html
();
// 获取文本,不含标签
const
getText
=
(
val
)
=>
editor
.
txt
.
text
();
// 清除
const
onClear
=
()
=>
{
editor
.
txt
.
clear
();
};
// 设置内容
const
setHtml
=
(
val
)
=>
{
editor
.
txt
.
html
(
val
||
''
);
};
// 关闭人员下拉选框
const
closeList
=
()
=>
{
selectPersonIndex
=
null
;
setSelectIndex
(
null
);
selectBoxRef
.
current
.
style
.
display
=
'none'
;
if
(
range
)
{
selection
.
removeRange
(
range
);
range
=
null
;
}
};
// @某人
const
onSelect
=
(
item
)
=>
{
if
(
range
)
{
range
.
deleteContents
();
// 删除前一个@符号
editor
.
cmd
.
do
(
'delete'
);
const
_html
=
`<span><span data-userId="
${
item
.
userId
}
" data-type="person" >@
${
item
.
userName
}
</span><span> </span></span>`
;
editor
.
cmd
.
do
(
'insertElem'
,
_html
);
closeList
();
selection
.
collapseToEnd
();
}
};
// const onChange = val => {
// setInputValue(val.currentTarget.value);
// handleChange(val.currentTarget.value);
// };
let
timer
=
null
;
const
filterList
=
(
val
,
list
)
=>
{
if
(
!
val
)
{
if
(
list
.
length
===
0
)
{
selectBoxRef
.
current
.
style
.
display
=
'none'
;
}
setSelectSearchList
(
list
);
}
else
{
const
arr
=
getArrayByName
(
val
,
allPeople
);
if
(
arr
.
length
===
0
)
{
selectBoxRef
.
current
.
style
.
display
=
'none'
;
}
setSelectSearchList
(
arr
);
}
};
const
handleChange
=
(
val
,
list
)
=>
{
if
(
timer
)
{
clearTimeout
(
timer
);
}
timer
=
setTimeout
(()
=>
{
timer
=
null
;
filterList
(
val
,
list
);
},
200
);
// filterList(val, list);
};
/**
* 根据字符串模糊搜索返回符合条件的数据
* name 搜索字符串
* array 检索json数组
*/
const
getArrayByName
=
(
name
,
array
)
=>
{
const
result
=
[];
array
.
forEach
((
i
)
=>
{
if
(
i
.
name
.
indexOf
(
name
)
!=
-
1
)
result
.
push
({
userName
:
i
.
name
,
userId
:
i
.
id
,
port
:
i
.
port
});
});
return
result
;
};
const
addFile
=
(
e
)
=>
{
if
(
e
.
target
)
{
const
file
=
e
.
target
.
files
[
0
];
const
formData
=
new
FormData
();
formData
.
append
(
'file'
,
file
);
setLoading
(
true
);
request
({
url
:
API
.
POST_UPLOADERFILES
,
method
:
'POST'
,
data
:
formData
,
})
.
then
((
res
)
=>
{
if
(
res
.
data
)
{
const
arr
=
[...
fileList
];
const
url
=
res
.
data
.
replace
(
/
[\\
\/
=
]
/g
,
'/'
);
arr
.
unshift
({
name
:
file
.
name
,
type
:
file
.
type
?
file
.
type
.
toLowerCase
()
:
''
,
size
:
file
.
size
,
path
:
`
${
API
.
GET_DOWNLOADFILES
}
?filePath=
${
url
}
`
,
});
// setFileList(arr);
props
.
onChangeFile
(
arr
);
setLoading
(
false
);
}
else
{
message
.
error
(
res
.
msg
);
setLoading
(
false
);
}
})
.
catch
((
err
)
=>
{
setLoading
(
false
);
});
}
};
const
onDelFile
=
(
item
)
=>
{
const
arr
=
[];
fileList
.
forEach
((
i
)
=>
{
if
(
i
.
path
!==
item
.
path
)
{
arr
.
push
(
i
);
}
});
// setFileList(arr);
props
.
onChangeFile
(
arr
);
};
useImperativeHandle
(
ref
,
()
=>
({
setHtml
,
onClear
,
getHtml
,
getText
,
}));
return
(
<
div
className
=
{
styles
.
RichText
}
id
=
"RichText"
>
{
loading
?
(
<
div
className
=
{
styles
.
loadingWrap
}
style
=
{{
zIndex
:
zIndex
+
20
}}
>
<
Spin
spinning
=
{
loading
}
/
>
<
/div
>
)
:
null
}
<
div
id
=
"RichTextToolbar"
className
=
{
styles
.
RichTextToolbar
}
/
>
<
div
ref
=
{
richTextRef
}
id
=
"RichTextContainer"
className
=
{
styles
.
RichTextContainer
}
/
>
<
div
className
=
{
styles
.
RichTextFileList
}
>
<
FileListItem
list
=
{
fileList
}
onDel
=
{(
val
)
=>
{
onDelFile
(
val
);
}}
type
=
"edit"
onPreview
=
{(
val
)
=>
{
if
(
!
val
)
return
;
setImgPreviewSrc
(
val
.
path
);
setImgVisible
(
true
);
}}
/
>
<
/div
>
<
div
ref
=
{
selectBoxRef
}
className
=
{
styles
.
selectBox
}
style
=
{{
maxWidth
:
'300px'
,
minWidth
:
'150px'
,
zIndex
:
zIndex
+
10
}}
>
{
selectSearchList
.
length
?
(
<
div
className
=
{
styles
.
selectList
}
>
{
selectSearchList
.
map
((
item
,
index
)
=>
(
<
div
key
=
{
item
.
userId
}
onClick
=
{()
=>
{
onSelect
(
item
);
}}
className
=
{
classNames
(
'selectItem'
,
styles
.
selectItem
,
selectIndex
===
index
?
styles
.
selectActiveItem
:
''
,
)}
>
{
item
.
userName
}
<
/div
>
))}
<
/div
>
)
:
null
}
<
/div
>
<
input
style
=
{{
display
:
'none'
}}
type
=
"file"
ref
=
{
fileInputRef
}
onChange
=
{(
e
)
=>
{
addFile
(
e
);
}}
name
=
"file"
/>
<
Image
width
=
{
200
}
style
=
{{
display
:
'none'
}}
src
=
{
imgPreviewSrc
}
preview
=
{{
visible
:
imgVisible
,
src
:
imgPreviewSrc
,
onVisibleChange
:
(
value
)
=>
{
setImgVisible
(
value
);
if
(
!
value
)
setImgPreviewSrc
(
''
);
},
}}
/
>
<
/div
>
);
});
export
default
RichText
;
packages/base-components/RichText/src/index.js
View file @
3649ea48
...
...
@@ -41,18 +41,17 @@
* 下拉列表默认显示为任务相关人员,加项目人员(做了去重,任务相关人员在最上面)
* @搜索时,搜索全部人员
*/
import
React
,
{
useState
,
useEffect
,
useRef
,
useImperativeHandle
,
forwardRef
}
from
'react'
;
import
{
Spin
,
Input
,
Image
,
message
}
from
'antd'
;
import
{
Image
,
message
,
Spin
}
from
'antd'
;
import
classNames
from
'classnames'
;
import
{
forwardRef
,
useEffect
,
useImperativeHandle
,
useRef
,
useState
}
from
'react'
;
// import { request } from '@wisdom-utils/utils';
import
request
from
'umi-request'
;
import
WangEditor
from
'./wangEditor.js'
;
// import { API } from '@/api/service/workflow';
const
API
=
{}
const
API
=
{}
;
// import { appWork, projectManageService } from '@/api';
import
FileListItem
from
'./fileListItem'
;
import
styles
from
'./index.less'
;
let
editor
=
null
;
let
range
;
const
selection
=
window
.
getSelection
();
...
...
@@ -62,502 +61,496 @@ let allPeople = []; // 全部人员
let
selectPersonIndex
;
let
selectPerson
=
[];
const
RichText
=
forwardRef
((
props
,
ref
)
=>
{
const
[
loading
,
setLoading
]
=
useState
(
false
);
const
[
zIndex
,
setZIndex
]
=
useState
(
500
);
// const [inputValue, setInputValue] = useState('');
const
[
selectIndex
,
setSelectIndex
]
=
useState
(
null
);
const
[
selectList
,
setSelectList
]
=
useState
([]);
const
[
selectSearchList
,
setSelectSearchList
]
=
useState
([]);
const
[
fileList
,
setFileList
]
=
useState
([]);
const
[
imgVisible
,
setImgVisible
]
=
useState
(
false
);
const
[
imgPreviewSrc
,
setImgPreviewSrc
]
=
useState
(
''
);
const
richTextRef
=
useRef
();
const
selectBoxRef
=
useRef
();
const
fileInputRef
=
useRef
();
const
getData
=
async
()
=>
{
// const { data } = await projectManageService.GetWorkHourUserList({ projectId: props.projectId });
// setSelectList(data);
};
// 获取全部人员信息
const
getAllPeople
=
async
()
=>
{
// const res = await appWork.GetAllPersonnels();
// allPeople = res?.data?.data || [];
};
// 图片上传
const
uploadImg
=
file
=>
{
const
formData
=
new
FormData
();
formData
.
append
(
'file'
,
file
);
setLoading
(
true
);
request
({
url
:
API
.
POST_UPLOADERFILES
,
method
:
'POST'
,
data
:
formData
,
})
.
then
(
res
=>
{
if
(
!
res
.
data
)
return
;
const
img
=
res
.
data
.
replace
(
/
[\\
\/
=
]
/g
,
'/'
);
const
imgHtml
=
`
<img contenteditable="false" style="display: block;" width="50%" src="
${
API
.
GET_DOWNLOADFILES
}
?filePath=
${
img
}
" >
const
[
loading
,
setLoading
]
=
useState
(
false
);
const
[
zIndex
,
setZIndex
]
=
useState
(
500
);
// const [inputValue, setInputValue] = useState('');
const
[
selectIndex
,
setSelectIndex
]
=
useState
(
null
);
const
[
selectList
,
setSelectList
]
=
useState
([]);
const
[
selectSearchList
,
setSelectSearchList
]
=
useState
([]);
const
[
fileList
,
setFileList
]
=
useState
([]);
const
[
imgVisible
,
setImgVisible
]
=
useState
(
false
);
const
[
imgPreviewSrc
,
setImgPreviewSrc
]
=
useState
(
''
);
const
richTextRef
=
useRef
();
const
selectBoxRef
=
useRef
();
const
fileInputRef
=
useRef
();
const
getData
=
async
()
=>
{
// const { data } = await projectManageService.GetWorkHourUserList({ projectId: props.projectId });
// setSelectList(data);
};
// 获取全部人员信息
const
getAllPeople
=
async
()
=>
{
// const res = await appWork.GetAllPersonnels();
// allPeople = res?.data?.data || [];
};
// 图片上传
const
uploadImg
=
(
file
)
=>
{
const
formData
=
new
FormData
();
formData
.
append
(
'file'
,
file
);
setLoading
(
true
);
request
({
url
:
API
.
POST_UPLOADERFILES
,
method
:
'POST'
,
data
:
formData
,
})
.
then
((
res
)
=>
{
if
(
!
res
.
data
)
return
;
const
img
=
res
.
data
.
replace
(
/
[\\
\/
=
]
/g
,
'/'
);
const
imgHtml
=
`
<img contenteditable="false" style="display: block;" width="50%" src="
${
API
.
GET_DOWNLOADFILES
}
?filePath=
${
img
}
" >
`
;
// 已弃用(会出现插入图片后,光标消失的问题)
// const imgHtml = `
// <div class="RichText-image" contenteditable="false">
// <img class="RichText-image-img" src="${API.GET_DOWNLOADFILES}?filePath=${img}">
// <div class="RichText-image-mask" type="preview" >
// <div class="RichText-image-mask-info" type="preview">
// <span role="img" aria-label="eye" class="anticon anticon-eye">
// <svg viewBox="64 64 896 896" focusable="false" data-icon="eye" width="1em" height="1em" fill="currentColor" aria-hidden="true">
// <path d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"></path>
// </svg>
// </span>
// 预览
// </div>
// </div>
// </div>`;
editor
.
cmd
.
do
(
'insertHTML'
,
imgHtml
);
setLoading
(
false
);
})
.
catch
(
err
=>
{
setLoading
(
false
);
});
};
const
init
=
()
=>
{
const
{
BtnMenu
}
=
WangEditor
;
editor
=
new
WangEditor
(
'#RichTextToolbar'
,
'#RichTextContainer'
);
// 自定义菜单
const
menuKey
=
'fileMenuKey'
;
if
(
props
.
onChangeFile
)
{
class
InsertABCMenu
extends
BtnMenu
{
constructor
(
editor
)
{
const
$elem
=
WangEditor
.
$
(
`<div class="w-e-menu">
// 已弃用(会出现插入图片后,光标消失的问题)
// const imgHtml = `
// <div class="RichText-image" contenteditable="false">
// <img class="RichText-image-img" src="${API.GET_DOWNLOADFILES}?filePath=${img}">
// <div class="RichText-image-mask" type="preview" >
// <div class="RichText-image-mask-info" type="preview">
// <span role="img" aria-label="eye" class="anticon anticon-eye">
// <svg viewBox="64 64 896 896" focusable="false" data-icon="eye" width="1em" height="1em" fill="currentColor" aria-hidden="true">
// <path d="M942.2 486.2C847.4 286.5 704.1 186 512 186c-192.2 0-335.4 100.5-430.2 300.3a60.3 60.3 0 000 51.5C176.6 737.5 319.9 838 512 838c192.2 0 335.4-100.5 430.2-300.3 7.7-16.2 7.7-35 0-51.5zM512 766c-161.3 0-279.4-81.8-362.7-254C232.6 339.8 350.7 258 512 258c161.3 0 279.4 81.8 362.7 254C791.5 684.2 673.4 766 512 766zm-4-430c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm0 288c-61.9 0-112-50.1-112-112s50.1-112 112-112 112 50.1 112 112-50.1 112-112 112z"></path>
// </svg>
// </span>
// 预览
// </div>
// </div>
// </div>`;
editor
.
cmd
.
do
(
'insertHTML'
,
imgHtml
);
setLoading
(
false
);
})
.
catch
((
err
)
=>
{
setLoading
(
false
);
});
};
const
init
=
()
=>
{
const
{
BtnMenu
}
=
WangEditor
;
editor
=
new
WangEditor
(
'#RichTextToolbar'
,
'#RichTextContainer'
);
// 自定义菜单
const
menuKey
=
'fileMenuKey'
;
if
(
props
.
onChangeFile
)
{
class
InsertABCMenu
extends
BtnMenu
{
constructor
(
editor
)
{
const
$elem
=
WangEditor
.
$
(
`<div class="w-e-menu">
<i class="w-e-icon-link">
</i>
</div>`
,
);
super
(
$elem
,
editor
);
}
// 菜单点击事件
clickHandler
()
{
// 触发选择文件
fileInputRef
.
current
.
click
();
}
// 菜单激活状态
tryChangeActive
()
{
// this.active(); // 菜单激活
}
}
editor
.
menus
.
extend
(
menuKey
,
InsertABCMenu
);
);
super
(
$elem
,
editor
);
}
editor
.
config
=
Object
.
assign
(
{},
editor
.
config
,
{
placeholder
:
props
.
placeholder
??
''
,
focus
:
false
,
pasteFilterStyle
:
true
,
// 忽略粘贴样式
pasteIgnoreImg
:
true
,
// 忽略粘贴的图片
styleWithCSS
:
false
,
zIndex
:
500
,
menus
:
[
'bold'
,
'fontSize'
,
'italic'
,
'underline'
,
'strikeThrough'
,
'foreColor'
,
'backColor'
,
'list'
,
'justify'
,
'table'
,
menuKey
,
'undo'
,
'redo'
,
'image'
,
],
},
props
.
config
||
{},
);
setZIndex
(
Number
(
editor
.
config
.
zIndex
));
// 内容变更
editor
.
config
.
onchange
=
(
newHtml
,
e
)
=>
{
props
.
onChange
(
newHtml
);
};
// 粘贴前置处理
editor
.
config
.
pasteTextHandle
=
pasteStr
=>
pasteStr
;
editor
.
config
.
onblur
=
newHtml
=>
{
selectBoxRef
.
current
.
style
.
display
=
'none'
;
};
// 点击事件
editor
.
txt
.
eventHooks
.
clickEvents
.
push
(
e
=>
{
// 图片预览
// 已弃用
if
(
e
.
target
.
getAttribute
(
'type'
)
===
'preview'
)
{
const
imgSrc
=
e
.
target
?.
parentNode
?.
parentNode
?.
getElementsByTagName
(
'img'
)?.[
0
]?.
getAttribute
(
'src'
);
setImgPreviewSrc
(
imgSrc
);
setImgVisible
(
true
);
}
// 关闭选人的下拉框
selectBoxRef
.
current
.
style
.
display
=
'none'
;
});
editor
.
txt
.
eventHooks
.
onPreviewEvents
.
push
(
link
=>
{
// 图片预览
if
(
link
)
{
setImgPreviewSrc
(
link
);
setImgVisible
(
true
);
}
});
// 菜单点击事件
clickHandler
()
{
// 触发选择文件
fileInputRef
.
current
.
click
();
}
editor
.
txt
.
eventHooks
.
imgClickEvents
.
push
(
e
=>
{
// 图片预览
// 已弃用
// let img = e?.selector?.getAttribute('src') || '';
// if (img) {
// setImgPreviewSrc(img);
// setImgVisible(true);
// }
});
// 粘贴图片上传
editor
.
txt
.
eventHooks
.
pasteEvents
.
push
(
e
=>
{
const
file
=
e
?.
clipboardData
?.
items
[
0
]?.
getAsFile
()
||
null
;
if
(
!
file
)
return
;
uploadImg
(
file
);
});
// 菜单激活状态
tryChangeActive
()
{
// this.active(); // 菜单激活
}
}
editor
.
menus
.
extend
(
menuKey
,
InsertABCMenu
);
}
editor
.
create
();
editor
.
txt
.
html
(
props
.
content
||
''
);
richTextRef
.
current
.
onkeydown
=
keyDownEvent
;
richTextRef
.
current
.
addEventListener
(
'input'
,
e
=>
{
if
(
range
)
{
// 判断节点是否在选区及光标是否在@后面
const
type
=
selection
.
containsNode
(
selection
.
getRangeAt
(
0
).
commonAncestorContainer
,
false
);
if
(
!
type
||
startOffset
>
selection
.
focusOffset
)
{
closeList
();
return
;
}
range
.
setEnd
(
selection
.
getRangeAt
(
0
).
commonAncestorContainer
,
selection
.
focusOffset
);
// console.log('🚀 ~ range', range.toString(), tempList, selectList);
const
str
=
range
.
toString
()
||
''
;
moveListBox
();
handleChange
(
str
,
tempList
);
}
if
(
e
.
data
!==
'@'
)
return
;
if
(
range
)
{
closeList
();
}
range
=
document
.
createRange
();
startOffset
=
selection
.
focusOffset
;
range
.
setStart
(
selection
.
getRangeAt
(
0
).
commonAncestorContainer
,
selection
.
focusOffset
);
selection
.
addRange
(
range
);
editor
.
config
=
Object
.
assign
(
{},
editor
.
config
,
{
placeholder
:
props
.
placeholder
??
''
,
focus
:
false
,
pasteFilterStyle
:
true
,
// 忽略粘贴样式
pasteIgnoreImg
:
true
,
// 忽略粘贴的图片
styleWithCSS
:
false
,
zIndex
:
500
,
menus
:
[
'bold'
,
'fontSize'
,
'italic'
,
'underline'
,
'strikeThrough'
,
'foreColor'
,
'backColor'
,
'list'
,
'justify'
,
'table'
,
menuKey
,
'undo'
,
'redo'
,
'image'
,
],
},
props
.
config
||
{},
);
moveListBox
(
);
// 清空搜索
handleChange
(
''
,
tempList
);
}
);
setZIndex
(
Number
(
editor
.
config
.
zIndex
)
);
// 内容变更
editor
.
config
.
onchange
=
(
newHtml
,
e
)
=>
{
props
.
onChange
(
newHtml
);
};
// 跟据光标位置移动下拉框
const
moveListBox
=
()
=>
{
// 获取光标位置
const
cursor
=
window
?.
getSelection
()
?.
getRangeAt
(
0
)
?.
getBoundingClientRect
()
||
null
;
const
containerRect
=
document
.
querySelector
(
'#RichText'
).
getBoundingClientRect
();
selectBoxRef
.
current
.
style
.
display
=
'block'
;
selectBoxRef
.
current
.
style
.
left
=
`
${
parseInt
(
cursor
.
x
-
containerRect
.
x
,
10
)
+
5
}
px`
;
selectBoxRef
.
current
.
style
.
top
=
`
${
parseInt
(
cursor
.
y
-
containerRect
.
y
,
10
)
+
25
}
px`
;
// 粘贴前置处理
editor
.
config
.
pasteTextHandle
=
(
pasteStr
)
=>
pasteStr
;
editor
.
config
.
onblur
=
(
newHtml
)
=>
{
selectBoxRef
.
current
.
style
.
display
=
'none'
;
};
// 键盘事件
const
keyDownEvent
=
evet
=>
{
// 上下方向键
if
(
evet
.
key
===
'ArrowDown'
||
evet
.
key
===
'ArrowUp'
)
{
evet
.
preventDefault
();
if
(
selectBoxRef
.
current
?.
style
?.
display
===
'block'
)
{
const
max
=
selectBoxRef
.
current
.
querySelectorAll
(
'.selectItem'
)?.
length
||
1000
;
let
val
=
selectPersonIndex
;
if
(
evet
.
key
===
'ArrowDown'
)
{
if
(
!
val
&&
val
!=
0
)
{
val
=
0
;
}
else
{
val
+=
1
;
}
}
if
(
evet
.
key
===
'ArrowUp'
)
val
-=
1
;
if
(
isNaN
(
val
)
||
!
val
||
val
<
0
)
val
=
0
;
if
(
val
>
max
-
1
)
val
=
max
-
1
;
selectPersonIndex
=
val
;
setSelectIndex
(
selectPersonIndex
);
}
}
if
(
evet
.
key
===
'Enter'
)
{
// 解决无法回车换行的bug
if
(
selectBoxRef
.
current
.
style
.
display
===
'block'
&&
selectSearchList
.
length
)
{
evet
.
preventDefault
();
if
(
selectPerson
[
selectPersonIndex
])
{
onSelect
(
selectPerson
[
selectPersonIndex
]);
}
return
false
;
}
// 点击事件
editor
.
txt
.
eventHooks
.
clickEvents
.
push
((
e
)
=>
{
// 图片预览
// 已弃用
if
(
e
.
target
.
getAttribute
(
'type'
)
===
'preview'
)
{
const
imgSrc
=
e
.
target
?.
parentNode
?.
parentNode
?.
getElementsByTagName
(
'img'
)?.[
0
]
?.
getAttribute
(
'src'
);
setImgPreviewSrc
(
imgSrc
);
setImgVisible
(
true
);
}
// 关闭选人的下拉框
selectBoxRef
.
current
.
style
.
display
=
'none'
;
});
}
};
editor
.
txt
.
eventHooks
.
onPreviewEvents
.
push
((
link
)
=>
{
// 图片预览
if
(
link
)
{
setImgPreviewSrc
(
link
);
setImgVisible
(
true
);
}
});
useEffect
(()
=>
{
richTextRef
.
current
&&
richTextRef
.
current
.
removeEventListener
(
'input'
,
e
=>
{
});
init
();
getAllPeople
();
return
()
=>
{
richTextRef
.
current
&&
richTextRef
.
current
.
removeEventListener
(
'input'
,
e
=>
{
});
editor
.
txt
.
eventHooks
.
imgClickEvents
.
push
((
e
)
=>
{
// 图片预览
// 已弃用
// let img = e?.selector?.getAttribute('src') || '';
// if (img) {
// setImgPreviewSrc(img);
// setImgVisible(true);
// }
});
// 粘贴图片上传
editor
.
txt
.
eventHooks
.
pasteEvents
.
push
((
e
)
=>
{
const
file
=
e
?.
clipboardData
?.
items
[
0
]?.
getAsFile
()
||
null
;
if
(
!
file
)
return
;
uploadImg
(
file
);
});
editor
&&
editor
.
destroy
();
editor
=
null
;
};
},
[]);
useEffect
(()
=>
{
selectPersonIndex
=
null
;
setSelectIndex
(
null
);
selectPerson
=
selectSearchList
||
[];
},
[
selectSearchList
]);
useEffect
(()
=>
{
if
(
props
.
projectId
)
getData
();
},
[
props
.
projectId
]);
useEffect
(()
=>
{
const
keys
=
[];
const
arr
=
[];
if
(
props
.
personList
)
{
props
.
personList
.
forEach
(
i
=>
{
i
.
userId
=
Number
(
i
.
userId
);
if
(
!
keys
.
includes
(
i
.
userId
))
{
keys
.
push
(
i
.
userId
);
arr
.
push
(
i
);
}
});
}
if
(
selectList
)
{
selectList
.
forEach
(
i
=>
{
i
.
userId
=
Number
(
i
.
userId
);
if
(
!
keys
.
includes
(
i
.
userId
))
{
arr
.
push
(
i
);
keys
.
push
(
i
.
userId
);
}
});
editor
.
create
();
editor
.
txt
.
html
(
props
.
content
||
''
);
richTextRef
.
current
.
onkeydown
=
keyDownEvent
;
richTextRef
.
current
.
addEventListener
(
'input'
,
(
e
)
=>
{
if
(
range
)
{
// 判断节点是否在选区及光标是否在@后面
const
type
=
selection
.
containsNode
(
selection
.
getRangeAt
(
0
).
commonAncestorContainer
,
false
);
if
(
!
type
||
startOffset
>
selection
.
focusOffset
)
{
closeList
();
return
;
}
range
.
setEnd
(
selection
.
getRangeAt
(
0
).
commonAncestorContainer
,
selection
.
focusOffset
);
// console.log('🚀 ~ range', range.toString(), tempList, selectList);
const
str
=
range
.
toString
()
||
''
;
moveListBox
();
handleChange
(
str
,
tempList
);
}
if
(
e
.
data
!==
'@'
)
return
;
if
(
range
)
{
closeList
();
}
range
=
document
.
createRange
();
startOffset
=
selection
.
focusOffset
;
range
.
setStart
(
selection
.
getRangeAt
(
0
).
commonAncestorContainer
,
selection
.
focusOffset
);
selection
.
addRange
(
range
);
tempList
=
arr
;
setSelectSearchList
(
arr
);
},
[
selectList
,
props
.
personList
]);
// useEffect(() => {
// if (!props.projectId) {
// setSelectList(props.personList || []);
// }
// }, [props.personList]);
useEffect
(()
=>
{
setFileList
(
props
.
fileList
);
},
[
props
.
fileList
]);
const
getHtml
=
val
=>
editor
.
txt
.
html
();
// 获取文本,不含标签
const
getText
=
val
=>
editor
.
txt
.
text
();
// 清除
const
onClear
=
()
=>
{
editor
.
txt
.
clear
();
};
// 设置内容
const
setHtml
=
val
=>
{
editor
.
txt
.
html
(
val
||
''
);
};
// 关闭人员下拉选框
const
closeList
=
()
=>
{
selectPersonIndex
=
null
;
setSelectIndex
(
null
);
selectBoxRef
.
current
.
style
.
display
=
'none'
;
if
(
range
)
{
selection
.
removeRange
(
range
);
range
=
null
;
}
};
// @某人
const
onSelect
=
item
=>
{
if
(
range
)
{
range
.
deleteContents
();
// 删除前一个@符号
editor
.
cmd
.
do
(
'delete'
);
const
_html
=
`<span><span data-userId="
${
item
.
userId
}
" data-type="person" >@
${
item
.
userName
}
</span><span> </span></span>`
;
editor
.
cmd
.
do
(
'insertElem'
,
_html
);
closeList
();
selection
.
collapseToEnd
();
moveListBox
();
// 清空搜索
handleChange
(
''
,
tempList
);
});
};
// 跟据光标位置移动下拉框
const
moveListBox
=
()
=>
{
// 获取光标位置
const
cursor
=
window
?.
getSelection
()?.
getRangeAt
(
0
)?.
getBoundingClientRect
()
||
null
;
const
containerRect
=
document
.
querySelector
(
'#RichText'
).
getBoundingClientRect
();
selectBoxRef
.
current
.
style
.
display
=
'block'
;
selectBoxRef
.
current
.
style
.
left
=
`
${
parseInt
(
cursor
.
x
-
containerRect
.
x
,
10
)
+
5
}
px`
;
selectBoxRef
.
current
.
style
.
top
=
`
${
parseInt
(
cursor
.
y
-
containerRect
.
y
,
10
)
+
25
}
px`
;
};
// 键盘事件
const
keyDownEvent
=
(
evet
)
=>
{
// 上下方向键
if
(
evet
.
key
===
'ArrowDown'
||
evet
.
key
===
'ArrowUp'
)
{
evet
.
preventDefault
();
if
(
selectBoxRef
.
current
?.
style
?.
display
===
'block'
)
{
const
max
=
selectBoxRef
.
current
.
querySelectorAll
(
'.selectItem'
)?.
length
||
1000
;
let
val
=
selectPersonIndex
;
if
(
evet
.
key
===
'ArrowDown'
)
{
if
(
!
val
&&
val
!=
0
)
{
val
=
0
;
}
else
{
val
+=
1
;
}
}
};
// const onChange = val => {
// setInputValue(val.currentTarget.value);
// handleChange(val.currentTarget.value);
// };
let
timer
=
null
;
const
filterList
=
(
val
,
list
)
=>
{
if
(
!
val
)
{
if
(
list
.
length
===
0
)
{
selectBoxRef
.
current
.
style
.
display
=
'none'
;
}
setSelectSearchList
(
list
);
}
else
{
const
arr
=
getArrayByName
(
val
,
allPeople
);
if
(
arr
.
length
===
0
)
{
selectBoxRef
.
current
.
style
.
display
=
'none'
;
}
setSelectSearchList
(
arr
);
if
(
evet
.
key
===
'ArrowUp'
)
val
-=
1
;
if
(
isNaN
(
val
)
||
!
val
||
val
<
0
)
val
=
0
;
if
(
val
>
max
-
1
)
val
=
max
-
1
;
selectPersonIndex
=
val
;
setSelectIndex
(
selectPersonIndex
);
}
}
if
(
evet
.
key
===
'Enter'
)
{
// 解决无法回车换行的bug
if
(
selectBoxRef
.
current
.
style
.
display
===
'block'
&&
selectSearchList
.
length
)
{
evet
.
preventDefault
();
if
(
selectPerson
[
selectPersonIndex
])
{
onSelect
(
selectPerson
[
selectPersonIndex
]);
}
return
false
;
}
}
};
useEffect
(()
=>
{
richTextRef
.
current
&&
richTextRef
.
current
.
removeEventListener
(
'input'
,
(
e
)
=>
{});
init
();
getAllPeople
();
return
()
=>
{
richTextRef
.
current
&&
richTextRef
.
current
.
removeEventListener
(
'input'
,
(
e
)
=>
{});
editor
&&
editor
.
destroy
();
editor
=
null
;
};
const
handleChange
=
(
val
,
list
)
=>
{
if
(
timer
)
{
clearTimeout
(
timer
);
},
[]);
useEffect
(()
=>
{
selectPersonIndex
=
null
;
setSelectIndex
(
null
);
selectPerson
=
selectSearchList
||
[];
},
[
selectSearchList
]);
useEffect
(()
=>
{
if
(
props
.
projectId
)
getData
();
},
[
props
.
projectId
]);
useEffect
(()
=>
{
const
keys
=
[];
const
arr
=
[];
if
(
props
.
personList
)
{
props
.
personList
.
forEach
((
i
)
=>
{
i
.
userId
=
Number
(
i
.
userId
);
if
(
!
keys
.
includes
(
i
.
userId
))
{
keys
.
push
(
i
.
userId
);
arr
.
push
(
i
);
}
timer
=
setTimeout
(()
=>
{
timer
=
null
;
filterList
(
val
,
list
);
},
200
);
// filterList(val, list);
};
/**
* 根据字符串模糊搜索返回符合条件的数据
* name 搜索字符串
* array 检索json数组
*/
const
getArrayByName
=
(
name
,
array
)
=>
{
const
result
=
[];
array
.
forEach
(
i
=>
{
if
(
i
.
name
.
indexOf
(
name
)
!=
-
1
)
result
.
push
({
userName
:
i
.
name
,
userId
:
i
.
id
,
port
:
i
.
port
});
});
return
result
;
};
const
addFile
=
e
=>
{
if
(
e
.
target
)
{
const
file
=
e
.
target
.
files
[
0
];
const
formData
=
new
FormData
();
formData
.
append
(
'file'
,
file
);
setLoading
(
true
);
request
({
url
:
API
.
POST_UPLOADERFILES
,
method
:
'POST'
,
data
:
formData
,
})
.
then
(
res
=>
{
if
(
res
.
data
)
{
const
arr
=
[...
fileList
];
const
url
=
res
.
data
.
replace
(
/
[\\
\/
=
]
/g
,
'/'
);
arr
.
unshift
({
name
:
file
.
name
,
type
:
file
.
type
?
file
.
type
.
toLowerCase
()
:
''
,
size
:
file
.
size
,
path
:
`
${
API
.
GET_DOWNLOADFILES
}
?filePath=
${
url
}
`
,
});
// setFileList(arr);
props
.
onChangeFile
(
arr
);
setLoading
(
false
);
}
else
{
message
.
error
(
res
.
msg
);
setLoading
(
false
);
}
})
.
catch
(
err
=>
{
setLoading
(
false
);
});
});
}
if
(
selectList
)
{
selectList
.
forEach
((
i
)
=>
{
i
.
userId
=
Number
(
i
.
userId
);
if
(
!
keys
.
includes
(
i
.
userId
))
{
arr
.
push
(
i
);
keys
.
push
(
i
.
userId
);
}
};
const
onDelFile
=
item
=>
{
const
arr
=
[];
fileList
.
forEach
(
i
=>
{
if
(
i
.
path
!==
item
.
path
)
{
arr
.
push
(
i
);
}
});
}
tempList
=
arr
;
setSelectSearchList
(
arr
);
},
[
selectList
,
props
.
personList
]);
// useEffect(() => {
// if (!props.projectId) {
// setSelectList(props.personList || []);
// }
// }, [props.personList]);
useEffect
(()
=>
{
setFileList
(
props
.
fileList
);
},
[
props
.
fileList
]);
const
getHtml
=
(
val
)
=>
editor
.
txt
.
html
();
// 获取文本,不含标签
const
getText
=
(
val
)
=>
editor
.
txt
.
text
();
// 清除
const
onClear
=
()
=>
{
editor
.
txt
.
clear
();
};
// 设置内容
const
setHtml
=
(
val
)
=>
{
editor
.
txt
.
html
(
val
||
''
);
};
// 关闭人员下拉选框
const
closeList
=
()
=>
{
selectPersonIndex
=
null
;
setSelectIndex
(
null
);
selectBoxRef
.
current
.
style
.
display
=
'none'
;
if
(
range
)
{
selection
.
removeRange
(
range
);
range
=
null
;
}
};
// @某人
const
onSelect
=
(
item
)
=>
{
if
(
range
)
{
range
.
deleteContents
();
// 删除前一个@符号
editor
.
cmd
.
do
(
'delete'
);
const
_html
=
`<span><span data-userId="
${
item
.
userId
}
" data-type="person" >@
${
item
.
userName
}
</span><span> </span></span>`
;
editor
.
cmd
.
do
(
'insertElem'
,
_html
);
closeList
();
selection
.
collapseToEnd
();
}
};
// const onChange = val => {
// setInputValue(val.currentTarget.value);
// handleChange(val.currentTarget.value);
// };
let
timer
=
null
;
const
filterList
=
(
val
,
list
)
=>
{
if
(
!
val
)
{
if
(
list
.
length
===
0
)
{
selectBoxRef
.
current
.
style
.
display
=
'none'
;
}
setSelectSearchList
(
list
);
}
else
{
const
arr
=
getArrayByName
(
val
,
allPeople
);
if
(
arr
.
length
===
0
)
{
selectBoxRef
.
current
.
style
.
display
=
'none'
;
}
setSelectSearchList
(
arr
);
}
};
const
handleChange
=
(
val
,
list
)
=>
{
if
(
timer
)
{
clearTimeout
(
timer
);
}
timer
=
setTimeout
(()
=>
{
timer
=
null
;
filterList
(
val
,
list
);
},
200
);
// filterList(val, list);
};
/**
* 根据字符串模糊搜索返回符合条件的数据
* name 搜索字符串
* array 检索json数组
*/
const
getArrayByName
=
(
name
,
array
)
=>
{
const
result
=
[];
array
.
forEach
((
i
)
=>
{
if
(
i
.
name
.
indexOf
(
name
)
!=
-
1
)
result
.
push
({
userName
:
i
.
name
,
userId
:
i
.
id
,
port
:
i
.
port
});
});
return
result
;
};
const
addFile
=
(
e
)
=>
{
if
(
e
.
target
)
{
const
file
=
e
.
target
.
files
[
0
];
const
formData
=
new
FormData
();
formData
.
append
(
'file'
,
file
);
setLoading
(
true
);
request
({
url
:
API
.
POST_UPLOADERFILES
,
method
:
'POST'
,
data
:
formData
,
})
.
then
((
res
)
=>
{
if
(
res
.
data
)
{
const
arr
=
[...
fileList
];
const
url
=
res
.
data
.
replace
(
/
[\\
\/
=
]
/g
,
'/'
);
arr
.
unshift
({
name
:
file
.
name
,
type
:
file
.
type
?
file
.
type
.
toLowerCase
()
:
''
,
size
:
file
.
size
,
path
:
`
${
API
.
GET_DOWNLOADFILES
}
?filePath=
${
url
}
`
,
});
// setFileList(arr);
props
.
onChangeFile
(
arr
);
setLoading
(
false
);
}
else
{
message
.
error
(
res
.
msg
);
setLoading
(
false
);
}
})
.
catch
((
err
)
=>
{
setLoading
(
false
);
});
// setFileList(arr);
props
.
onChangeFile
(
arr
);
};
useImperativeHandle
(
ref
,
()
=>
({
setHtml
,
onClear
,
getHtml
,
getText
,
}));
return
(
<
div
className
=
{
styles
.
RichText
}
id
=
"RichText"
>
{
loading
?
(
<
div
className
=
{
styles
.
loadingWrap
}
style
=
{{
zIndex
:
zIndex
+
20
}}
>
<
Spin
spinning
=
{
loading
}
/
>
<
/div
>
)
:
null
}
<
div
id
=
"RichTextToolbar"
className
=
{
styles
.
RichTextToolbar
}
/
>
<
div
ref
=
{
richTextRef
}
id
=
"RichTextContainer"
className
=
{
styles
.
RichTextContainer
}
/
>
<
div
className
=
{
styles
.
RichTextFileList
}
>
<
FileListItem
list
=
{
fileList
}
onDel
=
{
val
=>
{
onDelFile
(
val
);
}}
type
=
"edit"
onPreview
=
{
val
=>
{
if
(
!
val
)
return
;
setImgPreviewSrc
(
val
.
path
);
setImgVisible
(
true
);
}}
/
>
<
/div
>
<
div
ref
=
{
selectBoxRef
}
className
=
{
styles
.
selectBox
}
style
=
{{
maxWidth
:
'300px'
,
minWidth
:
'150px'
,
zIndex
:
zIndex
+
10
}}
>
{
selectSearchList
.
length
?
(
<
div
className
=
{
styles
.
selectList
}
>
{
selectSearchList
.
map
((
item
,
index
)
=>
(
<
div
key
=
{
item
.
userId
}
onClick
=
{()
=>
{
onSelect
(
item
);
}}
className
=
{
classNames
(
'selectItem'
,
styles
.
selectItem
,
selectIndex
===
index
?
styles
.
selectActiveItem
:
''
,
)}
>
{
item
.
userName
}
<
/div
>
))}
<
/div
>
)
:
null
}
<
/div
>
<
input
style
=
{{
display
:
'none'
}}
type
=
"file"
ref
=
{
fileInputRef
}
onChange
=
{
e
=>
{
addFile
(
e
);
}}
name
=
"file"
/>
<
Image
width
=
{
200
}
style
=
{{
display
:
'none'
}}
src
=
{
imgPreviewSrc
}
preview
=
{{
visible
:
imgVisible
,
src
:
imgPreviewSrc
,
onVisibleChange
:
value
=>
{
setImgVisible
(
value
);
if
(
!
value
)
setImgPreviewSrc
(
''
);
},
}}
/
>
}
};
const
onDelFile
=
(
item
)
=>
{
const
arr
=
[];
fileList
.
forEach
((
i
)
=>
{
if
(
i
.
path
!==
item
.
path
)
{
arr
.
push
(
i
);
}
});
// setFileList(arr);
props
.
onChangeFile
(
arr
);
};
useImperativeHandle
(
ref
,
()
=>
({
setHtml
,
onClear
,
getHtml
,
getText
,
}));
return
(
<
div
className
=
{
styles
.
RichText
}
id
=
"RichText"
>
{
loading
?
(
<
div
className
=
{
styles
.
loadingWrap
}
style
=
{{
zIndex
:
zIndex
+
20
}}
>
<
Spin
spinning
=
{
loading
}
/
>
<
/div
>
);
)
:
null
}
<
div
id
=
"RichTextToolbar"
className
=
{
styles
.
RichTextToolbar
}
/
>
<
div
ref
=
{
richTextRef
}
id
=
"RichTextContainer"
className
=
{
styles
.
RichTextContainer
}
/
>
<
div
className
=
{
styles
.
RichTextFileList
}
>
<
FileListItem
list
=
{
fileList
}
onDel
=
{(
val
)
=>
{
onDelFile
(
val
);
}}
type
=
"edit"
onPreview
=
{(
val
)
=>
{
if
(
!
val
)
return
;
setImgPreviewSrc
(
val
.
path
);
setImgVisible
(
true
);
}}
/
>
<
/div
>
<
div
ref
=
{
selectBoxRef
}
className
=
{
styles
.
selectBox
}
style
=
{{
maxWidth
:
'300px'
,
minWidth
:
'150px'
,
zIndex
:
zIndex
+
10
}}
>
{
selectSearchList
.
length
?
(
<
div
className
=
{
styles
.
selectList
}
>
{
selectSearchList
.
map
((
item
,
index
)
=>
(
<
div
key
=
{
item
.
userId
}
onClick
=
{()
=>
{
onSelect
(
item
);
}}
className
=
{
classNames
(
'selectItem'
,
styles
.
selectItem
,
selectIndex
===
index
?
styles
.
selectActiveItem
:
''
,
)}
>
{
item
.
userName
}
<
/div
>
))}
<
/div
>
)
:
null
}
<
/div
>
<
input
style
=
{{
display
:
'none'
}}
type
=
"file"
ref
=
{
fileInputRef
}
onChange
=
{(
e
)
=>
{
addFile
(
e
);
}}
name
=
"file"
/>
<
Image
width
=
{
200
}
style
=
{{
display
:
'none'
}}
src
=
{
imgPreviewSrc
}
preview
=
{{
visible
:
imgVisible
,
src
:
imgPreviewSrc
,
onVisibleChange
:
(
value
)
=>
{
setImgVisible
(
value
);
if
(
!
value
)
setImgPreviewSrc
(
''
);
},
}}
/
>
<
/div
>
);
});
export
default
RichText
;
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment