extraFile2 改 extraFile

This commit is contained in:
Wang Kejun 2023-11-06 18:48:49 +08:00
parent 8467b45c93
commit a1bc6f9493
43 changed files with 776 additions and 1280 deletions

View File

@ -70,18 +70,8 @@ export default OakComponent({
},
},
methods: {
// async addExtraFile(
// extraFile: EntityDict['extraFile']['CreateSingle']['data']
// ) {
// const result = await this.features.cache.operate('extraFile', {
// action: 'create',
// data: extraFile,
// id: generateNewId(),
// });
// return result;
// },
async uploadFile(extraFile) {
const result = await this.features.extraFile.createAndUpload(extraFile, extraFile.extra1);
async uploadFile(extraFile, file) {
const result = await this.features.extraFile.autoUpload(extraFile, file);
return result;
},
setEditor(editor) {

View File

@ -1,7 +1,7 @@
import "@wangeditor/editor/dist/css/style.css";
import { EntityDict } from "./../../../oak-app-domain";
import { WebComponentProps } from "oak-frontend-base";
export default function Render(props: WebComponentProps<EntityDict, "article", false, {
export default function Render(props: WebComponentProps<EntityDict, 'article', false, {
id: string;
name: string;
editor: any;
@ -19,11 +19,7 @@ export default function Render(props: WebComponentProps<EntityDict, "article", f
setEditor: (editor: any) => void;
check: () => void;
preview: () => void;
addExtraFile: (file: EntityDict["extraFile"]["CreateSingle"]["data"]) => Promise<void>;
uploadFile: (file: EntityDict["extraFile"]["CreateSingle"]["data"]) => Promise<{
bucket: string;
url: string;
}>;
uploadFile: (extraFile: EntityDict['extraFile']['CreateSingle']['data'], file: File) => Promise<string>;
clearContentTip: () => void;
gotoPreview: (content?: string, title?: string) => void;
}>): import("react/jsx-runtime").JSX.Element;

View File

@ -27,17 +27,17 @@ function customCheckImageFn(src, alt, url) {
}
export default function Render(props) {
const { methods: method, data } = props;
const { t, setEditor, check, preview, addExtraFile, uploadFile, update, setHtml, gotoPreview, } = method;
const { id, content, editor, origin1, oakFullpath, html, oakId, articleMenuId, changeIsEdit } = data;
const [articleId, setArticleId] = useState("");
const { t, setEditor, check, preview, uploadFile, update, setHtml, gotoPreview, } = method;
const { id, content, editor, origin1, oakFullpath, html, oakId, articleMenuId, changeIsEdit, } = data;
const [articleId, setArticleId] = useState('');
useEffect(() => {
if (id) {
setArticleId(id);
}
}, [id]);
return (_jsxs("div", { className: Style.container, children: [_jsx("div", { style: { width: 'calc(100% - 20px)' }, children: _jsx(Toolbar, { editor: editor, defaultConfig: toolbarConfig, mode: "default" }) }), _jsxs(Row, { children: [_jsx(Col, { flex: 4 }), _jsx(Col, { flex: 16, children: _jsx("div", { className: Style.content, children: _jsxs("div", { className: Style.editorContainer, children: [data.contentTip && (_jsx(Alert, { type: "info", message: t("tips.content"), closable: true, onClose: () => method.clearContentTip() })), _jsx("div", { className: Style.titleContainer, children: _jsx(Input, { onChange: (e) => update({ name: e.target.value }), value: data.name, placeholder: "请输入文章标题", size: "large", maxLength: 32, suffix: `${[...(data.name || "")].length}/32`, className: Style.titleInput }) }), _jsx("div", { className: Style.editorContent, children: _jsx(Editor, { defaultConfig: {
return (_jsxs("div", { className: Style.container, children: [_jsx("div", { style: { width: 'calc(100% - 20px)' }, children: _jsx(Toolbar, { editor: editor, defaultConfig: toolbarConfig, mode: "default" }) }), _jsxs(Row, { children: [_jsx(Col, { flex: 4 }), _jsx(Col, { flex: 16, children: _jsx("div", { className: Style.content, children: _jsxs("div", { className: Style.editorContainer, children: [data.contentTip && (_jsx(Alert, { type: "info", message: t('tips.content'), closable: true, onClose: () => method.clearContentTip() })), _jsx("div", { className: Style.titleContainer, children: _jsx(Input, { onChange: (e) => update({ name: e.target.value }), value: data.name, placeholder: '请输入文章标题', size: "large", maxLength: 32, suffix: `${[...(data.name || '')].length}/32`, className: Style.titleInput }) }), _jsx("div", { className: Style.editorContent, children: _jsx(Editor, { defaultConfig: {
autoFocus: true,
placeholder: "请输入文章内容...",
placeholder: '请输入文章内容...',
MENU_CONF: {
checkImage: customCheckImageFn,
uploadImage: {
@ -46,33 +46,28 @@ export default function Render(props) {
// TS 语法
// file 即选中的文件
const { name, size, type } = file;
const extension = name.substring(name.lastIndexOf(".") + 1);
const filename = name.substring(0, name.lastIndexOf("."));
const extension = name.substring(name.lastIndexOf('.') + 1);
const filename = name.substring(0, name.lastIndexOf('.'));
const extraFile = {
entity: "article",
entity: 'article',
entityId: articleId,
extra1: file,
origin: origin1,
type: "image",
tag1: "source",
type: 'image',
tag1: 'source',
objectId: generateNewId(),
filename,
size,
extension,
bucket: "",
bucket: '',
id: generateNewId(),
};
try {
// 自己实现上传,并得到图片 url alt href
const { url, bucket } = await uploadFile(extraFile);
extraFile.bucket = bucket;
extraFile.extra1 = null;
// await addExtraFile(extraFile);
const url = await uploadFile(extraFile, file);
// 最后插入图片
insertFn("http://" + url, extraFile.filename);
}
catch (err) {
insertFn(url, extraFile.filename);
}
catch (err) { }
},
},
uploadVideo: {
@ -81,30 +76,27 @@ export default function Render(props) {
// TS 语法
// file 即选中的文件
const { name, size, type } = file;
const extension = name.substring(name.lastIndexOf(".") + 1);
const filename = name.substring(0, name.lastIndexOf("."));
const extension = name.substring(name.lastIndexOf('.') + 1);
const filename = name.substring(0, name.lastIndexOf('.'));
const extraFile = {
entity: "article",
entity: 'article',
entityId: articleId,
extra1: file,
origin: origin1,
type: "video",
tag1: "source",
type: 'video',
tag1: 'source',
objectId: generateNewId(),
filename,
size,
extension,
bucket: "",
bucket: '',
id: generateNewId(),
};
try {
// 自己实现上传,并得到图片 url alt href
const { url, bucket } = await uploadFile(extraFile);
extraFile.bucket = bucket;
extraFile.extra1 = null;
await addExtraFile(extraFile);
const url = await uploadFile(extraFile, file);
// 最后插入图片
insertFn("http://" + url, "http://" + url + "?vframe/jpg/offset/0");
insertFn(url, url +
'?vframe/jpg/offset/0');
}
catch (err) { }
},
@ -114,7 +106,8 @@ export default function Render(props) {
setHtml(editorDom.getHtml());
}, style: {
minHeight: 440,
}, mode: "default" }) }), _jsx("div", { className: Style.footer, children: _jsx(Row, { align: "middle", children: _jsx(Col, { flex: "none", children: _jsxs(Space, { children: [_jsx(Button, { disabled: !data.oakDirty || data.oakExecuting, type: "primary", onClick: () => {
}, mode: "default" }) }), _jsx("div", { className: Style.footer, children: _jsx(Row, { align: "middle", children: _jsx(Col, { flex: "none", children: _jsxs(Space, { children: [_jsx(Button, { disabled: !data.oakDirty ||
data.oakExecuting, type: "primary", onClick: () => {
check();
}, children: "\u4FDD\u5B58" }), _jsxs(Button, { onClick: () => {
gotoPreview(content, data.name);

View File

@ -1,5 +1,4 @@
import { generateNewId } from 'oak-domain/lib/utils/uuid';
import { OakUnloggedInException } from 'oak-domain/lib/types';
export default OakComponent({
isList: true,
properties: {
@ -49,26 +48,6 @@ export default OakComponent({
this.triggerEvent('statuschange', e);
}
},
async addExtraFile(extraFile) {
try {
const result = await this.features.cache.operate('extraFile', {
action: 'create',
data: extraFile,
id: generateNewId(),
});
return result;
}
catch (error) {
if (error.constructor.name ===
OakUnloggedInException.name) {
this.navigateTo({
url: '/login',
});
return;
}
throw error;
}
},
async onPickMp(event) {
if (process.env.OAK_PLATFORM === 'wechatMp') {
const { mediaType } = event.currentTarget.dataset;
@ -90,7 +69,6 @@ export default OakComponent({
const extension = tempFilePath.substring(tempFilePath.lastIndexOf('.') + 1);
const filename = tempFilePath.substring(0, tempFilePath.lastIndexOf('.'));
const extraFile = {
extra1: tempFilePath,
origin: 'qiniu',
type: 'image',
tag1: this.props.tag1 || 'editorImg',
@ -104,11 +82,11 @@ export default OakComponent({
entityId: this.props.entityId,
bucket: '',
id: generateNewId(),
uploadState: 'uploading'
};
const { url } = await this.features.extraFile.createAndUpload(extraFile, extraFile.extra1);
// await this.addExtraFile(extraFile);
const url = await this.features.extraFile.autoUpload(extraFile, tempFilePath);
this.editorCtx.insertImage({
src: 'http://' + url,
src: url,
});
}
}

View File

@ -20,10 +20,10 @@ export default OakComponent({
sort: 1,
applicationId: 1,
},
features: ['extraFile2'],
features: ['extraFile'],
formData({ data: extraFiles, features }) {
const avatar = extraFiles?.filter((ele) => !ele.$$deleteAt$$ && ele.tag1 === 'avatar')[0];
const avatarUrl = features.extraFile2.getUrl(avatar);
const avatarUrl = features.extraFile.getUrl(avatar);
return {
avatar,
avatarUrl,
@ -117,7 +117,7 @@ export default OakComponent({
};
// 如果autoUpload
if (autoUpload) {
await this.features.extraFile2.autoUpload(updateData, extra1);
await this.features.extraFile.autoUpload(updateData, extra1);
if (avatar) {
this.removeItem(avatar.id);
this.execute();
@ -128,7 +128,7 @@ export default OakComponent({
if (avatar) {
this.removeItem(avatar.id);
}
this.features.extraFile2.addLocalFile(id, extra1);
this.features.extraFile.addLocalFile(id, extra1);
}
},
},

View File

@ -2,7 +2,7 @@ import assert from 'assert';
export default OakComponent({
formData({ features }) {
const ids = this.getEfIds();
const states = ids.map((id) => features.extraFile2.getFileState(id));
const states = ids.map((id) => features.extraFile.getFileState(id));
let state = 'uploaded';
states.forEach((ele) => {
if (ele) {
@ -60,7 +60,7 @@ export default OakComponent({
assert(v[attr] instanceof Array);
const [e2, fk2] = rel;
if (e2 === 'extraFile') {
efIds.push(...(v[attr].map((ele) => ele.id)));
efIds.push(...v[attr].map((ele) => ele.id));
}
else {
v[attr].forEach((ele) => getRecursive(e2, ele));
@ -69,7 +69,7 @@ export default OakComponent({
}
};
if (value instanceof Array) {
value.forEach(ele => getRecursive(entity, ele));
value.forEach((ele) => getRecursive(entity, ele));
}
getRecursive(entity, value);
return efIds;
@ -81,11 +81,11 @@ export default OakComponent({
}
const promises = [];
ids.forEach((id) => {
const fileState = this.features.extraFile2.getFileState(id);
const fileState = this.features.extraFile.getFileState(id);
if (fileState) {
const { state } = fileState;
if (['local', 'failed'].includes(state)) {
promises.push(this.features.extraFile2.upload(id));
promises.push(this.features.extraFile.upload(id));
}
}
});
@ -117,5 +117,5 @@ export default OakComponent({
}
},
},
features: ['extraFile2'],
features: ['extraFile'],
});

View File

@ -1,7 +1,7 @@
import { WebComponentProps } from 'oak-frontend-base';
import { ButtonProps } from 'antd-mobile';
import { EntityDict } from '../../../oak-app-domain';
import { FileState } from '../../../features/extraFile2';
import { FileState } from '../../../features/extraFile';
export default function render(props: WebComponentProps<EntityDict, any, true, {
state: FileState;
size?: ButtonProps['size'];

View File

@ -1,7 +1,7 @@
import { WebComponentProps } from 'oak-frontend-base';
import { ButtonProps } from 'antd';
import { EntityDict } from '../../../oak-app-domain';
import { FileState } from '../../../features/extraFile2';
import { FileState } from '../../../features/extraFile';
export default function render(props: WebComponentProps<EntityDict, any, true, {
state: FileState;
size?: ButtonProps['size'];

View File

@ -209,7 +209,7 @@ export default OakComponent({
extra1: null,
uploadState: 'uploading',
}));
this.features.extraFile2.addLocalFile(id, file);
this.features.extraFile.addLocalFile(id, file);
},
async myUpdateItem(params) {
const { file } = this.state;

View File

@ -20,7 +20,7 @@ export default OakComponent({
isBridge: 1,
uploadState: 1,
},
features: ['extraFile2'],
features: ['extraFile'],
formData({ data, features }) {
let files = data?.sort((ele1, ele2) => ele1.sort - ele2.sort);
if (this.props.tag1) {
@ -30,9 +30,9 @@ export default OakComponent({
files = files?.filter((ele) => ele?.tag2 === this.props.tag2);
}
const files2 = files.map((ele) => {
const url = features.extraFile2.getUrl(ele);
const thumbUrl = features.extraFile2.getUrl(ele, this.props.style);
const fileName = features.extraFile2.getFileName(ele);
const url = features.extraFile.getUrl(ele);
const thumbUrl = features.extraFile.getUrl(ele, this.props.style);
const fileName = features.extraFile.getFileName(ele);
return {
url,
thumbUrl,

View File

@ -1,5 +1,5 @@
import { EntityDict } from '../../../oak-app-domain';
import { FileState } from '../../../features/extraFile2';
import { FileState } from '../../../features/extraFile';
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/types/Entity';
import { ReactComponentProps } from 'oak-frontend-base/lib/types/Page';
type ExtraFile = EntityDict['extraFile']['OpSchema'];

View File

@ -95,7 +95,7 @@ export default OakComponent({
}
},
},
features: ['extraFile2'],
features: ['extraFile'],
formData({ data, features }) {
let files = data
?.filter((ele) => !ele.$$deleteAt$$)
@ -107,10 +107,10 @@ export default OakComponent({
files = files?.filter((ele) => ele?.tag2 === this.props.tag2);
}
const files2 = files.map((ele) => {
const url = features.extraFile2.getUrl(ele);
const thumbUrl = features.extraFile2.getUrl(ele, 'thumbnail');
const fileState = features.extraFile2.getFileState(ele.id);
const fileName = features.extraFile2.getFileName(ele);
const url = features.extraFile.getUrl(ele);
const thumbUrl = features.extraFile.getUrl(ele, 'thumbnail');
const fileState = features.extraFile.getFileState(ele.id);
const fileName = features.extraFile.getFileName(ele);
return {
url,
thumbUrl,
@ -127,7 +127,7 @@ export default OakComponent({
methods: {
onRemove(file) {
this.removeItem(file.id);
this.features.extraFile2.removeLocalFiles([file.id]);
this.features.extraFile.removeLocalFiles([file.id]);
},
addExtraFileInner(options, file) {
const { type, origin = 'qiniu', // 默认qiniu
@ -154,7 +154,7 @@ export default OakComponent({
sort,
uploadState: 'uploading',
});
this.features.extraFile2.addLocalFile(id, file);
this.features.extraFile.addLocalFile(id, file);
},
addFileByWeb(file) {
const { size, type, name } = file;

View File

@ -83,13 +83,13 @@ export default OakComponent({
userId: session?.userId,
userMobile: session?.user?.mobile$user &&
session?.user?.mobile$user[0]?.mobile,
userAvatar: features.extraFile2.getUrl(session?.user?.extraFile$entity &&
userAvatar: features.extraFile.getUrl(session?.user?.extraFile$entity &&
session?.user?.extraFile$entity[0]),
};
if (type === 'image') {
const extraFile$entity = sessionMessage?.extraFile$entity;
Object.assign(newSessionMessage, {
picUrl: features.extraFile2.getUrl(extraFile$entity && extraFile$entity[0]),
picUrl: features.extraFile.getUrl(extraFile$entity && extraFile$entity[0]),
});
}
return newSessionMessage;

View File

@ -285,7 +285,7 @@ export default OakComponent({
entity: 'sessionMessage',
entityId: sessionMessageId,
};
await this.features.extraFile2.autoUpload(extraFile, originFileObj);
await this.features.extraFile.autoUpload(extraFile, originFileObj);
try {
this.updateItem({
createTime: Date.now(),
@ -298,12 +298,12 @@ export default OakComponent({
// },
// ],
}, sessionMessageId);
// this.features.extraFile2.addLocalFile(
// this.features.extraFile.addLocalFile(
// extraFile?.id,
// originFileObj
// );
await this.execute(undefined, false);
// this.features.extraFile2.upload(extraFile?.id);
// this.features.extraFile.upload(extraFile?.id);
this.pageScroll('comment');
this.createItem();
}
@ -368,12 +368,12 @@ export default OakComponent({
// },
// ],
// } as EntityDict['sessionMessage']['CreateSingle']['data']);
// this.features.extraFile2.addLocalFile(
// this.features.extraFile.addLocalFile(
// extraFile?.id,
// originFileObj
// );
// await this.execute(undefined, false);
// this.features.extraFile2.upload(extraFile?.id);
// this.features.extraFile.upload(extraFile?.id);
// } catch (err) {
// throw err;
// }

View File

@ -1,5 +1,6 @@
import { Feature } from 'oak-frontend-base';
import { Cache } from 'oak-frontend-base/es/features/cache';
import { RunningTree } from 'oak-frontend-base/es/features/runningTree';
import { Locales } from 'oak-frontend-base/es/features/locales';
import { CommonAspectDict } from 'oak-common-aspect';
import AspectDict from '../aspects/AspectDict';
@ -7,16 +8,25 @@ import { EntityDict } from '../oak-app-domain';
import { BackendRuntimeContext } from '../context/BackendRuntimeContext';
import { FrontendRuntimeContext } from '../context/FrontendRuntimeContext';
import { Application } from './application';
export type FileState = 'local' | 'uploading' | 'uploaded' | 'failed';
export declare class ExtraFile<ED extends EntityDict, Cxt extends BackendRuntimeContext<ED>, FrontCxt extends FrontendRuntimeContext<ED, Cxt, AD>, AD extends AspectDict<ED, Cxt> & CommonAspectDict<ED, Cxt>> extends Feature {
private cache;
private application;
private locales;
constructor(cache: Cache<ED, Cxt, FrontCxt, AD & CommonAspectDict<ED, Cxt>>, application: Application<ED, Cxt, FrontCxt, AD>, locales: Locales<ED, Cxt, FrontCxt, AD>);
createAndUpload(extraFile: EntityDict['extraFile']['CreateSingle']['data'], file: string | File): Promise<{
url: string;
}>;
upload(extraFile: EntityDict['extraFile']['CreateSingle']['data'], file: string | File): Promise<void>;
private files;
private runningTree;
constructor(cache: Cache<ED, Cxt, FrontCxt, AD & CommonAspectDict<ED, Cxt>>, application: Application<ED, Cxt, FrontCxt, AD>, locales: Locales<ED, Cxt, FrontCxt, AD>, runningTree: RunningTree<ED, Cxt, FrontCxt, AD>);
addLocalFile(id: string, file: File | string): void;
removeLocalFiles(ids: string[]): void;
upload(id: string): Promise<void>;
uploadCommit(efPaths: string[], oakFullpath: string): Promise<void>;
getUrl(extraFile?: EntityDict['extraFile']['OpSchema'] | EntityDict['extraFile']['Schema'] | null, style?: string): string;
getFileState(id: string): {
state: FileState;
percentage?: number;
} | undefined;
getFileName(extraFile: EntityDict['extraFile']['OpSchema']): string;
formatBytes(size: number): string;
autoUpload(extraFile: EntityDict['extraFile']['OpSchema'], file: File | string): Promise<string>;
private uploadToAspect;
}

View File

@ -3,115 +3,133 @@ import { Upload } from 'oak-frontend-base/es/utils/upload';
import { bytesToSize, getFileURL } from '../utils/extraFile';
import { assert } from 'oak-domain/lib/utils/assert';
import { getCos } from '../utils/cos';
import { generateNewId } from 'oak-domain/lib/utils/uuid';
import { unset } from 'oak-domain/lib/utils/lodash';
import { generateNewId, generateNewIdAsync } from 'oak-domain';
import { extraFileProjection } from '../types/Projection';
export class ExtraFile extends Feature {
cache;
application;
locales;
constructor(cache, application, locales) {
files;
runningTree;
constructor(cache, application, locales, runningTree) {
super();
this.cache = cache;
this.application = application;
this.locales = locales;
this.files = {};
this.runningTree = runningTree;
}
async createAndUpload(extraFile, file) {
await this.cache.operate('extraFile', {
action: 'create',
data: extraFile,
id: generateNewId(),
});
await this.upload(extraFile, file);
const application = this.application.getApplication();
return {
url: this.getUrl(extraFile),
addLocalFile(id, file) {
assert(!this.files[id]);
this.files[id] = {
file,
state: 'local',
};
this.publish();
}
async upload(extraFile, file) {
const { id, origin } = extraFile;
assert(origin, '未设置上传方式');
const [extraFileData] = this.cache.get('extraFile', {
data: {
origin: 1,
type: 1,
bucket: 1,
objectId: 1,
tag1: 1,
tag2: 1,
filename: 1,
md5: 1,
entity: 1,
entityId: 1,
extra1: 1,
extension: 1,
size: 1,
sort: 1,
fileType: 1,
isBridge: 1,
uploadState: 1,
uploadMeta: 1,
},
removeLocalFiles(ids) {
ids.forEach((id) => unset(this.files, id));
this.publish();
}
async upload(id) {
const [extraFile] = this.cache.get('extraFile', {
data: extraFileProjection,
filter: {
id,
},
});
assert(extraFile && extraFile.uploadState === 'uploading');
const item = this.files[id];
assert(item);
const { file, state } = item;
assert(['local', 'failed'].includes(state));
item.state = 'uploading';
item.percentage = 0;
const up = new Upload();
try {
const cos = getCos(origin);
await cos.upload(extraFileData, up.uploadFile, file, async () => undefined);
await this.cache.operate('extraFile', {
action: 'update',
data: {
uploadState: 'success',
},
filter: {
id,
},
id: generateNewId(),
});
const cos = getCos(extraFile.origin);
await cos.upload(extraFile, up.uploadFile, file, this.uploadToAspect.bind(this));
if (!cos.autoInform()) {
await this.cache.exec('operate', {
entity: 'extraFile',
operation: {
id: await generateNewIdAsync(),
action: 'update',
data: {
uploadState: 'success',
},
},
});
}
item.state = 'uploaded';
item.percentage = undefined;
this.publish();
}
catch (err) {
await this.cache.operate('extraFile', {
action: 'update',
data: {
uploadState: 'failed',
},
filter: {
id,
},
id: generateNewId(),
});
item.state = 'failed';
item.percentage = undefined;
this.publish();
throw err;
}
}
async uploadCommit(efPaths, oakFullpath) {
assert(false, '方法已经废弃');
assert(efPaths && efPaths.length > 0);
let ids = [];
if (oakFullpath) {
ids = efPaths
.map((path) => {
const path2 = path ? `${oakFullpath}.${path}` : oakFullpath;
const data = this.runningTree.getFreshValue(path2);
assert(data, `efPath为${path}的路径上取不到extraFile数据请设置正确的相对路径`);
return data.map((ele) => ele.id);
})
.flat()
.filter((ele) => !!ele);
}
assert(ids.length > 0);
const promises = [];
ids.forEach((id) => {
const fileState = this.getFileState(id);
if (fileState) {
const { state } = fileState;
if (['local', 'failed'].includes(state)) {
promises.push(this.upload(id));
}
}
});
if (promises.length > 0) {
await Promise.all(promises);
}
}
getUrl(extraFile, style) {
if (!extraFile) {
return '';
}
let url;
if (extraFile?.isBridge && extraFile?.extra1) {
if (typeof extraFile?.extra1 === 'string') {
url = this.locales.makeBridgeUrl(extraFile?.extra1);
return url;
}
return this.locales.makeBridgeUrl(extraFile?.extra1);
}
if (extraFile?.extra1) {
// 有extra1就用extra1 可能File对象 可能外部链接
if (typeof extraFile?.extra1 === 'string') {
return extraFile?.extra1;
const { id } = extraFile;
if (this.files[id]) {
const { file } = this.files[id];
if (typeof file === 'string') {
return file;
}
if (extraFile?.extra1 instanceof File) {
return getFileURL(extraFile?.extra1) || '';
if (file instanceof File) {
return getFileURL(file);
}
return extraFile?.extra1 || '';
assert(false, 'the incoming file is not supported');
}
const { origin } = extraFile;
const cos = getCos(origin);
const context = this.cache.begin();
this.cache.commit();
url = cos.composeFileUrl(extraFile, context, style);
return url;
return cos.composeFileUrl(extraFile, context, style);
}
getFileState(id) {
if (this.files[id]) {
return this.files[id];
}
}
getFileName(extraFile) {
const name = extraFile.filename +
@ -121,4 +139,53 @@ export class ExtraFile extends Feature {
formatBytes(size) {
return bytesToSize(size);
}
async autoUpload(extraFile, file) {
const extraFileId = extraFile.id || generateNewId();
const applicationId = extraFile.applicationId || this.application.getApplicationId();
await this.cache.operate('extraFile', {
action: 'create',
data: Object.assign(extraFile, {
id: extraFileId,
applicationId,
}),
id: await generateNewIdAsync(),
});
const [newExtraFile] = this.cache.get('extraFile', {
data: extraFileProjection,
filter: {
id: extraFileId,
},
});
const up = new Upload();
try {
const cos = getCos(newExtraFile.origin);
await cos.upload(newExtraFile, up.uploadFile, file, this.uploadToAspect.bind(this));
return this.getUrl(newExtraFile);
}
catch (err) {
await this.cache.operate('extraFile', {
action: 'remove',
data: {},
filter: {
id: extraFileId,
},
id: await generateNewIdAsync(),
});
throw err;
}
}
// 私有
async uploadToAspect(file, name, // 文件的part name
aspectName, // 上传的aspect名
formData, // 上传的其它part参数
autoInform // 上传成功是否会自动通知server若不会则需要前台显式通知
) {
const formData2 = new FormData();
for (const key of Object.keys(formData)) {
formData2.append(key, formData[key]);
}
formData2.append(name || 'file', file);
const { result } = await this.cache.exec(aspectName, formData2);
return result;
}
}

View File

@ -1,32 +0,0 @@
import { Feature } from 'oak-frontend-base';
import { Cache } from 'oak-frontend-base/es/features/cache';
import { RunningTree } from 'oak-frontend-base/es/features/runningTree';
import { Locales } from 'oak-frontend-base/es/features/locales';
import { CommonAspectDict } from 'oak-common-aspect';
import AspectDict from '../aspects/AspectDict';
import { EntityDict } from '../oak-app-domain';
import { BackendRuntimeContext } from '../context/BackendRuntimeContext';
import { FrontendRuntimeContext } from '../context/FrontendRuntimeContext';
import { Application } from './application';
export type FileState = 'local' | 'uploading' | 'uploaded' | 'failed';
export declare class ExtraFile2<ED extends EntityDict, Cxt extends BackendRuntimeContext<ED>, FrontCxt extends FrontendRuntimeContext<ED, Cxt, AD>, AD extends AspectDict<ED, Cxt> & CommonAspectDict<ED, Cxt>> extends Feature {
private cache;
private application;
private locales;
private files;
private runningTree;
constructor(cache: Cache<ED, Cxt, FrontCxt, AD & CommonAspectDict<ED, Cxt>>, application: Application<ED, Cxt, FrontCxt, AD>, locales: Locales<ED, Cxt, FrontCxt, AD>, runningTree: RunningTree<ED, Cxt, FrontCxt, AD>);
addLocalFile(id: string, file: File | string): void;
removeLocalFiles(ids: string[]): void;
upload(id: string): Promise<void>;
uploadCommit(efPaths: string[], oakFullpath: string): Promise<void>;
getUrl(extraFile?: EntityDict['extraFile']['OpSchema'] | EntityDict['extraFile']['Schema'] | null, style?: string): string;
getFileState(id: string): {
state: FileState;
percentage?: number;
} | undefined;
getFileName(extraFile: EntityDict['extraFile']['OpSchema']): string;
formatBytes(size: number): string;
autoUpload(extraFile: EntityDict['extraFile']['OpSchema'], file: File | string): Promise<string>;
private uploadToAspect;
}

View File

@ -1,193 +0,0 @@
import { Feature } from 'oak-frontend-base';
import { Upload } from 'oak-frontend-base/es/utils/upload';
import { bytesToSize, getFileURL } from '../utils/extraFile';
import { assert } from 'oak-domain/lib/utils/assert';
import { getCos } from '../utils/cos';
import { unset } from 'oak-domain/lib/utils/lodash';
import { generateNewId, generateNewIdAsync } from 'oak-domain';
import { extraFileProjection } from '../types/Projection';
export class ExtraFile2 extends Feature {
cache;
application;
locales;
files;
runningTree;
constructor(cache, application, locales, runningTree) {
super();
this.cache = cache;
this.application = application;
this.locales = locales;
this.files = {};
this.runningTree = runningTree;
}
addLocalFile(id, file) {
assert(!this.files[id]);
this.files[id] = {
file,
state: 'local',
};
this.publish();
}
removeLocalFiles(ids) {
ids.forEach((id) => unset(this.files, id));
this.publish();
}
async upload(id) {
const [extraFile] = this.cache.get('extraFile', {
data: extraFileProjection,
filter: {
id,
},
});
assert(extraFile && extraFile.uploadState === 'uploading');
const item = this.files[id];
assert(item);
const { file, state } = item;
assert(['local', 'failed'].includes(state));
item.state = 'uploading';
item.percentage = 0;
const up = new Upload();
try {
const cos = getCos(extraFile.origin);
await cos.upload(extraFile, up.uploadFile, file, this.uploadToAspect.bind(this));
if (!cos.autoInform()) {
await this.cache.exec('operate', {
entity: 'extraFile',
operation: {
id: await generateNewIdAsync(),
action: 'update',
data: {
uploadState: 'success',
},
},
});
}
item.state = 'uploaded';
item.percentage = undefined;
this.publish();
}
catch (err) {
item.state = 'failed';
item.percentage = undefined;
this.publish();
}
}
async uploadCommit(efPaths, oakFullpath) {
assert(false, '方法已经废弃');
assert(efPaths && efPaths.length > 0);
let ids = [];
if (oakFullpath) {
ids = efPaths
.map((path) => {
const path2 = path
? `${oakFullpath}.${path}`
: oakFullpath;
const data = this.runningTree.getFreshValue(path2);
assert(data, `efPath为${path}的路径上取不到extraFile数据请设置正确的相对路径`);
return data.map((ele) => ele.id);
})
.flat()
.filter((ele) => !!ele);
}
assert(ids.length > 0);
const promises = [];
ids.forEach((id) => {
const fileState = this.getFileState(id);
if (fileState) {
const { state } = fileState;
if (['local', 'failed'].includes(state)) {
promises.push(this.upload(id));
}
}
});
if (promises.length > 0) {
await Promise.all(promises);
}
}
getUrl(extraFile, style) {
if (!extraFile) {
return '';
}
if (extraFile?.isBridge && extraFile?.extra1) {
return this.locales.makeBridgeUrl(extraFile?.extra1);
}
const { id } = extraFile;
if (this.files[id]) {
const { file } = this.files[id];
if (typeof file === 'string') {
return file;
}
if (file instanceof File) {
return getFileURL(file);
}
assert(false, 'the incoming file is not supported');
}
const { origin } = extraFile;
const cos = getCos(origin);
const context = this.cache.begin();
this.cache.commit();
return cos.composeFileUrl(extraFile, context, style);
}
getFileState(id) {
if (this.files[id]) {
return this.files[id];
}
}
getFileName(extraFile) {
const name = extraFile.filename +
(extraFile.extension ? `.${extraFile.extension}` : '');
return name;
}
formatBytes(size) {
return bytesToSize(size);
}
async autoUpload(extraFile, file) {
const extraFileId = extraFile.id || generateNewId();
const applicationId = extraFile.applicationId || this.application.getApplicationId();
await this.cache.operate('extraFile', {
action: 'create',
data: Object.assign(extraFile, {
id: extraFileId,
applicationId,
}),
id: await generateNewIdAsync(),
});
const [newExtraFile] = this.cache.get('extraFile', {
data: extraFileProjection,
filter: {
id: extraFileId,
},
});
const up = new Upload();
try {
const cos = getCos(newExtraFile.origin);
await cos.upload(newExtraFile, up.uploadFile, file, this.uploadToAspect.bind(this));
return this.getUrl(newExtraFile);
}
catch (err) {
await this.cache.operate('extraFile', {
action: 'remove',
data: {},
filter: {
id: extraFileId,
},
id: await generateNewIdAsync(),
});
throw err;
}
}
// 私有
async uploadToAspect(file, name, // 文件的part name
aspectName, // 上传的aspect名
formData, // 上传的其它part参数
autoInform // 上传成功是否会自动通知server若不会则需要前台显式通知
) {
const formData2 = new FormData();
for (const key of Object.keys(formData)) {
formData2.append(key, formData[key]);
}
formData2.append(name || 'file', file);
const { result } = await this.cache.exec(aspectName, formData2);
return result;
}
}

View File

@ -1,7 +1,6 @@
import { CommonAspectDict } from 'oak-common-aspect';
import { Token } from './token';
import { ExtraFile } from './extraFile';
import { ExtraFile2 } from './extraFile2';
import { Application } from './application';
import { Config } from './config';
import { Template } from './template';
@ -20,7 +19,6 @@ export declare function initialize<ED extends EntityDict, Cxt extends BackendRun
export type GeneralFeatures<ED extends EntityDict, Cxt extends BackendRuntimeContext<ED>, FrontCxt extends FrontendRuntimeContext<ED, Cxt, AD>, AD extends AspectDict<ED, Cxt> & CommonAspectDict<ED, Cxt>> = {
token: Token<ED, Cxt, FrontCxt, AD>;
extraFile: ExtraFile<ED, Cxt, FrontCxt, AD>;
extraFile2: ExtraFile2<ED, Cxt, FrontCxt, AD>;
application: Application<ED, Cxt, FrontCxt, AD>;
config: Config<ED, Cxt, FrontCxt, AD>;
template: Template<ED, Cxt, FrontCxt, AD>;

View File

@ -1,6 +1,5 @@
import { Token } from './token';
import { ExtraFile } from './extraFile';
import { ExtraFile2 } from './extraFile2';
import { Application } from './application';
import { Config } from './config';
import { Template } from './template';
@ -15,9 +14,7 @@ export function initialize(basicFeatures, type, domain) {
const wechatMenu = new WechatMenu(basicFeatures.cache, basicFeatures.localStorage);
const wechatPublicTag = new WechatPublicTag(basicFeatures.cache, basicFeatures.localStorage);
const userWechatPublicTag = new UserWechatPublicTag(basicFeatures.cache, basicFeatures.localStorage);
// 临时代码,合并后再删
const extraFile = new ExtraFile(basicFeatures.cache, application, basicFeatures.locales);
const extraFile2 = new ExtraFile2(basicFeatures.cache, application, basicFeatures.locales, basicFeatures.runningTree);
const extraFile = new ExtraFile(basicFeatures.cache, application, basicFeatures.locales, basicFeatures.runningTree);
const config = new Config(basicFeatures.cache);
const template = new Template(basicFeatures.cache);
const weiXinJsSdk = new WeiXinJsSdk(basicFeatures.cache, basicFeatures.localStorage, basicFeatures.environment);
@ -25,7 +22,6 @@ export function initialize(basicFeatures, type, domain) {
return {
token,
extraFile,
extraFile2,
application,
config,
template,

View File

@ -81,7 +81,7 @@ export default OakComponent({
}
},
uploadFile(extraFile) {
return this.features.extraFile.createAndUpload(extraFile, extraFile.extra1);
return this.features.extraFile.autoUpload(extraFile, extraFile.extra1);
},
setEditor(editor) {
this.setState({

View File

@ -1,5 +1,6 @@
import { Feature } from 'oak-frontend-base';
import { Cache } from 'oak-frontend-base/es/features/cache';
import { RunningTree } from 'oak-frontend-base/es/features/runningTree';
import { Locales } from 'oak-frontend-base/es/features/locales';
import { CommonAspectDict } from 'oak-common-aspect';
import AspectDict from '../aspects/AspectDict';
@ -7,16 +8,25 @@ import { EntityDict } from '../oak-app-domain';
import { BackendRuntimeContext } from '../context/BackendRuntimeContext';
import { FrontendRuntimeContext } from '../context/FrontendRuntimeContext';
import { Application } from './application';
export type FileState = 'local' | 'uploading' | 'uploaded' | 'failed';
export declare class ExtraFile<ED extends EntityDict, Cxt extends BackendRuntimeContext<ED>, FrontCxt extends FrontendRuntimeContext<ED, Cxt, AD>, AD extends AspectDict<ED, Cxt> & CommonAspectDict<ED, Cxt>> extends Feature {
private cache;
private application;
private locales;
constructor(cache: Cache<ED, Cxt, FrontCxt, AD & CommonAspectDict<ED, Cxt>>, application: Application<ED, Cxt, FrontCxt, AD>, locales: Locales<ED, Cxt, FrontCxt, AD>);
createAndUpload(extraFile: EntityDict['extraFile']['CreateSingle']['data'], file: string | File): Promise<{
url: string;
}>;
upload(extraFile: EntityDict['extraFile']['CreateSingle']['data'], file: string | File): Promise<void>;
private files;
private runningTree;
constructor(cache: Cache<ED, Cxt, FrontCxt, AD & CommonAspectDict<ED, Cxt>>, application: Application<ED, Cxt, FrontCxt, AD>, locales: Locales<ED, Cxt, FrontCxt, AD>, runningTree: RunningTree<ED, Cxt, FrontCxt, AD>);
addLocalFile(id: string, file: File | string): void;
removeLocalFiles(ids: string[]): void;
upload(id: string): Promise<void>;
uploadCommit(efPaths: string[], oakFullpath: string): Promise<void>;
getUrl(extraFile?: EntityDict['extraFile']['OpSchema'] | EntityDict['extraFile']['Schema'] | null, style?: string): string;
getFileState(id: string): {
state: FileState;
percentage?: number;
} | undefined;
getFileName(extraFile: EntityDict['extraFile']['OpSchema']): string;
formatBytes(size: number): string;
autoUpload(extraFile: EntityDict['extraFile']['OpSchema'], file: File | string): Promise<string>;
private uploadToAspect;
}

View File

@ -6,115 +6,133 @@ const upload_1 = require("oak-frontend-base/es/utils/upload");
const extraFile_1 = require("../utils/extraFile");
const assert_1 = require("oak-domain/lib/utils/assert");
const cos_1 = require("../utils/cos");
const uuid_1 = require("oak-domain/lib/utils/uuid");
const lodash_1 = require("oak-domain/lib/utils/lodash");
const oak_domain_1 = require("oak-domain");
const Projection_1 = require("../types/Projection");
class ExtraFile extends oak_frontend_base_1.Feature {
cache;
application;
locales;
constructor(cache, application, locales) {
files;
runningTree;
constructor(cache, application, locales, runningTree) {
super();
this.cache = cache;
this.application = application;
this.locales = locales;
this.files = {};
this.runningTree = runningTree;
}
async createAndUpload(extraFile, file) {
await this.cache.operate('extraFile', {
action: 'create',
data: extraFile,
id: (0, uuid_1.generateNewId)(),
});
await this.upload(extraFile, file);
const application = this.application.getApplication();
return {
url: this.getUrl(extraFile),
addLocalFile(id, file) {
(0, assert_1.assert)(!this.files[id]);
this.files[id] = {
file,
state: 'local',
};
this.publish();
}
async upload(extraFile, file) {
const { id, origin } = extraFile;
(0, assert_1.assert)(origin, '未设置上传方式');
const [extraFileData] = this.cache.get('extraFile', {
data: {
origin: 1,
type: 1,
bucket: 1,
objectId: 1,
tag1: 1,
tag2: 1,
filename: 1,
md5: 1,
entity: 1,
entityId: 1,
extra1: 1,
extension: 1,
size: 1,
sort: 1,
fileType: 1,
isBridge: 1,
uploadState: 1,
uploadMeta: 1,
},
removeLocalFiles(ids) {
ids.forEach((id) => (0, lodash_1.unset)(this.files, id));
this.publish();
}
async upload(id) {
const [extraFile] = this.cache.get('extraFile', {
data: Projection_1.extraFileProjection,
filter: {
id,
},
});
(0, assert_1.assert)(extraFile && extraFile.uploadState === 'uploading');
const item = this.files[id];
(0, assert_1.assert)(item);
const { file, state } = item;
(0, assert_1.assert)(['local', 'failed'].includes(state));
item.state = 'uploading';
item.percentage = 0;
const up = new upload_1.Upload();
try {
const cos = (0, cos_1.getCos)(origin);
await cos.upload(extraFileData, up.uploadFile, file, async () => undefined);
await this.cache.operate('extraFile', {
action: 'update',
data: {
uploadState: 'success',
},
filter: {
id,
},
id: (0, uuid_1.generateNewId)(),
});
const cos = (0, cos_1.getCos)(extraFile.origin);
await cos.upload(extraFile, up.uploadFile, file, this.uploadToAspect.bind(this));
if (!cos.autoInform()) {
await this.cache.exec('operate', {
entity: 'extraFile',
operation: {
id: await (0, oak_domain_1.generateNewIdAsync)(),
action: 'update',
data: {
uploadState: 'success',
},
},
});
}
item.state = 'uploaded';
item.percentage = undefined;
this.publish();
}
catch (err) {
await this.cache.operate('extraFile', {
action: 'update',
data: {
uploadState: 'failed',
},
filter: {
id,
},
id: (0, uuid_1.generateNewId)(),
});
item.state = 'failed';
item.percentage = undefined;
this.publish();
throw err;
}
}
async uploadCommit(efPaths, oakFullpath) {
(0, assert_1.assert)(false, '方法已经废弃');
(0, assert_1.assert)(efPaths && efPaths.length > 0);
let ids = [];
if (oakFullpath) {
ids = efPaths
.map((path) => {
const path2 = path ? `${oakFullpath}.${path}` : oakFullpath;
const data = this.runningTree.getFreshValue(path2);
(0, assert_1.assert)(data, `efPath为${path}的路径上取不到extraFile数据请设置正确的相对路径`);
return data.map((ele) => ele.id);
})
.flat()
.filter((ele) => !!ele);
}
(0, assert_1.assert)(ids.length > 0);
const promises = [];
ids.forEach((id) => {
const fileState = this.getFileState(id);
if (fileState) {
const { state } = fileState;
if (['local', 'failed'].includes(state)) {
promises.push(this.upload(id));
}
}
});
if (promises.length > 0) {
await Promise.all(promises);
}
}
getUrl(extraFile, style) {
if (!extraFile) {
return '';
}
let url;
if (extraFile?.isBridge && extraFile?.extra1) {
if (typeof extraFile?.extra1 === 'string') {
url = this.locales.makeBridgeUrl(extraFile?.extra1);
return url;
}
return this.locales.makeBridgeUrl(extraFile?.extra1);
}
if (extraFile?.extra1) {
// 有extra1就用extra1 可能File对象 可能外部链接
if (typeof extraFile?.extra1 === 'string') {
return extraFile?.extra1;
const { id } = extraFile;
if (this.files[id]) {
const { file } = this.files[id];
if (typeof file === 'string') {
return file;
}
if (extraFile?.extra1 instanceof File) {
return (0, extraFile_1.getFileURL)(extraFile?.extra1) || '';
if (file instanceof File) {
return (0, extraFile_1.getFileURL)(file);
}
return extraFile?.extra1 || '';
(0, assert_1.assert)(false, 'the incoming file is not supported');
}
const { origin } = extraFile;
const cos = (0, cos_1.getCos)(origin);
const context = this.cache.begin();
this.cache.commit();
url = cos.composeFileUrl(extraFile, context, style);
return url;
return cos.composeFileUrl(extraFile, context, style);
}
getFileState(id) {
if (this.files[id]) {
return this.files[id];
}
}
getFileName(extraFile) {
const name = extraFile.filename +
@ -124,5 +142,54 @@ class ExtraFile extends oak_frontend_base_1.Feature {
formatBytes(size) {
return (0, extraFile_1.bytesToSize)(size);
}
async autoUpload(extraFile, file) {
const extraFileId = extraFile.id || (0, oak_domain_1.generateNewId)();
const applicationId = extraFile.applicationId || this.application.getApplicationId();
await this.cache.operate('extraFile', {
action: 'create',
data: Object.assign(extraFile, {
id: extraFileId,
applicationId,
}),
id: await (0, oak_domain_1.generateNewIdAsync)(),
});
const [newExtraFile] = this.cache.get('extraFile', {
data: Projection_1.extraFileProjection,
filter: {
id: extraFileId,
},
});
const up = new upload_1.Upload();
try {
const cos = (0, cos_1.getCos)(newExtraFile.origin);
await cos.upload(newExtraFile, up.uploadFile, file, this.uploadToAspect.bind(this));
return this.getUrl(newExtraFile);
}
catch (err) {
await this.cache.operate('extraFile', {
action: 'remove',
data: {},
filter: {
id: extraFileId,
},
id: await (0, oak_domain_1.generateNewIdAsync)(),
});
throw err;
}
}
// 私有
async uploadToAspect(file, name, // 文件的part name
aspectName, // 上传的aspect名
formData, // 上传的其它part参数
autoInform // 上传成功是否会自动通知server若不会则需要前台显式通知
) {
const formData2 = new FormData();
for (const key of Object.keys(formData)) {
formData2.append(key, formData[key]);
}
formData2.append(name || 'file', file);
const { result } = await this.cache.exec(aspectName, formData2);
return result;
}
}
exports.ExtraFile = ExtraFile;

View File

@ -1,32 +0,0 @@
import { Feature } from 'oak-frontend-base';
import { Cache } from 'oak-frontend-base/es/features/cache';
import { RunningTree } from 'oak-frontend-base/es/features/runningTree';
import { Locales } from 'oak-frontend-base/es/features/locales';
import { CommonAspectDict } from 'oak-common-aspect';
import AspectDict from '../aspects/AspectDict';
import { EntityDict } from '../oak-app-domain';
import { BackendRuntimeContext } from '../context/BackendRuntimeContext';
import { FrontendRuntimeContext } from '../context/FrontendRuntimeContext';
import { Application } from './application';
export type FileState = 'local' | 'uploading' | 'uploaded' | 'failed';
export declare class ExtraFile2<ED extends EntityDict, Cxt extends BackendRuntimeContext<ED>, FrontCxt extends FrontendRuntimeContext<ED, Cxt, AD>, AD extends AspectDict<ED, Cxt> & CommonAspectDict<ED, Cxt>> extends Feature {
private cache;
private application;
private locales;
private files;
private runningTree;
constructor(cache: Cache<ED, Cxt, FrontCxt, AD & CommonAspectDict<ED, Cxt>>, application: Application<ED, Cxt, FrontCxt, AD>, locales: Locales<ED, Cxt, FrontCxt, AD>, runningTree: RunningTree<ED, Cxt, FrontCxt, AD>);
addLocalFile(id: string, file: File | string): void;
removeLocalFiles(ids: string[]): void;
upload(id: string): Promise<void>;
uploadCommit(efPaths: string[], oakFullpath: string): Promise<void>;
getUrl(extraFile?: EntityDict['extraFile']['OpSchema'] | EntityDict['extraFile']['Schema'] | null, style?: string): string;
getFileState(id: string): {
state: FileState;
percentage?: number;
} | undefined;
getFileName(extraFile: EntityDict['extraFile']['OpSchema']): string;
formatBytes(size: number): string;
autoUpload(extraFile: EntityDict['extraFile']['OpSchema'], file: File | string): Promise<string>;
private uploadToAspect;
}

View File

@ -1,197 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExtraFile2 = void 0;
const oak_frontend_base_1 = require("oak-frontend-base");
const upload_1 = require("oak-frontend-base/es/utils/upload");
const extraFile_1 = require("../utils/extraFile");
const assert_1 = require("oak-domain/lib/utils/assert");
const cos_1 = require("../utils/cos");
const lodash_1 = require("oak-domain/lib/utils/lodash");
const oak_domain_1 = require("oak-domain");
const Projection_1 = require("../types/Projection");
class ExtraFile2 extends oak_frontend_base_1.Feature {
cache;
application;
locales;
files;
runningTree;
constructor(cache, application, locales, runningTree) {
super();
this.cache = cache;
this.application = application;
this.locales = locales;
this.files = {};
this.runningTree = runningTree;
}
addLocalFile(id, file) {
(0, assert_1.assert)(!this.files[id]);
this.files[id] = {
file,
state: 'local',
};
this.publish();
}
removeLocalFiles(ids) {
ids.forEach((id) => (0, lodash_1.unset)(this.files, id));
this.publish();
}
async upload(id) {
const [extraFile] = this.cache.get('extraFile', {
data: Projection_1.extraFileProjection,
filter: {
id,
},
});
(0, assert_1.assert)(extraFile && extraFile.uploadState === 'uploading');
const item = this.files[id];
(0, assert_1.assert)(item);
const { file, state } = item;
(0, assert_1.assert)(['local', 'failed'].includes(state));
item.state = 'uploading';
item.percentage = 0;
const up = new upload_1.Upload();
try {
const cos = (0, cos_1.getCos)(extraFile.origin);
await cos.upload(extraFile, up.uploadFile, file, this.uploadToAspect.bind(this));
if (!cos.autoInform()) {
await this.cache.exec('operate', {
entity: 'extraFile',
operation: {
id: await (0, oak_domain_1.generateNewIdAsync)(),
action: 'update',
data: {
uploadState: 'success',
},
},
});
}
item.state = 'uploaded';
item.percentage = undefined;
this.publish();
}
catch (err) {
item.state = 'failed';
item.percentage = undefined;
this.publish();
}
}
async uploadCommit(efPaths, oakFullpath) {
(0, assert_1.assert)(false, '方法已经废弃');
(0, assert_1.assert)(efPaths && efPaths.length > 0);
let ids = [];
if (oakFullpath) {
ids = efPaths
.map((path) => {
const path2 = path
? `${oakFullpath}.${path}`
: oakFullpath;
const data = this.runningTree.getFreshValue(path2);
(0, assert_1.assert)(data, `efPath为${path}的路径上取不到extraFile数据请设置正确的相对路径`);
return data.map((ele) => ele.id);
})
.flat()
.filter((ele) => !!ele);
}
(0, assert_1.assert)(ids.length > 0);
const promises = [];
ids.forEach((id) => {
const fileState = this.getFileState(id);
if (fileState) {
const { state } = fileState;
if (['local', 'failed'].includes(state)) {
promises.push(this.upload(id));
}
}
});
if (promises.length > 0) {
await Promise.all(promises);
}
}
getUrl(extraFile, style) {
if (!extraFile) {
return '';
}
if (extraFile?.isBridge && extraFile?.extra1) {
return this.locales.makeBridgeUrl(extraFile?.extra1);
}
const { id } = extraFile;
if (this.files[id]) {
const { file } = this.files[id];
if (typeof file === 'string') {
return file;
}
if (file instanceof File) {
return (0, extraFile_1.getFileURL)(file);
}
(0, assert_1.assert)(false, 'the incoming file is not supported');
}
const { origin } = extraFile;
const cos = (0, cos_1.getCos)(origin);
const context = this.cache.begin();
this.cache.commit();
return cos.composeFileUrl(extraFile, context, style);
}
getFileState(id) {
if (this.files[id]) {
return this.files[id];
}
}
getFileName(extraFile) {
const name = extraFile.filename +
(extraFile.extension ? `.${extraFile.extension}` : '');
return name;
}
formatBytes(size) {
return (0, extraFile_1.bytesToSize)(size);
}
async autoUpload(extraFile, file) {
const extraFileId = extraFile.id || (0, oak_domain_1.generateNewId)();
const applicationId = extraFile.applicationId || this.application.getApplicationId();
await this.cache.operate('extraFile', {
action: 'create',
data: Object.assign(extraFile, {
id: extraFileId,
applicationId,
}),
id: await (0, oak_domain_1.generateNewIdAsync)(),
});
const [newExtraFile] = this.cache.get('extraFile', {
data: Projection_1.extraFileProjection,
filter: {
id: extraFileId,
},
});
const up = new upload_1.Upload();
try {
const cos = (0, cos_1.getCos)(newExtraFile.origin);
await cos.upload(newExtraFile, up.uploadFile, file, this.uploadToAspect.bind(this));
return this.getUrl(newExtraFile);
}
catch (err) {
await this.cache.operate('extraFile', {
action: 'remove',
data: {},
filter: {
id: extraFileId,
},
id: await (0, oak_domain_1.generateNewIdAsync)(),
});
throw err;
}
}
// 私有
async uploadToAspect(file, name, // 文件的part name
aspectName, // 上传的aspect名
formData, // 上传的其它part参数
autoInform // 上传成功是否会自动通知server若不会则需要前台显式通知
) {
const formData2 = new FormData();
for (const key of Object.keys(formData)) {
formData2.append(key, formData[key]);
}
formData2.append(name || 'file', file);
const { result } = await this.cache.exec(aspectName, formData2);
return result;
}
}
exports.ExtraFile2 = ExtraFile2;

View File

@ -1,7 +1,6 @@
import { CommonAspectDict } from 'oak-common-aspect';
import { Token } from './token';
import { ExtraFile } from './extraFile';
import { ExtraFile2 } from './extraFile2';
import { Application } from './application';
import { Config } from './config';
import { Template } from './template';
@ -20,7 +19,6 @@ export declare function initialize<ED extends EntityDict, Cxt extends BackendRun
export type GeneralFeatures<ED extends EntityDict, Cxt extends BackendRuntimeContext<ED>, FrontCxt extends FrontendRuntimeContext<ED, Cxt, AD>, AD extends AspectDict<ED, Cxt> & CommonAspectDict<ED, Cxt>> = {
token: Token<ED, Cxt, FrontCxt, AD>;
extraFile: ExtraFile<ED, Cxt, FrontCxt, AD>;
extraFile2: ExtraFile2<ED, Cxt, FrontCxt, AD>;
application: Application<ED, Cxt, FrontCxt, AD>;
config: Config<ED, Cxt, FrontCxt, AD>;
template: Template<ED, Cxt, FrontCxt, AD>;

View File

@ -4,7 +4,6 @@ exports.initialize = void 0;
const tslib_1 = require("tslib");
const token_1 = require("./token");
const extraFile_1 = require("./extraFile");
const extraFile2_1 = require("./extraFile2");
const application_1 = require("./application");
const config_1 = require("./config");
const template_1 = require("./template");
@ -19,9 +18,7 @@ function initialize(basicFeatures, type, domain) {
const wechatMenu = new wechatMenu_1.WechatMenu(basicFeatures.cache, basicFeatures.localStorage);
const wechatPublicTag = new wechatPublicTag_1.WechatPublicTag(basicFeatures.cache, basicFeatures.localStorage);
const userWechatPublicTag = new userWechatPublicTag_1.UserWechatPublicTag(basicFeatures.cache, basicFeatures.localStorage);
// 临时代码,合并后再删
const extraFile = new extraFile_1.ExtraFile(basicFeatures.cache, application, basicFeatures.locales);
const extraFile2 = new extraFile2_1.ExtraFile2(basicFeatures.cache, application, basicFeatures.locales, basicFeatures.runningTree);
const extraFile = new extraFile_1.ExtraFile(basicFeatures.cache, application, basicFeatures.locales, basicFeatures.runningTree);
const config = new config_1.Config(basicFeatures.cache);
const template = new template_1.Template(basicFeatures.cache);
const weiXinJsSdk = new weiXinJsSdk_1.WeiXinJsSdk(basicFeatures.cache, basicFeatures.localStorage, basicFeatures.environment);
@ -29,7 +26,6 @@ function initialize(basicFeatures, type, domain) {
return {
token,
extraFile,
extraFile2,
application,
config,
template,

View File

@ -76,21 +76,14 @@ export default OakComponent({
},
},
methods: {
// async addExtraFile(
// extraFile: EntityDict['extraFile']['CreateSingle']['data']
// ) {
// const result = await this.features.cache.operate('extraFile', {
// action: 'create',
// data: extraFile,
// id: generateNewId(),
// });
// return result;
// },
async uploadFile(
extraFile: EntityDict['extraFile']['CreateSingle']['data']
extraFile: EntityDict['extraFile']['CreateSingle']['data'],
file: File | string
) {
const result = await this.features.extraFile.createAndUpload(extraFile, extraFile.extra1!);
const result = await this.features.extraFile.autoUpload(
extraFile as EntityDict['extraFile']['OpSchema'],
file!
);
return result;
},

View File

@ -52,7 +52,7 @@ function customCheckImageFn(
export default function Render(
props: WebComponentProps<
EntityDict,
"article",
'article',
false,
{
id: string;
@ -73,12 +73,10 @@ export default function Render(
setEditor: (editor: any) => void;
check: () => void;
preview: () => void;
addExtraFile: (
file: EntityDict["extraFile"]["CreateSingle"]["data"]
) => Promise<void>;
uploadFile: (
file: EntityDict["extraFile"]["CreateSingle"]["data"]
) => Promise<{ bucket: string; url: string }>;
extraFile: EntityDict['extraFile']['CreateSingle']['data'],
file: File
) => Promise<string>;
clearContentTip: () => void;
gotoPreview: (content?: string, title?: string) => void;
}
@ -90,14 +88,23 @@ export default function Render(
setEditor,
check,
preview,
addExtraFile,
uploadFile,
update,
setHtml,
gotoPreview,
} = method;
const { id, content, editor, origin1, oakFullpath, html, oakId, articleMenuId, changeIsEdit } = data;
const [articleId, setArticleId] = useState("");
const {
id,
content,
editor,
origin1,
oakFullpath,
html,
oakId,
articleMenuId,
changeIsEdit,
} = data;
const [articleId, setArticleId] = useState('');
useEffect(() => {
if (id) {
@ -106,7 +113,13 @@ export default function Render(
}, [id]);
return (
<div className={Style.container}>
<div style={{ width: 'calc(100% - 20px)' }}><Toolbar editor={editor} defaultConfig={toolbarConfig} mode="default" /></div>
<div style={{ width: 'calc(100% - 20px)' }}>
<Toolbar
editor={editor}
defaultConfig={toolbarConfig}
mode="default"
/>
</div>
<Row>
<Col flex={4} />
<Col flex={16}>
@ -115,19 +128,23 @@ export default function Render(
{data.contentTip && (
<Alert
type="info"
message={t("tips.content")}
message={t('tips.content')}
closable
onClose={() => method.clearContentTip()}
/>
)}
<div className={Style.titleContainer}>
<Input
onChange={(e) => update({ name: e.target.value })}
onChange={(e) =>
update({ name: e.target.value })
}
value={data.name}
placeholder={"请输入文章标题"}
placeholder={'请输入文章标题'}
size="large"
maxLength={32}
suffix={`${[...(data.name || "")].length}/32`}
suffix={`${
[...(data.name || '')].length
}/32`}
className={Style.titleInput}
/>
</div>
@ -135,47 +152,60 @@ export default function Render(
<Editor
defaultConfig={{
autoFocus: true,
placeholder: "请输入文章内容...",
placeholder: '请输入文章内容...',
MENU_CONF: {
checkImage: customCheckImageFn,
uploadImage: {
// 自定义上传
async customUpload(file: File, insertFn: InsertFnType) {
async customUpload(
file: File,
insertFn: InsertFnType
) {
// 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",
entity: 'article',
entityId: articleId,
extra1: file as any,
origin: origin1,
type: "image",
tag1: "source",
objectId: generateNewId(),
type: 'image',
tag1: 'source',
objectId:
generateNewId(),
filename,
size,
extension,
bucket: "",
bucket: '',
id: generateNewId(),
} as EntityDict["extraFile"]["CreateSingle"]["data"];
} as EntityDict['extraFile']['CreateSingle']['data'];
try {
// 自己实现上传,并得到图片 url alt href
const { url, bucket } = await uploadFile(extraFile);
extraFile.bucket = bucket;
extraFile.extra1 = null;
// await addExtraFile(extraFile);
const url =
await uploadFile(
extraFile,
file
);
// 最后插入图片
insertFn("http://" + url, extraFile.filename);
} catch (err) {
}
insertFn(
url,
extraFile.filename
);
} catch (err) {}
},
},
uploadVideo: {
@ -186,41 +216,50 @@ 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",
entity: 'article',
entityId: articleId,
extra1: file as any,
origin: origin1,
type: "video",
tag1: "source",
objectId: generateNewId(),
type: 'video',
tag1: 'source',
objectId:
generateNewId(),
filename,
size,
extension,
bucket: "",
bucket: '',
id: generateNewId(),
} as EntityDict["extraFile"]["CreateSingle"]["data"];
} as EntityDict['extraFile']['CreateSingle']['data'];
try {
// 自己实现上传,并得到图片 url alt href
const { url, bucket } = await uploadFile(extraFile);
extraFile.bucket = bucket;
extraFile.extra1 = null;
await addExtraFile(extraFile);
const url =
await uploadFile(
extraFile,
file
);
// 最后插入图片
insertFn(
"http://" + url,
"http://" + url + "?vframe/jpg/offset/0"
url,
url +
'?vframe/jpg/offset/0'
);
} catch (err) { }
} catch (err) {}
},
},
},
@ -240,7 +279,10 @@ export default function Render(
<Col flex="none">
<Space>
<Button
disabled={!data.oakDirty || data.oakExecuting}
disabled={
!data.oakDirty ||
data.oakExecuting
}
type="primary"
onClick={() => {
check();
@ -250,7 +292,10 @@ export default function Render(
</Button>
<Button
onClick={() => {
gotoPreview(content, data.name);
gotoPreview(
content,
data.name
);
}}
>
<EyeOutlined />
@ -265,6 +310,6 @@ export default function Render(
</Col>
<Col flex={4} />
</Row>
</div >
</div>
);
}

View File

@ -51,29 +51,7 @@ export default OakComponent({
this.triggerEvent('statuschange', e);
}
},
async addExtraFile(
extraFile: EntityDict['extraFile']['CreateSingle']['data']
) {
try {
const result = await this.features.cache.operate('extraFile', {
action: 'create',
data: extraFile,
id: generateNewId(),
});
return result;
} catch (error) {
if (
(error as OakException<EntityDict>).constructor.name ===
OakUnloggedInException.name
) {
this.navigateTo({
url: '/login',
});
return;
}
throw error;
}
},
async onPickMp(event: WechatMiniprogram.Touch) {
if (process.env.OAK_PLATFORM === 'wechatMp') {
const { mediaType } = event.currentTarget.dataset;
@ -99,7 +77,6 @@ export default OakComponent({
tempFilePath.lastIndexOf('.')
);
const extraFile = {
extra1: tempFilePath,
origin: 'qiniu',
type: 'image',
tag1: this.props.tag1 || 'editorImg',
@ -113,13 +90,15 @@ export default OakComponent({
entityId: this.props.entityId,
bucket: '',
id: generateNewId(),
uploadState: 'uploading'
} as EntityDict['extraFile']['CreateSingle']['data'];
const { url } =
await this.features.extraFile.createAndUpload(extraFile, extraFile.extra1!);
const url = await this.features.extraFile.autoUpload(
extraFile as EntityDict['extraFile']['OpSchema'],
tempFilePath!
);
// await this.addExtraFile(extraFile);
(this as any).editorCtx.insertImage({
src: 'http://' + url,
src: url,
});
}
} catch (err: any) {

View File

@ -22,12 +22,12 @@ export default OakComponent({
sort: 1,
applicationId: 1,
},
features: ['extraFile2'],
features: ['extraFile'],
formData({ data: extraFiles, features }) {
const avatar = extraFiles?.filter(
(ele) => !ele.$$deleteAt$$ && ele.tag1 === 'avatar'
)[0];
const avatarUrl = features.extraFile2.getUrl(
const avatarUrl = features.extraFile.getUrl(
avatar as EntityDict['extraFile']['OpSchema']
);
return {
@ -137,7 +137,7 @@ export default OakComponent({
// 如果autoUpload
if (autoUpload) {
await this.features.extraFile2.autoUpload(
await this.features.extraFile.autoUpload(
updateData as EntityDict['extraFile']['OpSchema'],
extra1
);
@ -150,7 +150,7 @@ export default OakComponent({
if (avatar) {
this.removeItem(avatar.id as string);
}
this.features.extraFile2.addLocalFile(id, extra1);
this.features.extraFile.addLocalFile(id, extra1);
}
},
},

View File

@ -1,6 +1,6 @@
import assert from 'assert';
import { EntityDict } from '../../../oak-app-domain';
import { FileState } from '../../../features/extraFile2';
import { FileState } from '../../../features/extraFile';
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/types/Entity';
import { ReactComponentProps } from 'oak-frontend-base/lib/types/Page';
import { ButtonProps } from 'antd';
@ -9,7 +9,7 @@ import { ButtonProps as AmButtonProps } from 'antd-mobile';
export default OakComponent({
formData({ features }) {
const ids: string[] = this.getEfIds();
const states = ids.map((id) => features.extraFile2.getFileState(id));
const states = ids.map((id) => features.extraFile.getFileState(id));
let state: FileState = 'uploaded';
states.forEach((ele) => {
if (ele) {
@ -31,47 +31,54 @@ export default OakComponent({
type: 'primary',
executeText: '',
buttonProps: {},
afterCommit: () => { },
beforeCommit: (() => true) as () => boolean | undefined | Promise<boolean | undefined>,
afterCommit: () => {},
beforeCommit: (() => true) as () =>
| boolean
| undefined
| Promise<boolean | undefined>,
},
methods: {
getEfIds() {
const entity = this.features.runningTree.getEntity(this.state.oakFullpath);
const value = this.features.runningTree.getFreshValue(this.state.oakFullpath);
const entity = this.features.runningTree.getEntity(
this.state.oakFullpath
);
const value = this.features.runningTree.getFreshValue(
this.state.oakFullpath
);
const efIds = [] as string[];
const getRecursive = (e: string, v: Record<string, any>) => {
for (const attr in v) {
const rel = this.features.cache.judgeRelation(e as keyof EntityDict, attr);
const rel = this.features.cache.judgeRelation(
e as keyof EntityDict,
attr
);
if (rel === 2) {
assert(typeof v[attr] === 'object');
if (attr === 'extraFile') {
assert(v[attr].id);
efIds.push(v[attr].id);
}
else {
} else {
getRecursive(attr, v[attr]);
}
}
else if (typeof rel === 'string') {
} else if (typeof rel === 'string') {
assert(typeof v[attr] === 'object');
if (rel === 'extraFile') {
assert(v[attr].id);
efIds.push(v[attr].id);
}
else {
} else {
getRecursive(rel, v[attr]);
}
}
else if (rel instanceof Array) {
} else if (rel instanceof Array) {
assert(v[attr] instanceof Array);
const [e2, fk2] = rel;
if (e2 === 'extraFile') {
efIds.push(...(v[attr].map((ele: { id: string }) => ele.id)));
}
else {
v[attr].forEach(
(ele: any) => getRecursive(e2, ele)
efIds.push(
...v[attr].map((ele: { id: string }) => ele.id)
);
} else {
v[attr].forEach((ele: any) =>
getRecursive(e2, ele)
);
}
}
@ -79,9 +86,7 @@ export default OakComponent({
};
if (value instanceof Array) {
value.forEach(
ele => getRecursive(entity, ele)
);
value.forEach((ele) => getRecursive(entity, ele));
}
getRecursive(entity, value as any);
return efIds;
@ -94,11 +99,11 @@ export default OakComponent({
const promises: Promise<void>[] = [];
ids.forEach((id) => {
const fileState = this.features.extraFile2.getFileState(id);
const fileState = this.features.extraFile.getFileState(id);
if (fileState) {
const { state } = fileState;
if (['local', 'failed'].includes(state)) {
promises.push(this.features.extraFile2.upload(id));
promises.push(this.features.extraFile.upload(id));
}
}
});
@ -130,21 +135,24 @@ export default OakComponent({
}
},
},
features: ['extraFile2'],
features: ['extraFile'],
}) as <ED2 extends EntityDict & BaseEntityDict, T2 extends keyof ED2>(
props: ReactComponentProps<
ED2,
T2,
true,
{
action?: string;
size?: ButtonProps['size'] | AmButtonProps['size'],
block?: boolean,
type?: ButtonProps['type'] | AmButtonProps['type'],
executeText?: string,
buttonProps?: ButtonProps & AmButtonProps,
afterCommit?: () => any,
beforeCommit?: () => boolean | undefined | Promise<boolean | undefined>,
}
>
) => React.ReactElement;
props: ReactComponentProps<
ED2,
T2,
true,
{
action?: string;
size?: ButtonProps['size'] | AmButtonProps['size'];
block?: boolean;
type?: ButtonProps['type'] | AmButtonProps['type'];
executeText?: string;
buttonProps?: ButtonProps & AmButtonProps;
afterCommit?: () => any;
beforeCommit?: () =>
| boolean
| undefined
| Promise<boolean | undefined>;
}
>
) => React.ReactElement;

View File

@ -2,7 +2,7 @@ import { WebComponentProps } from 'oak-frontend-base';
import React from 'react';
import { Button, ButtonProps } from 'antd';
import { EntityDict } from '../../../oak-app-domain';
import { FileState } from '../../../features/extraFile2';
import { FileState } from '../../../features/extraFile';
export default function render(
props: WebComponentProps<

View File

@ -2,7 +2,7 @@ import { WebComponentProps } from 'oak-frontend-base';
import React from 'react';
import { Button, ButtonProps } from 'antd-mobile';
import { EntityDict } from '../../../oak-app-domain';
import { FileState } from '../../../features/extraFile2';
import { FileState } from '../../../features/extraFile';
export default function render(
props: WebComponentProps<

View File

@ -222,7 +222,7 @@ export default OakComponent({
uploadState: 'uploading',
})
);
this.features.extraFile2.addLocalFile(id, file as any);
this.features.extraFile.addLocalFile(id, file as any);
},
async myUpdateItem(params: File | string) {
const { file } = this.state;

View File

@ -34,7 +34,7 @@ export default OakComponent({
isBridge: 1,
uploadState: 1,
},
features: ['extraFile2'],
features: ['extraFile'],
formData({ data, features }) {
let files = data?.sort((ele1, ele2) => ele1.sort! - ele2.sort!);
if (this.props.tag1) {
@ -45,12 +45,12 @@ export default OakComponent({
}
const files2 = files.map((ele) => {
const url = features.extraFile2.getUrl(ele as ExtraFile);
const thumbUrl = features.extraFile2.getUrl(
const url = features.extraFile.getUrl(ele as ExtraFile);
const thumbUrl = features.extraFile.getUrl(
ele as ExtraFile,
this.props.style
);
const fileName = features.extraFile2.getFileName(ele as ExtraFile);
const fileName = features.extraFile.getFileName(ele as ExtraFile);
return {
url,
thumbUrl,

View File

@ -1,5 +1,5 @@
import { EntityDict } from '../../../oak-app-domain';
import { FileState } from '../../../features/extraFile2';
import { FileState } from '../../../features/extraFile';
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/types/Entity';
import { ReactComponentProps } from 'oak-frontend-base/lib/types/Page';
import { generateNewId } from 'oak-domain';
@ -132,7 +132,7 @@ export default OakComponent({
}
},
},
features: ['extraFile2'],
features: ['extraFile'],
formData({ data, features }) {
let files = data
?.filter((ele) => !ele.$$deleteAt$$)
@ -145,13 +145,13 @@ export default OakComponent({
}
const files2 = files.map((ele) => {
const url = features.extraFile2.getUrl(ele as ExtraFile);
const thumbUrl = features.extraFile2.getUrl(
const url = features.extraFile.getUrl(ele as ExtraFile);
const thumbUrl = features.extraFile.getUrl(
ele as ExtraFile,
'thumbnail'
);
const fileState = features.extraFile2.getFileState(ele.id!);
const fileName = features.extraFile2.getFileName(ele as ExtraFile);
const fileState = features.extraFile.getFileState(ele.id!);
const fileName = features.extraFile.getFileName(ele as ExtraFile);
return {
url,
thumbUrl,
@ -168,7 +168,7 @@ export default OakComponent({
methods: {
onRemove(file: EnhancedExtraFile) {
this.removeItem(file.id);
this.features.extraFile2.removeLocalFiles([file.id]);
this.features.extraFile.removeLocalFiles([file.id]);
},
addExtraFileInner(
options: {
@ -201,7 +201,7 @@ export default OakComponent({
type,
tag1,
tag2,
objectId: generateNewId(), // 这个域用来标识唯一性
objectId: generateNewId(), // 这个域用来标识唯一性
entity,
filename,
size,
@ -211,7 +211,7 @@ export default OakComponent({
sort,
uploadState: 'uploading',
});
this.features.extraFile2.addLocalFile(id, file);
this.features.extraFile.addLocalFile(id, file);
},
addFileByWeb(file: File) {
const { size, type, name } = file;

View File

@ -86,7 +86,7 @@ export default OakComponent({
userMobile:
session?.user?.mobile$user &&
session?.user?.mobile$user[0]?.mobile,
userAvatar: features.extraFile2.getUrl(
userAvatar: features.extraFile.getUrl(
session?.user?.extraFile$entity &&
session?.user?.extraFile$entity[0]
),
@ -96,7 +96,7 @@ export default OakComponent({
const extraFile$entity = sessionMessage?.extraFile$entity;
Object.assign(newSessionMessage, {
picUrl: features.extraFile2.getUrl(
picUrl: features.extraFile.getUrl(
extraFile$entity && extraFile$entity[0]
),
});

View File

@ -102,10 +102,8 @@ export default OakComponent({
},
},
formData({ data, features }) {
const sessionMessages = data?.filter(
(ele) => ele.$$createAt$$ !== 1
)
const sessionMessages = data?.filter((ele) => ele.$$createAt$$ !== 1);
// 获取用户最后一条sessionMessage
const userLastMessage = this.getUserLastMessage() as RowWithActions<
EntityDict,
@ -328,7 +326,7 @@ export default OakComponent({
entityId: sessionMessageId,
} as EntityDict['extraFile']['CreateSingle']['data'];
await this.features.extraFile2.autoUpload(
await this.features.extraFile.autoUpload(
extraFile as EntityDict['extraFile']['OpSchema'],
originFileObj
);
@ -348,12 +346,12 @@ export default OakComponent({
},
sessionMessageId
);
// this.features.extraFile2.addLocalFile(
// this.features.extraFile.addLocalFile(
// extraFile?.id,
// originFileObj
// );
await this.execute(undefined, false);
// this.features.extraFile2.upload(extraFile?.id);
// this.features.extraFile.upload(extraFile?.id);
this.pageScroll('comment');
this.createItem();
} catch (err) {
@ -424,12 +422,12 @@ export default OakComponent({
// },
// ],
// } as EntityDict['sessionMessage']['CreateSingle']['data']);
// this.features.extraFile2.addLocalFile(
// this.features.extraFile.addLocalFile(
// extraFile?.id,
// originFileObj
// );
// await this.execute(undefined, false);
// this.features.extraFile2.upload(extraFile?.id);
// this.features.extraFile.upload(extraFile?.id);
// } catch (err) {
// throw err;
// }

View File

@ -1,11 +1,11 @@
import { Feature } from 'oak-frontend-base';
import { Upload } from 'oak-frontend-base/es/utils/upload';
import { Cache } from 'oak-frontend-base/es/features/cache';
import { RunningTree } from 'oak-frontend-base/es/features/runningTree';
import { Locales } from 'oak-frontend-base/es/features/locales';
import { CommonAspectDict } from 'oak-common-aspect';
import AspectDict from '../aspects/AspectDict';
import { EntityDict } from '../oak-app-domain';
import { Config, Origin } from '../types/Config';
import { BackendRuntimeContext } from '../context/BackendRuntimeContext';
import { FrontendRuntimeContext } from '../context/FrontendRuntimeContext';
import { Application } from './application'
@ -13,7 +13,11 @@ import { bytesToSize, getFileURL } from '../utils/extraFile';
import { assert } from 'oak-domain/lib/utils/assert';
import { getCos } from '../utils/cos';
import { OpSchema } from '../oak-app-domain/ExtraFile/Schema';
import { generateNewId } from 'oak-domain/lib/utils/uuid';
import { unset } from 'oak-domain/lib/utils/lodash';
import { generateNewId, generateNewIdAsync } from 'oak-domain';
import { extraFileProjection } from '../types/Projection';
export type FileState = 'local' | 'uploading' | 'uploaded' | 'failed';
export class ExtraFile<
ED extends EntityDict,
@ -24,102 +28,125 @@ export class ExtraFile<
private cache: Cache<ED, Cxt, FrontCxt, AD & CommonAspectDict<ED, Cxt>>;
private application: Application<ED, Cxt, FrontCxt, AD>;
private locales: Locales<ED, Cxt, FrontCxt, AD>;
private files: Record<
string,
{
file: File | string;
state: FileState;
percentage?: number;
}
>;
private runningTree: RunningTree<ED, Cxt, FrontCxt, AD>;
constructor(
cache: Cache<ED, Cxt, FrontCxt, AD & CommonAspectDict<ED, Cxt>>,
application: Application<ED, Cxt, FrontCxt, AD>,
locales: Locales<ED, Cxt, FrontCxt, AD>
locales: Locales<ED, Cxt, FrontCxt, AD>,
runningTree: RunningTree<ED, Cxt, FrontCxt, AD>
) {
super();
this.cache = cache;
this.application = application;
this.locales = locales;
this.files = {};
this.runningTree = runningTree;
}
async createAndUpload(
extraFile: EntityDict['extraFile']['CreateSingle']['data'],
file: string | File,
) {
await this.cache.operate('extraFile', {
action: 'create',
data: extraFile,
id: generateNewId(),
} as EntityDict['extraFile']['Operation']);
await this.upload(
extraFile,
file
);
const application = this.application.getApplication();
return {
url: this.getUrl(
extraFile as EntityDict['extraFile']['OpSchema']
),
addLocalFile(id: string, file: File | string) {
assert(!this.files[id]);
this.files[id] = {
file,
state: 'local',
};
this.publish();
}
async upload(
extraFile: EntityDict['extraFile']['CreateSingle']['data'],
file: string | File
) {
const { id, origin } = extraFile;
assert(origin, '未设置上传方式');
const [extraFileData] = this.cache.get('extraFile', {
data: {
origin: 1,
type: 1,
bucket: 1,
objectId: 1,
tag1: 1,
tag2: 1,
filename: 1,
md5: 1,
entity: 1,
entityId: 1,
extra1: 1,
extension: 1,
size: 1,
sort: 1,
fileType: 1,
isBridge: 1,
uploadState: 1,
uploadMeta: 1,
},
removeLocalFiles(ids: string[]) {
ids.forEach((id) => unset(this.files, id));
this.publish();
}
async upload(id: string) {
const [extraFile] = this.cache.get('extraFile', {
data: extraFileProjection,
filter: {
id,
},
});
assert(extraFile && extraFile.uploadState === 'uploading');
const item = this.files[id];
assert(item);
const { file, state } = item;
assert(['local', 'failed'].includes(state));
item.state = 'uploading';
item.percentage = 0;
const up = new Upload();
try {
const cos = getCos<ED, Cxt, FrontCxt>(origin);
const cos = getCos<ED, Cxt, FrontCxt>(extraFile.origin!);
await cos.upload(
extraFileData as OpSchema,
extraFile as OpSchema,
up.uploadFile,
file,
async () => undefined
this.uploadToAspect.bind(this)
);
await this.cache.operate('extraFile', {
action: 'update',
data: {
uploadState: 'success',
},
filter: {
id,
},
id: generateNewId(),
} as EntityDict['extraFile']['Operation']);
if (!cos.autoInform()) {
await this.cache.exec('operate', {
entity: 'extraFile',
operation: {
id: await generateNewIdAsync(),
action: 'update',
data: {
uploadState: 'success',
},
} as ED['extraFile']['Operation'],
});
}
item.state = 'uploaded';
item.percentage = undefined;
this.publish();
} catch (err) {
await this.cache.operate('extraFile', {
action: 'update',
data: {
uploadState: 'failed',
},
filter: {
id,
},
id: generateNewId(),
} as EntityDict['extraFile']['Operation']);
item.state = 'failed';
item.percentage = undefined;
this.publish();
throw err;
}
}
async uploadCommit(efPaths: string[], oakFullpath: string) {
assert(false, '方法已经废弃');
assert(efPaths && efPaths.length > 0);
let ids = [] as string[];
if (oakFullpath) {
ids = efPaths
.map((path) => {
const path2 = path ? `${oakFullpath}.${path}` : oakFullpath;
const data = this.runningTree.getFreshValue(path2);
assert(
data,
`efPath为${path}的路径上取不到extraFile数据请设置正确的相对路径`
);
return (
data as Partial<EntityDict['extraFile']['OpSchema']>[]
).map((ele) => ele.id);
})
.flat()
.filter((ele) => !!ele) as string[];
}
assert(ids.length > 0);
const promises: Promise<void>[] = [];
ids.forEach((id) => {
const fileState = this.getFileState(id);
if (fileState) {
const { state } = fileState;
if (['local', 'failed'].includes(state)) {
promises.push(this.upload(id));
}
}
});
if (promises.length > 0) {
await Promise.all(promises);
}
}
@ -133,29 +160,37 @@ export class ExtraFile<
if (!extraFile) {
return '';
}
let url;
if (extraFile?.isBridge && extraFile?.extra1) {
if (typeof extraFile?.extra1 === 'string') {
url = this.locales.makeBridgeUrl(extraFile?.extra1);
return url;
}
return this.locales.makeBridgeUrl(extraFile?.extra1);
}
if (extraFile?.extra1) {
// 有extra1就用extra1 可能File对象 可能外部链接
if (typeof extraFile?.extra1 === 'string') {
return extraFile?.extra1;
const { id } = extraFile;
if (this.files[id]) {
const { file } = this.files[id];
if (typeof file === 'string') {
return file;
}
if ((extraFile?.extra1 as File) instanceof File) {
return getFileURL(extraFile?.extra1) || '';
if (file instanceof File) {
return getFileURL(file);
}
return extraFile?.extra1 || '';
assert(false, 'the incoming file is not supported');
}
const { origin } = extraFile;
const cos = getCos<ED, Cxt, FrontCxt>(origin);
const context = this.cache.begin();
this.cache.commit();
url = cos.composeFileUrl(extraFile, context, style);
return url;
return cos.composeFileUrl(extraFile, context, style);
}
getFileState(id: string):
| {
state: FileState;
percentage?: number;
}
| undefined {
if (this.files[id]) {
return this.files[id];
}
}
getFileName(extraFile: EntityDict['extraFile']['OpSchema']) {
@ -169,4 +204,71 @@ export class ExtraFile<
formatBytes(size: number) {
return bytesToSize(size);
}
}
async autoUpload(
extraFile: EntityDict['extraFile']['OpSchema'],
file: File | string
) {
const extraFileId = extraFile.id || generateNewId();
const applicationId =
extraFile.applicationId || this.application.getApplicationId();
await this.cache.operate('extraFile', {
action: 'create',
data: Object.assign(extraFile, {
id: extraFileId,
applicationId,
}),
id: await generateNewIdAsync(),
} as EntityDict['extraFile']['Operation']);
const [newExtraFile] = this.cache.get('extraFile', {
data: extraFileProjection,
filter: {
id: extraFileId,
},
});
const up = new Upload();
try {
const cos = getCos<ED, Cxt, FrontCxt>(newExtraFile.origin!);
await cos.upload(
newExtraFile as OpSchema,
up.uploadFile,
file,
this.uploadToAspect.bind(this)
);
return this.getUrl(
newExtraFile as EntityDict['extraFile']['Schema']
);
} catch (err) {
await this.cache.operate('extraFile', {
action: 'remove',
data: {},
filter: {
id: extraFileId,
},
id: await generateNewIdAsync(),
} as EntityDict['extraFile']['Operation']);
throw err;
}
}
// 私有
private async uploadToAspect(
file: File | string,
name: string, // 文件的part name
aspectName: string, // 上传的aspect名
formData: Record<string, any>, // 上传的其它part参数
autoInform?: boolean // 上传成功是否会自动通知server若不会则需要前台显式通知
) {
const formData2 = new FormData();
for (const key of Object.keys(formData)) {
formData2.append(key, formData[key]);
}
formData2.append(name || 'file', file as File);
const { result } = await this.cache.exec(
aspectName as keyof CommonAspectDict<ED, Cxt>,
formData2
);
return result;
}
}

View File

@ -1,277 +0,0 @@
import { Feature } from 'oak-frontend-base';
import { Upload } from 'oak-frontend-base/es/utils/upload';
import { Cache } from 'oak-frontend-base/es/features/cache';
import { RunningTree } from 'oak-frontend-base/es/features/runningTree';
import { Locales } from 'oak-frontend-base/es/features/locales';
import { CommonAspectDict } from 'oak-common-aspect';
import AspectDict from '../aspects/AspectDict';
import { EntityDict } from '../oak-app-domain';
import { BackendRuntimeContext } from '../context/BackendRuntimeContext';
import { FrontendRuntimeContext } from '../context/FrontendRuntimeContext';
import { Application } from './application'
import { bytesToSize, getFileURL } from '../utils/extraFile';
import { assert } from 'oak-domain/lib/utils/assert';
import { getCos } from '../utils/cos';
import { OpSchema } from '../oak-app-domain/ExtraFile/Schema';
import { unset } from 'oak-domain/lib/utils/lodash';
import { generateNewId, generateNewIdAsync } from 'oak-domain';
import { extraFileProjection } from '../types/Projection';
export type FileState = 'local' | 'uploading' | 'uploaded' | 'failed';
export class ExtraFile2<
ED extends EntityDict,
Cxt extends BackendRuntimeContext<ED>,
FrontCxt extends FrontendRuntimeContext<ED, Cxt, AD>,
AD extends AspectDict<ED, Cxt> & CommonAspectDict<ED, Cxt>
> extends Feature {
private cache: Cache<ED, Cxt, FrontCxt, AD & CommonAspectDict<ED, Cxt>>;
private application: Application<ED, Cxt, FrontCxt, AD>;
private locales: Locales<ED, Cxt, FrontCxt, AD>;
private files: Record<
string,
{
file: File | string;
state: FileState;
percentage?: number;
}
>;
private runningTree: RunningTree<ED, Cxt, FrontCxt, AD>;
constructor(
cache: Cache<ED, Cxt, FrontCxt, AD & CommonAspectDict<ED, Cxt>>,
application: Application<ED, Cxt, FrontCxt, AD>,
locales: Locales<ED, Cxt, FrontCxt, AD>,
runningTree: RunningTree<ED, Cxt, FrontCxt, AD>,
) {
super();
this.cache = cache;
this.application = application;
this.locales = locales;
this.files = {};
this.runningTree = runningTree;
}
addLocalFile(id: string, file: File | string) {
assert(!this.files[id]);
this.files[id] = {
file,
state: 'local',
};
this.publish();
}
removeLocalFiles(ids: string[]) {
ids.forEach((id) => unset(this.files, id));
this.publish();
}
async upload(id: string) {
const [extraFile] = this.cache.get('extraFile', {
data: extraFileProjection,
filter: {
id,
},
});
assert(extraFile && extraFile.uploadState === 'uploading');
const item = this.files[id];
assert(item);
const { file, state } = item;
assert(['local', 'failed'].includes(state));
item.state = 'uploading';
item.percentage = 0;
const up = new Upload();
try {
const cos = getCos<ED, Cxt, FrontCxt>(extraFile.origin!);
await cos.upload(
extraFile as OpSchema,
up.uploadFile,
file,
this.uploadToAspect.bind(this)
);
if (!cos.autoInform()) {
await this.cache.exec('operate', {
entity: 'extraFile',
operation: {
id: await generateNewIdAsync(),
action: 'update',
data: {
uploadState: 'success',
},
} as ED['extraFile']['Operation'],
});
}
item.state = 'uploaded';
item.percentage = undefined;
this.publish();
} catch (err) {
item.state = 'failed';
item.percentage = undefined;
this.publish();
}
}
async uploadCommit(efPaths: string[], oakFullpath: string) {
assert(false, '方法已经废弃');
assert(efPaths && efPaths.length > 0);
let ids = [] as string[];
if (oakFullpath) {
ids = efPaths
.map((path) => {
const path2 = path
? `${oakFullpath}.${path}`
: oakFullpath;
const data =
this.runningTree.getFreshValue(path2);
assert(
data,
`efPath为${path}的路径上取不到extraFile数据请设置正确的相对路径`
);
return (
data as Partial<EntityDict['extraFile']['OpSchema']>[]
).map((ele) => ele.id);
})
.flat()
.filter((ele) => !!ele) as string[];
}
assert(ids.length > 0);
const promises: Promise<void>[] = [];
ids.forEach((id) => {
const fileState = this.getFileState(id);
if (fileState) {
const { state } = fileState;
if (['local', 'failed'].includes(state)) {
promises.push(this.upload(id));
}
}
});
if (promises.length > 0) {
await Promise.all(promises);
}
}
getUrl(
extraFile?:
| EntityDict['extraFile']['OpSchema']
| EntityDict['extraFile']['Schema']
| null,
style?: string
) {
if (!extraFile) {
return '';
}
if (extraFile?.isBridge && extraFile?.extra1) {
return this.locales.makeBridgeUrl(extraFile?.extra1);
}
const { id } = extraFile;
if (this.files[id]) {
const { file } = this.files[id];
if (typeof file === 'string') {
return file
}
if (file instanceof File) {
return getFileURL(file);
}
assert(false, 'the incoming file is not supported');
}
const { origin } = extraFile;
const cos = getCos<ED, Cxt, FrontCxt>(origin);
const context = this.cache.begin();
this.cache.commit();
return cos.composeFileUrl(extraFile, context, style);
}
getFileState(id: string):
| {
state: FileState;
percentage?: number;
}
| undefined {
if (this.files[id]) {
return this.files[id];
}
}
getFileName(extraFile: EntityDict['extraFile']['OpSchema']) {
const name =
extraFile.filename +
(extraFile.extension ? `.${extraFile.extension}` : '');
return name;
}
formatBytes(size: number) {
return bytesToSize(size);
}
async autoUpload(
extraFile: EntityDict['extraFile']['OpSchema'],
file: File | string
) {
const extraFileId = extraFile.id || generateNewId();
const applicationId =
extraFile.applicationId || this.application.getApplicationId();
await this.cache.operate('extraFile', {
action: 'create',
data: Object.assign(extraFile, {
id: extraFileId,
applicationId,
}),
id: await generateNewIdAsync(),
} as EntityDict['extraFile']['Operation']);
const [newExtraFile] = this.cache.get('extraFile', {
data: extraFileProjection,
filter: {
id: extraFileId,
},
});
const up = new Upload();
try {
const cos = getCos<ED, Cxt, FrontCxt>(newExtraFile.origin!);
await cos.upload(
newExtraFile as OpSchema,
up.uploadFile,
file,
this.uploadToAspect.bind(this)
);
return this.getUrl(
newExtraFile as EntityDict['extraFile']['Schema']
);
} catch (err) {
await this.cache.operate('extraFile', {
action: 'remove',
data: {},
filter: {
id: extraFileId,
},
id: await generateNewIdAsync(),
} as EntityDict['extraFile']['Operation']);
throw err;
}
}
// 私有
private async uploadToAspect(
file: File | string,
name: string, // 文件的part name
aspectName: string, // 上传的aspect名
formData: Record<string, any>, // 上传的其它part参数
autoInform?: boolean // 上传成功是否会自动通知server若不会则需要前台显式通知
) {
const formData2 = new FormData();
for (const key of Object.keys(formData)) {
formData2.append(key, formData[key]);
}
formData2.append(name || 'file', file as File);
const { result } = await this.cache.exec(
aspectName as keyof CommonAspectDict<ED, Cxt>,
formData2
);
return result;
}
}

View File

@ -1,7 +1,6 @@
import { CommonAspectDict } from 'oak-common-aspect';
import { Token } from './token';
import { ExtraFile } from './extraFile';
import { ExtraFile2 } from './extraFile2';
import { Application } from './application';
import { Config } from './config';
import { Template } from './template';
@ -54,9 +53,12 @@ export function initialize<
basicFeatures.localStorage,
)
// 临时代码,合并后再删
const extraFile = new ExtraFile<ED, Cxt, FrontCxt, AD>(basicFeatures.cache, application, basicFeatures.locales);
const extraFile2 = new ExtraFile2<ED, Cxt, FrontCxt, AD>(basicFeatures.cache, application, basicFeatures.locales, basicFeatures.runningTree);
const extraFile = new ExtraFile<ED, Cxt, FrontCxt, AD>(
basicFeatures.cache,
application,
basicFeatures.locales,
basicFeatures.runningTree
);
const config = new Config<ED, Cxt, FrontCxt, AD>(basicFeatures.cache);
const template = new Template<ED, Cxt, FrontCxt, AD>(basicFeatures.cache);
const weiXinJsSdk = new WeiXinJsSdk<ED, Cxt, FrontCxt, AD>(
@ -69,7 +71,6 @@ export function initialize<
return {
token,
extraFile,
extraFile2,
application,
config,
template,
@ -89,7 +90,6 @@ export type GeneralFeatures<
> = {
token: Token<ED, Cxt, FrontCxt, AD>;
extraFile: ExtraFile<ED, Cxt, FrontCxt, AD>;
extraFile2: ExtraFile2<ED, Cxt, FrontCxt, AD>;
application: Application<ED, Cxt, FrontCxt, AD>;
config: Config<ED, Cxt, FrontCxt, AD>;
template: Template<ED, Cxt, FrontCxt, AD>;

View File

@ -90,7 +90,10 @@ export default OakComponent({
},
uploadFile(extraFile: EntityDict['extraFile']['CreateSingle']['data']) {
return this.features.extraFile.createAndUpload(extraFile, extraFile.extra1!);
return this.features.extraFile.autoUpload(
extraFile as EntityDict['extraFile']['OpSchema'],
extraFile.extra1!
);
},
setEditor(editor: IDomEditor | null) {