177 lines
8.2 KiB
JavaScript
177 lines
8.2 KiB
JavaScript
import React from 'react';
|
||
import { Alert, Card, Button, Row, Col, Space, Affix, Input } from 'antd';
|
||
import '@wangeditor/editor/dist/css/style.css'; // 引入 css
|
||
import { Editor, Toolbar } from '@wangeditor/editor-for-react';
|
||
import ExtraFileUpload from '../../extraFile/upload';
|
||
import ExtraFileCommit from '../../extraFile/commit';
|
||
import { generateNewId } from 'oak-domain/lib/utils/uuid';
|
||
import Style from './web.module.less';
|
||
// 工具栏配置
|
||
const toolbarConfig = {
|
||
excludeKeys: ['fullScreen'],
|
||
}; // TS 语法
|
||
// 自定义校验图片
|
||
function customCheckImageFn(src, alt, url) {
|
||
// TS 语法
|
||
if (!src) {
|
||
return;
|
||
}
|
||
if (src.indexOf('http') !== 0) {
|
||
return '图片网址必须以 http/https 开头';
|
||
}
|
||
return true;
|
||
// 返回值有三种选择:
|
||
// 1. 返回 true ,说明检查通过,编辑器将正常插入图片
|
||
// 2. 返回一个字符串,说明检查未通过,编辑器会阻止插入。会 alert 出错误信息(即返回的字符串)
|
||
// 3. 返回 undefined(即没有任何返回),说明检查未通过,编辑器会阻止插入。但不会提示任何信息
|
||
}
|
||
export default function Render(props) {
|
||
const { methods, data } = props;
|
||
const { t, setEditor, preview, uploadFile, update, setHtml, navigateBack, } = methods;
|
||
const { editor, origin, oakFullpath } = data;
|
||
return (<div className={Style.container}>
|
||
<Affix offsetTop={64}>
|
||
<Toolbar editor={editor} defaultConfig={toolbarConfig} mode="default"/>
|
||
</Affix>
|
||
|
||
<Row>
|
||
<Col flex={4}/>
|
||
|
||
<Col flex={16}>
|
||
<div className={Style.content}>
|
||
<div className={Style.editorContainer}>
|
||
<div className={Style.titleContainer}>
|
||
<Input onChange={(e) => update({ title: e.target.value })} value={data.title} placeholder={t('placeholder.title')} size="large" maxLength={64} suffix={`${[...(data.title || '')].length}/64`} className={Style.titleInput}/>
|
||
</div>
|
||
<div className={Style.authorContainer}>
|
||
<Input onChange={(e) => update({ author: e.target.value })} value={data.author} placeholder={t('placeholder.author')} className={Style.input} maxLength={16}/>
|
||
</div>
|
||
{data.contentTip && (<Alert type="info" message={t('tips.content')} closable onClose={() => methods.clearContentTip()}/>)}
|
||
<Editor defaultConfig={{
|
||
// TS 语法
|
||
placeholder: t('placeholder.content'),
|
||
MENU_CONF: {
|
||
// fontSize: {
|
||
// fontSizeList: [
|
||
// '12px',
|
||
// '16px',
|
||
// '24px',
|
||
// '40px',
|
||
// ],
|
||
// },
|
||
checkImage: customCheckImageFn,
|
||
uploadImage: {
|
||
// 自定义上传
|
||
async customUpload(file, insertFn) {
|
||
// TS 语法
|
||
// file 即选oi中的文件
|
||
const { name, size, type } = file;
|
||
const extension = name.substring(name.lastIndexOf('.') +
|
||
1);
|
||
const filename = name.substring(0, name.lastIndexOf('.'));
|
||
const extraFile = {
|
||
origin: origin,
|
||
type: 'image',
|
||
tag1: 'source',
|
||
objectId: generateNewId(),
|
||
filename,
|
||
size,
|
||
extension,
|
||
bucket: '',
|
||
id: generateNewId(),
|
||
sort: 1000,
|
||
uploadState: 'uploading',
|
||
};
|
||
try {
|
||
// 自己实现上传,并得到图片 url alt href
|
||
const url = await uploadFile(extraFile, file);
|
||
// 最后插入图片
|
||
insertFn(url, extraFile.filename);
|
||
}
|
||
catch (err) { }
|
||
},
|
||
},
|
||
uploadVideo: {
|
||
// 自定义上传
|
||
async customUpload(file, insertFn) {
|
||
// TS 语法
|
||
// file 即选中的文件
|
||
const { name, size, type } = file;
|
||
const extension = name.substring(name.lastIndexOf('.') +
|
||
1);
|
||
const filename = name.substring(0, name.lastIndexOf('.'));
|
||
const extraFile = {
|
||
origin: origin,
|
||
type: 'video',
|
||
tag1: 'source',
|
||
objectId: generateNewId(),
|
||
filename,
|
||
size,
|
||
extension,
|
||
bucket: '',
|
||
id: generateNewId(),
|
||
sort: 1000,
|
||
uploadState: 'uploading',
|
||
};
|
||
try {
|
||
// 自己实现上传,并得到图片 url alt href
|
||
const url = await uploadFile(extraFile, file);
|
||
// 最后插入图片
|
||
insertFn(url, url +
|
||
'?vframe/jpg/offset/0');
|
||
}
|
||
catch (err) { }
|
||
},
|
||
},
|
||
},
|
||
}} value={data.content} onCreated={setEditor} onChange={(editor) => {
|
||
setHtml(editor.getHtml());
|
||
}} mode="default" style={{
|
||
minHeight: '520px',
|
||
overflowY: 'hidden',
|
||
}}/>
|
||
<div className={Style.abstract}>
|
||
<Card title="封面及摘要" bordered={false} className={Style.card}>
|
||
<Row>
|
||
<Col flex="none">
|
||
<ExtraFileUpload maxNumber={1} oakPath={`${oakFullpath}.extraFile$entity`} type="image" origin="qiniu" tag1="cover" entity="article"></ExtraFileUpload>
|
||
</Col>
|
||
<Col flex="auto">
|
||
<Input.TextArea autoSize={{
|
||
minRows: 4,
|
||
}} maxLength={120} placeholder={t('placeholder.abstract')} onChange={(e) => update({
|
||
abstract: e.target.value,
|
||
})} value={data.abstract || ''}></Input.TextArea>
|
||
</Col>
|
||
</Row>
|
||
</Card>
|
||
</div>
|
||
<div className={Style.footer}>
|
||
<Row align="middle">
|
||
<Col flex="auto">
|
||
<div className={Style.contentNumber}>
|
||
<span>正文字数 0</span>
|
||
</div>
|
||
</Col>
|
||
<Col flex="none">
|
||
<Space>
|
||
<ExtraFileCommit afterCommit={() => {
|
||
navigateBack();
|
||
}} oakPath={oakFullpath}/>
|
||
<Button onClick={() => {
|
||
preview();
|
||
}}>
|
||
预览
|
||
</Button>
|
||
</Space>
|
||
</Col>
|
||
</Row>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</Col>
|
||
<Col flex={4}/>
|
||
</Row>
|
||
</div>);
|
||
}
|