Commit d4f782c4 authored by 周宏民's avatar 周宏民

feat: rich

parent 6620388e
Pipeline #53929 failed with stages
in 5 seconds
......@@ -93,7 +93,7 @@ export default {
'DatePickerCustom',
'LoadBox',
'TipTool',
'RichText',
'rich',
],
},
{
......
# `@wisdom-components/rich`
> TODO: description
## Usage
```
const rich = require('@wisdom-components/rich');
// TODO: DEMONSTRATE API
```
'use strict';
const rich = require('..');
describe('@wisdom-components/rich', () => {
it('needs tests');
});
{
"name": "@wisdom-components/rich",
"version": "0.0.1",
"description": "> TODO: description",
"author": "hongmye <1014185119@qq.com>",
"homepage": "",
"license": "ISC",
"module": "es/index.js",
"main": "lib/index.js",
"sideEffects": [
"*.less"
],
"directories": {
"lib": "lib",
"test": "__tests__"
},
"files": [
"lib",
"es",
"dist"
],
"publishConfig": {
"registry": "https://g.civnet.cn:4873/"
},
"repository": {
"type": "git",
"url": "https://g.civnet.cn:8443/ReactWeb5/wisdom-components.git"
},
"scripts": {
"test": "echo \"Error: run tests from root\" && exit 1"
},
"dependencies": {
"@babel/runtime": "^7.17.9",
"classnames": "^2.2.6"
}
}
/*
* @Title:富文本展示,可预览图片
* @Author: hongmye
* @Date: 2022-03-21 14:44:49
*/
/**
* 1.引入组件 import RichTextShow from '@/components/RichText/RichTextShow';
* 示例:<RichTextShow content={this.state.content} onClickImg={src => {}} />;
*
* 2.传递方法 onClickImg 点击图片回调(不需可不传)
*
* 3.传值接收 content 富文本内容
* fileList 附件列表
*
*
*/
import { Image } from 'antd';
import { useEffect, useState } from 'react';
import FileListItem from './fileListItem';
import styles from './index.less';
function RichTextShow(props) {
const [imgVisible, setImgVisible] = useState(false);
const [imgPreviewSrc, setImgPreviewSrc] = useState('');
const [fileList, setFileList] = useState([]);
const onPreview = (e) => {
e.stopPropagation();
const src = e?.target?.src || null;
if (!src) return;
setImgPreviewSrc(src);
setImgVisible(true);
props.onClickImg && props.onClickImg(src, e.target);
};
useEffect(() => {
setFileList(props.fileList);
}, [props.fileList]);
useEffect(() => {}, [props.content]);
return (
<div style={{ ...props.style }}>
<div
className={styles.RichTextShow}
onClick={onPreview}
dangerouslySetInnerHTML={{ __html: props.content || '' }}
/>
{fileList && fileList.length > 0 && (
<div className={styles.RichTextFileList}>
<FileListItem
list={fileList}
onDel={(val) => {
onDelFile(val);
}}
onPreview={(val) => {
if (!val) return;
setImgPreviewSrc(val.path);
setImgVisible(true);
}}
/>
</div>
)}
<Image
width={200}
style={{ display: 'none' }}
src={imgPreviewSrc}
preview={{
visible: imgVisible,
src: imgPreviewSrc,
onVisibleChange: (value) => {
setImgVisible(value);
if (!value) setImgPreviewSrc('');
},
}}
/>
</div>
);
}
export default RichTextShow;
/*
* @Title:
* @Author: hongmye
* @Date: 2022-06-23 16:03:16
*/
// @ts-ignore
import React, { useEffect, useState } from 'react';
import Empty from '@wisdom-components/empty';
// import BasicTable from '../index';
import request from 'umi-request';
const Demo = () => {
const [columns, setColumns] = useState([]);
const [dataSource, setDataSource] = useState([]);
useEffect(() => {
fetchData();
}, []);
const fetchData = (params = {}) => {
request(`${baseUrl}/AcrossTable/GetEquipmentDataReport`, {
method: 'get',
params: {},
}).then(function (response) {
const data = response.data;
let column = data.map((item, index) => {
return {
title: `${item.DName + item.DType}(${item.Unit})`,
dataIndex: `name${index}`,
key: `name${index}`,
};
});
column.unshift({
title: '时间',
dataIndex: 'time',
key: 'time',
});
let dataSource = data[0].NameDate.map((item, index) => ({ key: index, time: item.Time }));
data.forEach((item, index) => {
item.NameDate.forEach((child) => {
dataSource.forEach((v) => {
if (child.Time === v.time) v[`name${index}`] = child.Value;
});
});
});
setColumns(column);
setDataSource(dataSource);
});
};
// @ts-ignore
return (
<div style={{ height: '800px' }}>
{!!dataSource.length && (
// <BasicTable
// dataSource={dataSource}
// columns={columns}
// bordered={false}
// pagination={{ pageSize: 20, size: 'default' }}
// />
)}
{!dataSource.length && <Empty description={'暂无数据'} />}
</div>
);
};
export default Demo;
const baseUrl = 'https://www.fastmock.site/mock/162c15dca15c4dba9ba51e0a0b76929b/api';
/*
* @Title: 富文本编辑器demo
* @Author: hongmye
* @Date: 2022-03-07 14:44:32
*/
import React from 'react';
import { Button } from 'antd';
import RichText from '../index';
import RichTextShow from '../RichTextShow';
// import { projectManageService } from '@/api';
import styles from './demo.less';
class RichTextDemo extends React.Component {
constructor(props) {
super(props);
this.myRichText = React.createRef();
this.state = {
personList: [
{ userId: 568, userName: '徐乐' },
{ userId: 569, userName: '余苏苏' },
{ userId: 572, userName: '周宏民' },
],
content: '',
fileList: [],
};
}
getData = async () => {
// const res = await projectManageService.GetWorkHourUserList({ projectId: 19 });
// this.setState({
// // personList: res.data || [],
// });
// const arr = [
// {
// name: '工时管理_工时日报 (1).xlsx',
// type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
// size: 8192,
// path:
// '/PandaWorkFlow/WorkFlow/AccountManage/DownloadFiles?filePath=b0422fdb-6c63-4b34-8021-b54102236bcb/工时管理_工时日报/(1).xlsx',
// },
// {
// name: '浊度.png',
// type: 'image/png',
// size: 6789,
// path:
// '/PandaWorkFlow/WorkFlow/AccountManage/DownloadFiles?filePath=fd00ce06-52c0-410a-bd30-8aed9131a616/浊度.png',
// },
// ];
// // this.setState({ fileList: arr });
// this.myRichText.current && this.myRichText.current.setHtml('');
};
componentDidMount = () => {
this.getData();
};
componentDidUpdate = (propNev, stateNev) => { };
componentWillUnmount() {
this.setState = () => { };
}
render() {
return (
<div className={styles.RichTextDemo}>
<div className={styles.RichTextDemoContainer}>
<RichText
// content={this.state.content}
personList={this.state.personList}
onChange={val => {
this.setState({ content: val });
}}
onChangeFile={arr => {
this.setState({ fileList: arr });
}}
fileList={this.state.fileList}
projectId={19}
ref={this.myRichText}
/>
</div>
{/* <div className={styles.contentBox}>{this.state.content}</div> */}
<div className={styles.contentBox}>
<RichTextShow content={this.state.content} onClickImg={src => { }} fileList={this.state.fileList} />
</div>
<div className={styles.contentBox}>{this.state.content}</div>
</div>
);
}
}
export default RichTextDemo;
.RichTextDemoContainer {
// height: 400px;
// padding: 0px 100px;
background-color: #fff;
}
.contentBox {
// height: 400px;
margin-top: 10px;
padding: 20px;
background-color: #fff;
}
.RichTextDemo {
height: 100%;
overflow-y: scroll;
}
\ No newline at end of file
/*
* @Title:
* @Author: hongmye
* @Date: 2022-03-21 14:44:49
*/
import React, { useState, useEffect, useRef, useImperativeHandle, forwardRef } from 'react';
import { FileTwoTone, DeleteOutlined, FileImageTwoTone, DownloadOutlined, PictureOutlined } from '@ant-design/icons';
import { Button } from 'antd';
import iconFile from './icon_file.png';
function fileListItem(props) {
const [fileList, setFileList] = useState([]);
const imgType = ['image/png', 'image/jpg', 'image/jpeg'];
const onPreview = item => {
props.onPreview(item);
};
const onDownLoad = item => {
const a = document.createElement('a');
a.setAttribute('download', item.name);
a.setAttribute('href', item.path);
document.documentElement.appendChild(a);
a.target = '_black';
a.click();
};
useEffect(() => {
setFileList(props.list || []);
}, [props.list]);
return (
<>
{fileList.map((item, index) => {
return (
<div key={index} className={'panda-civ-work-upload-list panda-civ-work-upload-list-picture'}>
<div className={'panda-civ-work-upload-list-picture-container'}>
<div className={'panda-civ-work-upload-list-item'}>
<div className={'panda-civ-work-upload-list-item-info'}>
<span className={'panda-civ-work-upload-span'}>
<div
className={
'panda-civ-work-upload-list-item-thumbnail panda-civ-work-upload-list-item-file'
}
>
{/* {imgType.includes(item.type) ? <FileImageTwoTone /> : <FileTwoTone />} */}
<img src={iconFile} />
</div>
<span
className={'panda-civ-work-upload-list-item-name'}
title={item.name}
style={{ cursor: 'pointer' }}
>
{item.name}
</span>
<span className={'panda-civ-work-upload-list-item-card-actions picture'}>
{imgType.includes(item.type) && (
<Button
title="预览"
type="text"
onClick={e => {
e && e.stopPropagation();
onPreview(item);
}}
style={{ padding: '4px 8px' }}
>
<PictureOutlined />
</Button>
)}
<Button
title="下载"
type="text"
onClick={e => {
e && e.stopPropagation();
onDownLoad(item);
}}
style={{ padding: '4px 8px' }}
>
<DownloadOutlined />
</Button>
{props.type === 'edit' && (
<Button
title="删除文件"
type="text"
onClick={e => {
e && e.stopPropagation();
props.onDel(item);
}}
style={{ padding: '4px 8px' }}
>
<DeleteOutlined />
</Button>
)}
</span>
</span>
</div>
</div>
</div>
</div>
);
})}
</>
);
}
export default fileListItem;
This diff is collapsed.
@import '~antd/es/style/themes/default.less';
// @import '~antd/es/image/style/index.less';
.RichText {
width: 100%;
height: auto;
min-height: 200px;
position: relative;
display: flex;
flex-direction: column;
border: 1px solid #d9d9d9;
border-radius: 2px;
.loadingWrap {
width: 100%;
height: 100%;
position: absolute;
display: flex;
align-items: center;
justify-content: center;
top: 0;
left: 0;
background-color: rgba(255, 255, 255, 0.8);
}
.selectBox {
position: absolute;
top: 0;
left: 0;
background: #fff;
max-height: 250px;
overflow-y: scroll;
display: none;
}
.selectList{
border: 1px solid #efefef;
box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.16), 0 0;
}
.selectList .selectItem:hover {
background: #1685FF;
color: #fff;
cursor: pointer;
}
.selectList .selectActiveItem {
background: #1685FF;
color: #fff;
}
.selectItem {
padding: 5px 10px;
}
ol li {
list-style: decimal !important;
}
ul li {
list-style: disc !important;
}
}
.RichTextFileList {
padding: 20px;
:global {
.@{ant-prefix}-upload-list-item {
height: 44px;
padding: 8px 16px;
.@{ant-prefix}-upload-list-item-thumbnail,
.@{ant-prefix}-upload-list-item-file {
width: 20px;
height: 22px;
>img {
width: 100%;
height: 100%;
}
}
.@{ant-prefix}-upload-list-item-name {
line-height: normal;
}
}
}
}
.RichTextContainer {
background-color: #fff;
color: #000000d9;
position: relative;
overflow-y: scroll;
flex: 1;
pre {
white-space: pre-wrap;
word-wrap: break-word;
}
}
.RichTextToolbar {
border-bottom: 1px solid #d9d9d9;
// height: 0;
// overflow: hidden;
}
.RichTextShow img {
cursor: pointer;
}
.RichTextShow span[data-type='person'] {
font-weight: bold;
color: #44acb6 !important;
}
:global {
#RichTextContainer span[data-type='person'] {
font-weight: bold;
color: #44acb6 !important;
}
.w-e-menu[data-title="图片"] {
display: none;
}
.RichText-image {
position: relative;
display: inline-block;
}
#RichTextContainer .RichText-image-img {
max-width: calc(100% - 20px);
display: inline-block;
width: auto;
height: auto;
}
.RichText-image-mask {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
background: rgba(0, 0, 0, 0.5);
cursor: pointer;
opacity: 0;
transition: opacity 0.3s;
padding: 0;
margin: 0;
box-sizing: border-box;
}
.RichText-image-mask-info {
padding: 0;
margin: 0;
box-sizing: border-box;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
cursor: pointer;
color: #fff;
font-size: 20px;
}
.RichText-image-mask-info .anticon {
margin-inline-end: 4px;
}
.anticon svg {
display: inline-block;
}
.RichText-image-mask:hover {
opacity: 1;
}
.r-t-add-file {
color: #999;
font-family: 'w-e-icon' !important;
speak: none;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
-webkit-font-smoothing: antialiased;
padding: 0;
margin: 0;
box-sizing: border-box;
}
.r-t-add-file:before {
content: "\e9cb";
}
.@{ant-prefix}-image-error {
display: block;
}
}
\ No newline at end of file
---
title: RichText - 富文本编辑器
nav:
title: 基础组件
path: /components
group:
path: /
---
# RichText 富文本编辑器
## 代码演示
### 常规使用
<code src="./demos/demo.jsx">
### 合计
<!-- <code src="./demos/Summary.tsx"> -->
## API
api 参考 Antd Table 组件 https://www.wangeditor.com/v4/
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment