fix: 新建article时 ,上传文件extraFile缺失entityId

This commit is contained in:
wkj 2025-12-15 18:18:04 +08:00
parent 21348fff43
commit ae1d1cf70a
12 changed files with 176 additions and 232 deletions

View File

@ -21,7 +21,6 @@ export default OakComponent({
data: {
editor: null,
html: '',
contentTip: false,
},
properties: {
articleMenuId: '',
@ -40,15 +39,14 @@ export default OakComponent({
next.editor.setHtml(next.content);
}
},
oakId(prev, next) {
if (prev.oakId !== next.oakId) {
const { editor } = this.state;
if (editor == null)
return;
editor.destroy();
this.setEditor(null);
}
},
// oakId(prev, next) {
// if (prev.oakId !== next.oakId) {
// const { editor } = this.state;
// if (editor == null) return;
// editor.destroy();
// this.setEditor(null);
// }
// },
name(prev, next) {
if (prev.name !== next.name) {
window.document.title = next.name ?? '';
@ -58,7 +56,7 @@ export default OakComponent({
lifetimes: {
async ready() {
const { oakId, articleMenuId } = this.props;
if (!oakId) {
if (this.isCreation()) {
if (articleMenuId) {
this.update({
articleMenuId,
@ -89,11 +87,6 @@ export default OakComponent({
editor,
});
},
clearContentTip() {
this.setState({
contentTip: false,
});
},
async check() {
if (this.state.name &&
this.state.name.length > 0 &&

View File

@ -7,7 +7,7 @@
"name": "请输入文章标题",
"content": "请输入文章内容..."
},
"chcek": {
"check": {
"no name": "请填写文章标题!",
"no content": "请填写文章内容!"
}

View File

@ -8,7 +8,6 @@ export default function Render(props: WebComponentProps<EntityDict, 'article', f
editor: any;
content?: string;
origin?: null | EntityDict['extraFile']['Schema']['origin'];
contentTip: boolean;
articleMenuId: string;
oakId: string;
tocPosition: 'none' | 'left' | 'right';
@ -26,6 +25,5 @@ export default function Render(props: WebComponentProps<EntityDict, 'article', f
setEditor: (editor: any) => void;
check: () => void;
uploadFile: (extraFile: EntityDict['extraFile']['CreateOperationData'], file: File) => Promise<string>;
clearContentTip: () => void;
gotoPreview: (content?: string, title?: string) => void;
}>): React.JSX.Element;

View File

@ -1,5 +1,5 @@
import React, { useState, useEffect } from "react";
import { Alert, Button, Space, Input, } from "antd";
import { Button, Space, Input, } from "antd";
import "@wangeditor/editor/dist/css/style.css"; // 引入 css
import { Editor, Toolbar } from "@wangeditor/editor-for-react";
import { SlateNode } from "@wangeditor/editor";
@ -30,7 +30,7 @@ function customCheckImageFn(src, alt, url) {
}
export default function Render(props) {
const { methods, data } = props;
const { t, setEditor, check, uploadFile, update, setHtml, gotoPreview, clearContentTip, } = methods;
const { t, setMessage, setEditor, check, uploadFile, update, setHtml, gotoPreview, } = methods;
const { oakId, oakFullpath, id, name, content, editor, origin, tocPosition = 'none', highlightBgColor, scrollId, tocWidth, tocHeight, height = 600, tocClosed = false, execuable, activeColor, html, oakLoading, oakExecuting, } = data;
const [articleId, setArticleId] = useState('');
const [toc, setToc] = useState([]);
@ -80,7 +80,6 @@ export default function Render(props) {
<div className={classNames(Style.editorContainer, {
[Style.editorExternalContainer]: !!scrollId,
})}>
{data.contentTip && (<Alert type="info" message={t("tips.content")} closable onClose={() => clearContentTip()}/>)}
<div className={Style.titleContainer}>
<Input onChange={(e) => {
if (e.target.value.trim() && e.target.value.trim() !== '') {
@ -89,102 +88,107 @@ export default function Render(props) {
else {
update({ name: null });
}
}} value={data.name ?? ''} placeholder={t('placeholder.name')} size="large" maxLength={32}
// suffix={`${(data.name || "").length}/32`}
showCount className={Style.titleInput}/>
}} value={data.name ?? ''} placeholder={t('placeholder.name')} size="large" maxLength={32} showCount className={Style.titleInput}/>
</div>
<div>
<Editor defaultConfig={{
autoFocus: true,
placeholder: t('placeholder.content'),
MENU_CONF: {
checkImage: customCheckImageFn,
uploadImage: {
// 自定义上传
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 = {
entity: "article",
entityId: oakId || articleId,
origin: origin,
type: "image",
tag1: "source",
objectId: generateNewId(),
filename,
size,
extension,
bucket: "",
id: generateNewId(),
fileType: type,
};
try {
// 自己实现上传,并得到图片 url alt href
const url = await uploadFile(extraFile, file);
// 最后插入图片
insertFn(url, extraFile.filename);
}
catch (err) { }
{!!articleId && <Editor defaultConfig={{
autoFocus: true,
placeholder: t('placeholder.content'),
MENU_CONF: {
checkImage: customCheckImageFn,
uploadImage: {
// 自定义上传
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 = {
entity: "article",
entityId: oakId || articleId,
origin: origin,
type: "image",
tag1: "source",
objectId: generateNewId(),
filename,
size,
extension,
bucket: "",
id: generateNewId(),
fileType: type,
};
try {
// 自己实现上传,并得到图片 url alt href
const url = await uploadFile(extraFile, file);
// 最后插入图片
insertFn(url, extraFile.filename);
}
catch (err) {
setMessage({
type: "error",
content: err.message,
});
}
},
},
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 = {
entity: "article",
entityId: oakId || articleId,
origin: origin,
type: "video",
tag1: "source",
objectId: generateNewId(),
filename,
size,
extension,
bucket: "",
id: generateNewId(),
fileType: type,
};
try {
// 自己实现上传,并得到图片 url alt href
const url = await uploadFile(extraFile, file);
// 最后插入图片
insertFn(url, url + "?vframe/jpg/offset/0");
}
catch (err) {
setMessage({
type: "error",
content: err.message,
});
}
},
},
},
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 = {
entity: "article",
entityId: oakId || articleId,
origin: origin,
type: "video",
tag1: "source",
objectId: generateNewId(),
filename,
size,
extension,
bucket: "",
id: generateNewId(),
fileType: type,
};
try {
// 自己实现上传,并得到图片 url alt href
const url = await uploadFile(extraFile, file);
// 最后插入图片
insertFn(url, url +
"?vframe/jpg/offset/0");
}
catch (err) { }
},
},
},
}} onCreated={setEditor} onChange={(editor) => {
setHtml(editor.getHtml());
const headers = editor.getElemsByTypePrefix("header");
const tocItems = headers.map((header) => {
const text = SlateNode.string(header);
const { id, type } = header;
return {
text,
level: parseInt(type.substring(6)),
id,
};
});
setToc([...tocItems]);
}} style={{
minHeight: '100%',
}} mode="default"/>
}} onCreated={setEditor} onChange={(editor) => {
setHtml(editor.getHtml());
const headers = editor.getElemsByTypePrefix("header");
const tocItems = headers.map((header) => {
const text = SlateNode.string(header);
const { id, type } = header;
return {
text,
level: parseInt(type.substring(6)),
id,
};
});
setToc([...tocItems]);
}} style={{
minHeight: '100%',
}} mode="default"/>}
</div>
</div>
</div>
{tocPosition === "right" ? (<TocView toc={toc} showToc={showToc} tocPosition="right" setShowToc={setShowToc}
// highlightBgColor={highlightBgColor}
activeColor={activeColor} scrollId={containerId} tocWidth={tocWidth} tocHeight={tocHeight}/>) : null}
{tocPosition === "right" ? (<TocView toc={toc} showToc={showToc} tocPosition="right" setShowToc={setShowToc} activeColor={activeColor} scrollId={containerId} tocWidth={tocWidth} tocHeight={tocHeight}/>) : null}
</div>
</div>);
}

View File

@ -30,7 +30,7 @@ function customCheckImageFn(src, alt, url) {
}
export default function Render(props) {
const { methods, data } = props;
const { t, setEditor, check, uploadFile, update, setHtml, gotoPreview, clearContentTip, } = methods;
const { t, setMessage, setEditor, check, uploadFile, update, setHtml, gotoPreview, clearContentTip, } = methods;
const { oakId, oakFullpath, id, content, editor, origin, tocPosition = 'none', highlightBgColor, activeColor, scrollId, tocWidth, tocHeight, height = 600, html, oakLoading, oakExecuting, } = data;
const [articleId, setArticleId] = useState('');
const [toc, setToc] = useState([]);
@ -98,7 +98,12 @@ export default function Render(props) {
// 最后插入图片
insertFn(url, extraFile.filename);
}
catch (err) { }
catch (err) {
setMessage({
type: "error",
content: err.message,
});
}
},
},
uploadVideo: {
@ -130,7 +135,12 @@ export default function Render(props) {
insertFn(url, url +
"?vframe/jpg/offset/0");
}
catch (err) { }
catch (err) {
setMessage({
type: "error",
content: err.message,
});
}
},
},
},

View File

@ -68,7 +68,7 @@ const i18ns = [
"name": "请输入文章标题",
"content": "请输入文章内容..."
},
"chcek": {
"check": {
"no name": "请填写文章标题!",
"no content": "请填写文章内容!"
}

View File

@ -70,7 +70,7 @@ const i18ns = [
"name": "请输入文章标题",
"content": "请输入文章内容..."
},
"chcek": {
"check": {
"no name": "请填写文章标题!",
"no content": "请填写文章内容!"
}

View File

@ -1,6 +1,5 @@
import { IDomEditor } from '@wangeditor/editor';
import { EntityDict } from '../../../oak-app-domain';
import { OakRowInconsistencyException } from 'oak-domain/lib/types';
export default OakComponent({
entity: 'article',
@ -25,7 +24,6 @@ export default OakComponent({
data: {
editor: null as IDomEditor | null,
html: '',
contentTip: false,
},
properties: {
articleMenuId: '',
@ -44,14 +42,14 @@ export default OakComponent({
next.editor.setHtml(next.content);
}
},
oakId(prev, next) {
if (prev.oakId !== next.oakId) {
const { editor } = this.state;
if (editor == null) return;
editor.destroy();
this.setEditor(null);
}
},
// oakId(prev, next) {
// if (prev.oakId !== next.oakId) {
// const { editor } = this.state;
// if (editor == null) return;
// editor.destroy();
// this.setEditor(null);
// }
// },
name(prev, next) {
if (prev.name !== next.name) {
window.document.title = next.name ?? '';
@ -61,7 +59,7 @@ export default OakComponent({
lifetimes: {
async ready() {
const { oakId, articleMenuId } = this.props;
if (!oakId) {
if (this.isCreation()) {
if (articleMenuId) {
this.update({
articleMenuId,
@ -98,11 +96,6 @@ export default OakComponent({
editor,
});
},
clearContentTip() {
this.setState({
contentTip: false,
});
},
async check() {
if (
this.state.name &&

View File

@ -7,7 +7,7 @@
"name": "请输入文章标题",
"content": "请输入文章内容..."
},
"chcek": {
"check": {
"no name": "请填写文章标题!",
"no content": "请填写文章内容!"
}

View File

@ -56,7 +56,6 @@ export default function Render(
editor: any;
content?: string;
origin?: null | EntityDict['extraFile']['Schema']['origin'];
contentTip: boolean;
articleMenuId: string;
oakId: string;
tocPosition: 'none' | 'left' | 'right';
@ -78,7 +77,6 @@ export default function Render(
extraFile: EntityDict['extraFile']['CreateOperationData'],
file: File
) => Promise<string>;
clearContentTip: () => void;
gotoPreview: (content?: string, title?: string) => void;
}
>
@ -86,13 +84,13 @@ export default function Render(
const { methods, data } = props;
const {
t,
setMessage,
setEditor,
check,
uploadFile,
update,
setHtml,
gotoPreview,
clearContentTip,
} = methods;
const {
oakId,
@ -194,14 +192,6 @@ export default function Render(
[Style.editorExternalContainer]: !!scrollId,
})}
>
{data.contentTip && (
<Alert
type="info"
message={t("tips.content")}
closable
onClose={() => clearContentTip()}
/>
)}
<div className={Style.titleContainer}>
<Input
onChange={(e) => {
@ -215,13 +205,12 @@ export default function Render(
placeholder={t('placeholder.name')}
size="large"
maxLength={32}
// suffix={`${(data.name || "").length}/32`}
showCount
className={Style.titleInput}
/>
</div>
<div>
<Editor
{!!articleId && <Editor
defaultConfig={{
autoFocus: true,
placeholder: t('placeholder.content'),
@ -235,21 +224,9 @@ export default function Render(
) {
// TS 语法
// file 即选中的文件
const { name, size, type } =
file;
const extension =
name.substring(
name.lastIndexOf(
"."
) + 1
);
const filename =
name.substring(
0,
name.lastIndexOf(
"."
)
);
const { name, size, type } = file;
const extension = name.substring(name.lastIndexOf(".") + 1);
const filename = name.substring(0, name.lastIndexOf("."));
const extraFile = {
entity: "article",
entityId: oakId || articleId,
@ -267,17 +244,15 @@ export default function Render(
try {
// 自己实现上传,并得到图片 url alt href
const url =
await uploadFile(
extraFile,
file
);
const url = await uploadFile(extraFile, file);
// 最后插入图片
insertFn(
url,
extraFile.filename
);
} catch (err) { }
insertFn(url, extraFile.filename);
} catch (err: any) {
setMessage({
type: "error",
content: err.message,
})
}
},
},
uploadVideo: {
@ -288,21 +263,9 @@ export default function Render(
) {
// TS 语法
// file 即选中的文件
const { name, size, type } =
file;
const extension =
name.substring(
name.lastIndexOf(
"."
) + 1
);
const filename =
name.substring(
0,
name.lastIndexOf(
"."
)
);
const { name, size, type } = file;
const extension = name.substring(name.lastIndexOf(".") + 1);
const filename = name.substring(0, name.lastIndexOf("."));
const extraFile = {
entity: "article",
entityId: oakId || articleId,
@ -320,18 +283,15 @@ export default function Render(
try {
// 自己实现上传,并得到图片 url alt href
const url =
await uploadFile(
extraFile,
file
);
const url = await uploadFile(extraFile, file);
// 最后插入图片
insertFn(
url,
url +
"?vframe/jpg/offset/0"
);
} catch (err) { }
insertFn(url, url + "?vframe/jpg/offset/0");
} catch (err: any) {
setMessage({
type: "error",
content: err.message,
})
}
},
},
},
@ -359,7 +319,7 @@ export default function Render(
minHeight: '100%',
}}
mode="default"
/>
/>}
</div>
</div>
</div>
@ -369,7 +329,6 @@ export default function Render(
showToc={showToc}
tocPosition="right"
setShowToc={setShowToc}
// highlightBgColor={highlightBgColor}
activeColor={activeColor}
scrollId={containerId}
tocWidth={tocWidth}

View File

@ -84,6 +84,7 @@ export default function Render(
const { methods, data } = props;
const {
t,
setMessage,
setEditor,
check,
uploadFile,
@ -198,21 +199,9 @@ export default function Render(
) {
// TS 语法
// file 即选中的文件
const { name, size, type } =
file;
const extension =
name.substring(
name.lastIndexOf(
"."
) + 1
);
const filename =
name.substring(
0,
name.lastIndexOf(
"."
)
);
const { name, size, type } = file;
const extension = name.substring(name.lastIndexOf(".") + 1);
const filename = name.substring(0, name.lastIndexOf("."));
const extraFile = {
entity: "article",
entityId: oakId || articleId,
@ -240,7 +229,12 @@ export default function Render(
url,
extraFile.filename
);
} catch (err) { }
} catch (err: any) {
setMessage({
type: "error",
content: err.message,
})
}
},
},
uploadVideo: {
@ -251,21 +245,9 @@ export default function Render(
) {
// TS 语法
// file 即选中的文件
const { name, size, type } =
file;
const extension =
name.substring(
name.lastIndexOf(
"."
) + 1
);
const filename =
name.substring(
0,
name.lastIndexOf(
"."
)
);
const { name, size, type } = file;
const extension = name.substring(name.lastIndexOf(".") + 1);
const filename = name.substring(0, name.lastIndexOf("."));
const extraFile = {
entity: "article",
entityId: oakId || articleId,
@ -294,7 +276,12 @@ export default function Render(
url +
"?vframe/jpg/offset/0"
);
} catch (err) { }
} catch (err: any) {
setMessage({
type: "error",
content: err.message,
})
}
},
},
},

View File

@ -70,7 +70,7 @@ const i18ns: I18n[] = [
"name": "请输入文章标题",
"content": "请输入文章内容..."
},
"chcek": {
"check": {
"no name": "请填写文章标题!",
"no content": "请填写文章内容!"
}