Merge branch 'dev' of codeup.aliyun.com:61c14a7efa282c88e103c23f/oak-general-business into dev

This commit is contained in:
qsc 2023-09-22 19:18:10 +08:00
commit 48784ed889
43 changed files with 1353 additions and 24 deletions

View File

@ -0,0 +1,10 @@
/// <reference types="react" />
import { EntityDict } from '../../../oak-app-domain';
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<EntityDict, keyof EntityDict, boolean, {
efPaths: string[];
size: import("antd/es/button").ButtonSize | "mini";
block: boolean | undefined;
type: "primary" | "text" | "default" | "link" | "button" | "dashed" | "submit" | "reset" | undefined;
executeText: string;
}>) => import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>>;
export default _default;

View File

@ -0,0 +1,48 @@
import assert from 'assert';
export default OakComponent({
formData({ features }) {
const ids = this.getEfIds();
const states = ids.map(id => features.extraFile2.getFileState(id));
let state = 'uploaded';
states.forEach((ele) => {
if (ele) {
if (['failed', 'local'].includes(ele.state)) {
state = ele.state;
}
else if (ele.state === 'uploading' && state === 'uploaded') {
state = 'uploading';
}
}
});
return {
state,
};
},
properties: {
efPaths: [],
size: 'middle',
block: false,
type: 'primary',
executeText: '',
},
methods: {
getEfIds() {
const { efPaths } = this.props;
const { oakFullpath } = this.state;
assert(efPaths);
if (oakFullpath) {
const ids = efPaths.map((path) => {
const path2 = path ? `${oakFullpath}.path` : oakFullpath;
const data = this.features.runningTree.getFreshValue(path2);
if (data) {
return data.map(ele => ele.id);
}
}).flat().filter(ele => !!ele);
return ids;
}
return [];
},
upload() {
}
}
});

View File

@ -0,0 +1,7 @@
{
"component": true,
"usingComponents": {
"l-icon": "@oak-frontend-base/miniprogram_npm/lin-ui/icon/index",
"l-button": "@oak-frontend-base/miniprogram_npm/lin-ui/button/index"
}
}

View File

@ -0,0 +1,5 @@
{
"executing": "%{text}中...",
"upload": "上传",
"uploading": "上传中"
}

13
es/components/extraFile/commit/web.d.ts vendored Normal file
View File

@ -0,0 +1,13 @@
import { WebComponentProps } from 'oak-frontend-base';
import { ButtonProps } from 'antd-mobile';
import { EntityDict } from '../../../oak-app-domain';
import { FileState } from '../../../features/extraFile2';
export default function render(props: WebComponentProps<EntityDict, any, true, {
state: FileState;
size?: ButtonProps['size'];
block?: ButtonProps['block'];
type?: ButtonProps['type'];
executeText?: string;
}, {
upload: () => Promise<void>;
}>): JSX.Element;

View File

@ -0,0 +1,28 @@
import { jsx as _jsx } from "react/jsx-runtime";
import { Button } from 'antd-mobile';
export default function render(props) {
const { state, oakExecutable, oakExecuting, oakDirty, size, block, type, executeText } = props.data;
const { t, upload, execute } = props.methods;
const disabled = oakExecuting || ['uploading'].includes(state) || (oakExecutable === false && ['uploaded'].includes(state));
let text = executeText || t('common::action.confirm');
if (oakExecuting) {
text = t('executing', { text });
}
else if (oakExecutable === false) {
if (state === 'uploading') {
text = t('uploading');
}
else if (['failed', 'local'].includes(state)) {
text = t('upload');
}
}
return (_jsx(Button, { type: type, size: size, block: block, disabled: disabled, onClick: async () => {
if (oakExecutable) {
await execute();
await upload();
}
else {
await upload();
}
}, children: text }));
}

View File

@ -0,0 +1,13 @@
import { WebComponentProps } from 'oak-frontend-base';
import { ButtonProps } from 'antd';
import { EntityDict } from '../../../oak-app-domain';
import { FileState } from '../../../features/extraFile2';
export default function render(props: WebComponentProps<EntityDict, any, true, {
state: FileState;
size?: ButtonProps['size'];
block?: ButtonProps['block'];
type?: ButtonProps['type'];
executeText?: string;
}, {
upload: () => Promise<void>;
}>): JSX.Element;

View File

@ -0,0 +1,28 @@
import { jsx as _jsx } from "react/jsx-runtime";
import { Button } from 'antd';
export default function render(props) {
const { state, oakExecutable, oakExecuting, oakDirty, size, block, type, executeText } = props.data;
const { t, upload, execute } = props.methods;
const disabled = oakExecuting || ['uploading'].includes(state) || (oakExecutable === false && ['uploaded'].includes(state));
let text = executeText || t('common::action.confirm');
if (oakExecuting) {
text = t('executing', { text });
}
else if (oakExecutable === false) {
if (state === 'uploading') {
text = t('uploading');
}
else if (['failed', 'local'].includes(state)) {
text = t('upload');
}
}
return (_jsx(Button, { type: type, size: size, block: block, disabled: disabled, onClick: async () => {
if (oakExecutable) {
await execute();
await upload();
}
else {
await upload();
}
}, children: text }));
}

View File

@ -0,0 +1,46 @@
import { EntityDict } from '../../../oak-app-domain';
import { FileState } from '../../../features/extraFile2';
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/types/Entity';
import { ReactComponentProps } from 'oak-frontend-base/lib/types/Page';
type ExtraFile = EntityDict['extraFile']['OpSchema'];
export interface EnhancedExtraFile extends ExtraFile {
url: string;
thumbUrl: string;
filename: string;
fileState?: FileState;
percentage?: number;
}
type SourceType = 'album' | 'camera';
export type Theme = 'file' | 'image' | 'image-flow' | 'custom';
type FileType = 'all' | 'video' | 'image' | 'file';
type ImgMode = 'scaleToFill' | 'aspectFit' | 'aspectFill' | 'widthFix' | "heightFix" | 'top' | 'bottom' | 'left' | 'right' | 'center' | 'top left' | 'top right' | 'bottom left' | 'bottom right';
declare const _default: <ED2 extends EntityDict & BaseEntityDict, T2 extends keyof ED2>(props: ReactComponentProps<ED2, T2, true, {
bucket: string;
removeLater: boolean;
autoUpload: boolean;
maxNumber: number;
extension: string[];
fileType: FileType;
selectCount: number;
sourceType: SourceType[];
mediaType: ('image' | 'video')[];
mode: ImgMode;
size: number;
showUploadList: boolean;
accept: string;
preview: boolean;
disableDelete: boolean;
disableAdd: boolean;
disableDownload: boolean;
disabled: boolean;
type: string;
origin: string;
tag1: string;
tag2: string;
entity: keyof ED2;
entityId: string;
theme: Theme;
showUploadProgress: boolean;
children?: React.ReactNode;
}>) => React.ReactElement;
export default _default;

View File

@ -0,0 +1,163 @@
import { generateNewId } from 'oak-domain/lib/utils/uuid';
;
export default OakComponent({
entity: 'extraFile',
isList: true,
projection: {
id: 1,
tag1: 1,
tag2: 1,
origin: 1,
bucket: 1,
objectId: 1,
filename: 1,
extra1: 1,
extension: 1,
type: 1,
entity: 1,
entityId: 1,
fileType: 1,
sort: 1,
isBridge: 1,
uploadState: 1,
},
data: {
// 根据 size 不同,计算的图片显示大小不同
itemSizePercentage: '',
},
wechatMp: {
externalClasses: ['oak-class', 'oak-item-class'],
},
filters: [
{
filter() {
const { tag1, tag2 } = this.props;
const filter1 = {};
if (tag1) {
Object.assign(filter1, { tag1 });
}
if (tag2) {
Object.assign(filter1, { tag2 });
}
return filter1;
},
},
],
properties: {
bucket: '',
removeLater: true,
autoUpload: false,
maxNumber: 20,
extension: [],
fileType: 'all',
selectCount: 1,
sourceType: ['album', 'camera'],
mediaType: ['image'],
// 图片显示模式
mode: 'aspectFit',
// 每行可显示的个数
size: 3,
showUploadList: true,
accept: 'image/*',
// 图片是否可预览
preview: true,
// 图片是否可删除
disableDelete: false,
// 上传按钮隐藏
disableAdd: false,
// 下按按钮隐藏
disableDownload: false,
type: 'file',
origin: 'qiniu',
tag1: '',
tag2: '',
entity: '',
entityId: '',
theme: 'image',
showUploadProgress: false,
},
listeners: {
maxNumber(prev, next) {
if (this.state.oakFullpath) {
if (prev.maxNumber !== next.maxNumber) {
this.reRender();
}
}
},
async size(prev, next) {
if (process.env.OAK_PLATFORM === 'wechatMp') {
const size = next.size;
if (!size) {
this.setState({ itemSizePercentage: '' });
return;
}
// 获取 .file-list__container 容器宽度
const res = await this.getNodeRectFromComponent(this, '.file-list__container');
const widthRpx = this.px2rpx(res.right - res.left);
// 根据容器宽度计算单张图片宽度百分比
const itemSizePercentage = (10 / size) * 10 - (20 / widthRpx) * 100 + '%;';
this.setState({
itemSizePercentage: itemSizePercentage,
});
}
},
},
features: ['extraFile2'],
formData({ data, features }) {
const files = data.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);
return {
url,
thumbUrl,
filename,
fileState: fileState?.state,
percentage: fileState?.percentage,
...ele,
};
});
return {
files,
};
},
methods: {
onRemove(file) {
this.removeItem(file.id);
this.features.extraFile2.removeLocalFiles([file.id]);
},
addExtraFileInner(options, file) {
const { type, origin, tag1, tag2, entity, entityId, bucket } = this.props;
const { name, fileType, size } = options;
const extension = name.substring(name.lastIndexOf('.') + 1);
const filename = name.substring(0, name.lastIndexOf('.'));
const { files } = this.state;
const sort = files.length * 10000;
const id = this.addItem({
bucket,
origin,
type,
tag1,
tag2,
objectId: generateNewId(),
entity,
filename,
size,
extension,
fileType,
entityId,
sort
});
this.features.extraFile2.addLocalFile(id, file);
},
addFileByWeb(file) {
const { size, type, name } = file;
this.addExtraFileInner({
name,
fileType: type,
size,
}, file);
}
}
});

View File

@ -0,0 +1,4 @@
{
"choosePicture": "请选择图片",
"chooseFile": "请选择文件"
}

28
es/components/extraFile/upload/web.d.ts vendored Normal file
View File

@ -0,0 +1,28 @@
import { UploadFile } from "antd";
import { WebComponentProps } from "oak-frontend-base";
import { EntityDict } from "../../../oak-app-domain";
import { EnhancedExtraFile, Theme } from './index';
export default function render(props: WebComponentProps<EntityDict, 'extraFile', true, {
files: EnhancedExtraFile[];
accept?: string;
maxNumber?: number;
multiple?: boolean;
draggable?: boolean;
theme?: Theme;
tips?: string;
beforeUpload?: (file: File) => Promise<boolean>;
style?: Record<string, string>;
className?: string;
directory?: boolean;
onPreview?: (file: UploadFile<any>) => void;
onDownload?: (file: UploadFile<any>) => void;
showUploadList?: boolean;
children?: JSX.Element;
disableInsert?: boolean;
disableDownload?: boolean;
disableDelete?: boolean;
preview?: boolean;
}, {
onRemove: (file: UploadFile) => void;
addFileByWeb: (file: UploadFile) => void;
}>): JSX.Element;

View File

@ -0,0 +1,140 @@
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import React, { useCallback } from 'react';
import { Space, Upload, Button } from "antd";
import { PlusOutlined } from "@ant-design/icons";
import classNames from "classnames";
import { DndProvider, useDrag, useDrop } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
import { TouchBackend } from "react-dnd-touch-backend";
import { isPc } from "oak-frontend-base/es/utils/utils";
import Style from "./web.module.less";
function getListType(theme) {
const themeMap = {
file: "text",
image: "picture-card",
"image-flow": "picture",
custom: "text",
};
return themeMap[theme];
}
const type = "DragableUploadList";
const DragableUploadListItem = ({ originNode, moveRow, file, fileList, }) => {
const ref = React.useRef(null);
const index = fileList.indexOf(file);
const [{ isOver, dropClassName }, drop] = useDrop({
accept: type,
collect: (monitor) => {
const { index: dragIndex } = monitor.getItem() || {};
if (dragIndex === index) {
return {};
}
return {
isOver: monitor.isOver(),
dropClassName: dragIndex < index ? " drop-over-downward" : " drop-over-upward",
};
},
drop: (item) => {
moveRow(item.index, index);
},
});
const [, drag] = useDrag({
type,
item: { index },
collect: (monitor) => ({
isDragging: monitor.isDragging(),
}),
});
drop(drag(ref));
return (_jsx("div", { ref: ref, className: `ant-upload-draggable-list-item ${isOver ? dropClassName : ""}`, style: { cursor: "move", height: "100%" }, children: originNode }));
};
export default function render(props) {
const { accept = "image/*", maxNumber = 20, multiple = maxNumber !== 1, draggable = false, theme = "image", tips, beforeUpload, style, className, directory = false, onPreview, onDownload, children, showUploadList = true, files = [], disableInsert = false, disableDownload = false, disableDelete = false, preview = true, } = props.data;
const { t, onRemove, addFileByWeb } = props.methods;
const listType = getListType(theme);
const getUploadButton = () => {
if (children) {
return children;
}
if (listType === "picture-card") {
return (_jsxs("div", { children: [_jsx(PlusOutlined, {}), _jsx("div", { style: { marginTop: 8 }, children: t('choosePicture') })] }));
}
return _jsx(Button, { type: "default", children: t('chooseFile') });
};
const transformToUploadFile = () => {
return files.map((file) => {
let status = undefined;
if (file.$$deleteAt$$) {
status = 'removed';
}
else if (file.fileState) {
switch (file.fileState) {
case 'failed': {
status = 'error';
break;
}
case 'local': {
break;
}
case 'uploaded': {
status = 'done';
break;
}
case 'uploading': {
status = 'uploading';
break;
}
default: {
break;
}
}
}
else {
switch (file.uploadState) {
case 'uploading': {
status = 'uploading';
break;
}
case 'failed': {
status = 'error';
break;
}
case 'success': {
status = 'done';
break;
}
}
}
return {
...file,
status,
name: file.filename,
uid: file.id,
size: file.size,
};
});
};
const moveRow = useCallback((dragIndex, hoverIndex) => {
console.log('dragIndex', dragIndex, 'hoverIndex', hoverIndex);
}, [files]);
return (_jsxs(Space, { direction: "vertical", className: Style["oak-upload"], style: { width: "100%" }, children: [_jsx(DndProvider, { backend: isPc ? HTML5Backend : TouchBackend, children: _jsx(Upload, { className: classNames(Style["oak-upload__upload"], className), style: style, directory: directory, showUploadList: showUploadList
? {
showPreviewIcon: preview,
showRemoveIcon: !disableDelete,
showDownloadIcon: !disableDownload,
}
: false, beforeUpload: async (file) => {
if (typeof beforeUpload === "function") {
const result = await beforeUpload(file);
if (result) {
return false;
}
}
return false;
}, multiple: multiple, accept: accept, listType: listType, fileList: transformToUploadFile(), onChange: ({ file, fileList, event }) => {
if (file instanceof File) {
addFileByWeb(file);
}
}, onRemove: onRemove, onPreview: onPreview, onDownload: onDownload, itemRender: (originNode, currentFile, currentFileList) => {
return (_jsx(DragableUploadListItem, { originNode: originNode, file: currentFile, fileList: currentFileList, moveRow: moveRow }));
}, children: !disableInsert && files.length < maxNumber ? getUploadButton() : null }) }), tips && _jsx("small", { className: Style["oak-upload__tips"], children: tips })] }));
}

View File

@ -135,6 +135,18 @@ const i18ns = [
"weChat-authorization-login-successful": "微信授权登录成功"
}
},
{
id: "d294bc3cfdc260f1bcc6b96b4b9b49b0",
namespace: "oak-general-business-c-extraFile-commit",
language: "zh-CN",
module: "oak-general-business",
position: "src/components/extraFile/commit",
data: {
"executing": "%{text}中...",
"upload": "上传",
"uploading": "上传中"
}
},
{
id: "5c2e6feed554bc476664f2a80e09677b",
namespace: "oak-general-business-c-extraFile-forUrl",
@ -162,11 +174,11 @@ const i18ns = [
}
},
{
id: "d30e4a47c1d768d74efae1fade371595",
namespace: "oak-general-business-c-extraFile-gallery2",
id: "a0e4461f6283d6ecd0b6bba64c0560c2",
namespace: "oak-general-business-c-extraFile-upload",
language: "zh-CN",
module: "oak-general-business",
position: "src/components/extraFile/gallery2",
position: "src/components/extraFile/upload",
data: {
"choosePicture": "请选择图片",
"chooseFile": "请选择文件"

View File

@ -0,0 +1,10 @@
/// <reference types="react" />
import { EntityDict } from '../../../oak-app-domain';
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<EntityDict, keyof EntityDict, boolean, {
efPaths: string[];
size: import("antd/es/button").ButtonSize | "mini";
block: boolean | undefined;
type: "primary" | "text" | "default" | "link" | "button" | "dashed" | "submit" | "reset" | undefined;
executeText: string;
}>) => import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>>;
export default _default;

View File

@ -0,0 +1,51 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const assert_1 = tslib_1.__importDefault(require("assert"));
exports.default = OakComponent({
formData({ features }) {
const ids = this.getEfIds();
const states = ids.map(id => features.extraFile2.getFileState(id));
let state = 'uploaded';
states.forEach((ele) => {
if (ele) {
if (['failed', 'local'].includes(ele.state)) {
state = ele.state;
}
else if (ele.state === 'uploading' && state === 'uploaded') {
state = 'uploading';
}
}
});
return {
state,
};
},
properties: {
efPaths: [],
size: 'middle',
block: false,
type: 'primary',
executeText: '',
},
methods: {
getEfIds() {
const { efPaths } = this.props;
const { oakFullpath } = this.state;
(0, assert_1.default)(efPaths);
if (oakFullpath) {
const ids = efPaths.map((path) => {
const path2 = path ? `${oakFullpath}.path` : oakFullpath;
const data = this.features.runningTree.getFreshValue(path2);
if (data) {
return data.map(ele => ele.id);
}
}).flat().filter(ele => !!ele);
return ids;
}
return [];
},
upload() {
}
}
});

View File

@ -0,0 +1,7 @@
{
"component": true,
"usingComponents": {
"l-icon": "@oak-frontend-base/miniprogram_npm/lin-ui/icon/index",
"l-button": "@oak-frontend-base/miniprogram_npm/lin-ui/button/index"
}
}

View File

@ -0,0 +1,5 @@
{
"executing": "%{text}中...",
"upload": "上传",
"uploading": "上传中"
}

View File

@ -0,0 +1,13 @@
import { WebComponentProps } from 'oak-frontend-base';
import { ButtonProps } from 'antd-mobile';
import { EntityDict } from '../../../oak-app-domain';
import { FileState } from '../../../features/extraFile2';
export default function render(props: WebComponentProps<EntityDict, any, true, {
state: FileState;
size?: ButtonProps['size'];
block?: ButtonProps['block'];
type?: ButtonProps['type'];
executeText?: string;
}, {
upload: () => Promise<void>;
}>): JSX.Element;

View File

@ -0,0 +1,31 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const jsx_runtime_1 = require("react/jsx-runtime");
const antd_mobile_1 = require("antd-mobile");
function render(props) {
const { state, oakExecutable, oakExecuting, oakDirty, size, block, type, executeText } = props.data;
const { t, upload, execute } = props.methods;
const disabled = oakExecuting || ['uploading'].includes(state) || (oakExecutable === false && ['uploaded'].includes(state));
let text = executeText || t('common::action.confirm');
if (oakExecuting) {
text = t('executing', { text });
}
else if (oakExecutable === false) {
if (state === 'uploading') {
text = t('uploading');
}
else if (['failed', 'local'].includes(state)) {
text = t('upload');
}
}
return ((0, jsx_runtime_1.jsx)(antd_mobile_1.Button, { type: type, size: size, block: block, disabled: disabled, onClick: async () => {
if (oakExecutable) {
await execute();
await upload();
}
else {
await upload();
}
}, children: text }));
}
exports.default = render;

View File

@ -0,0 +1,13 @@
import { WebComponentProps } from 'oak-frontend-base';
import { ButtonProps } from 'antd';
import { EntityDict } from '../../../oak-app-domain';
import { FileState } from '../../../features/extraFile2';
export default function render(props: WebComponentProps<EntityDict, any, true, {
state: FileState;
size?: ButtonProps['size'];
block?: ButtonProps['block'];
type?: ButtonProps['type'];
executeText?: string;
}, {
upload: () => Promise<void>;
}>): JSX.Element;

View File

@ -0,0 +1,31 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const jsx_runtime_1 = require("react/jsx-runtime");
const antd_1 = require("antd");
function render(props) {
const { state, oakExecutable, oakExecuting, oakDirty, size, block, type, executeText } = props.data;
const { t, upload, execute } = props.methods;
const disabled = oakExecuting || ['uploading'].includes(state) || (oakExecutable === false && ['uploaded'].includes(state));
let text = executeText || t('common::action.confirm');
if (oakExecuting) {
text = t('executing', { text });
}
else if (oakExecutable === false) {
if (state === 'uploading') {
text = t('uploading');
}
else if (['failed', 'local'].includes(state)) {
text = t('upload');
}
}
return ((0, jsx_runtime_1.jsx)(antd_1.Button, { type: type, size: size, block: block, disabled: disabled, onClick: async () => {
if (oakExecutable) {
await execute();
await upload();
}
else {
await upload();
}
}, children: text }));
}
exports.default = render;

View File

@ -0,0 +1,46 @@
import { EntityDict } from '../../../oak-app-domain';
import { FileState } from '../../../features/extraFile2';
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/types/Entity';
import { ReactComponentProps } from 'oak-frontend-base/lib/types/Page';
type ExtraFile = EntityDict['extraFile']['OpSchema'];
export interface EnhancedExtraFile extends ExtraFile {
url: string;
thumbUrl: string;
filename: string;
fileState?: FileState;
percentage?: number;
}
type SourceType = 'album' | 'camera';
export type Theme = 'file' | 'image' | 'image-flow' | 'custom';
type FileType = 'all' | 'video' | 'image' | 'file';
type ImgMode = 'scaleToFill' | 'aspectFit' | 'aspectFill' | 'widthFix' | "heightFix" | 'top' | 'bottom' | 'left' | 'right' | 'center' | 'top left' | 'top right' | 'bottom left' | 'bottom right';
declare const _default: <ED2 extends EntityDict & BaseEntityDict, T2 extends keyof ED2>(props: ReactComponentProps<ED2, T2, true, {
bucket: string;
removeLater: boolean;
autoUpload: boolean;
maxNumber: number;
extension: string[];
fileType: FileType;
selectCount: number;
sourceType: SourceType[];
mediaType: ('image' | 'video')[];
mode: ImgMode;
size: number;
showUploadList: boolean;
accept: string;
preview: boolean;
disableDelete: boolean;
disableAdd: boolean;
disableDownload: boolean;
disabled: boolean;
type: string;
origin: string;
tag1: string;
tag2: string;
entity: keyof ED2;
entityId: string;
theme: Theme;
showUploadProgress: boolean;
children?: React.ReactNode;
}>) => React.ReactElement;
export default _default;

View File

@ -0,0 +1,165 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const uuid_1 = require("oak-domain/lib/utils/uuid");
;
exports.default = OakComponent({
entity: 'extraFile',
isList: true,
projection: {
id: 1,
tag1: 1,
tag2: 1,
origin: 1,
bucket: 1,
objectId: 1,
filename: 1,
extra1: 1,
extension: 1,
type: 1,
entity: 1,
entityId: 1,
fileType: 1,
sort: 1,
isBridge: 1,
uploadState: 1,
},
data: {
// 根据 size 不同,计算的图片显示大小不同
itemSizePercentage: '',
},
wechatMp: {
externalClasses: ['oak-class', 'oak-item-class'],
},
filters: [
{
filter() {
const { tag1, tag2 } = this.props;
const filter1 = {};
if (tag1) {
Object.assign(filter1, { tag1 });
}
if (tag2) {
Object.assign(filter1, { tag2 });
}
return filter1;
},
},
],
properties: {
bucket: '',
removeLater: true,
autoUpload: false,
maxNumber: 20,
extension: [],
fileType: 'all',
selectCount: 1,
sourceType: ['album', 'camera'],
mediaType: ['image'],
// 图片显示模式
mode: 'aspectFit',
// 每行可显示的个数
size: 3,
showUploadList: true,
accept: 'image/*',
// 图片是否可预览
preview: true,
// 图片是否可删除
disableDelete: false,
// 上传按钮隐藏
disableAdd: false,
// 下按按钮隐藏
disableDownload: false,
type: 'file',
origin: 'qiniu',
tag1: '',
tag2: '',
entity: '',
entityId: '',
theme: 'image',
showUploadProgress: false,
},
listeners: {
maxNumber(prev, next) {
if (this.state.oakFullpath) {
if (prev.maxNumber !== next.maxNumber) {
this.reRender();
}
}
},
async size(prev, next) {
if (process.env.OAK_PLATFORM === 'wechatMp') {
const size = next.size;
if (!size) {
this.setState({ itemSizePercentage: '' });
return;
}
// 获取 .file-list__container 容器宽度
const res = await this.getNodeRectFromComponent(this, '.file-list__container');
const widthRpx = this.px2rpx(res.right - res.left);
// 根据容器宽度计算单张图片宽度百分比
const itemSizePercentage = (10 / size) * 10 - (20 / widthRpx) * 100 + '%;';
this.setState({
itemSizePercentage: itemSizePercentage,
});
}
},
},
features: ['extraFile2'],
formData({ data, features }) {
const files = data.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);
return {
url,
thumbUrl,
filename,
fileState: fileState?.state,
percentage: fileState?.percentage,
...ele,
};
});
return {
files,
};
},
methods: {
onRemove(file) {
this.removeItem(file.id);
this.features.extraFile2.removeLocalFiles([file.id]);
},
addExtraFileInner(options, file) {
const { type, origin, tag1, tag2, entity, entityId, bucket } = this.props;
const { name, fileType, size } = options;
const extension = name.substring(name.lastIndexOf('.') + 1);
const filename = name.substring(0, name.lastIndexOf('.'));
const { files } = this.state;
const sort = files.length * 10000;
const id = this.addItem({
bucket,
origin,
type,
tag1,
tag2,
objectId: (0, uuid_1.generateNewId)(),
entity,
filename,
size,
extension,
fileType,
entityId,
sort
});
this.features.extraFile2.addLocalFile(id, file);
},
addFileByWeb(file) {
const { size, type, name } = file;
this.addExtraFileInner({
name,
fileType: type,
size,
}, file);
}
}
});

View File

@ -0,0 +1,8 @@
{
"component": true,
"usingComponents": {
"l-icon": "@oak-frontend-base/miniprogram_npm/lin-ui/icon/index",
"l-button": "@oak-frontend-base/miniprogram_npm/lin-ui/button/index",
"oak-display": "../display/index"
}
}

View File

@ -0,0 +1,4 @@
{
"choosePicture": "请选择图片",
"chooseFile": "请选择文件"
}

View File

@ -0,0 +1,28 @@
import { UploadFile } from "antd";
import { WebComponentProps } from "oak-frontend-base";
import { EntityDict } from "../../../oak-app-domain";
import { EnhancedExtraFile, Theme } from './index';
export default function render(props: WebComponentProps<EntityDict, 'extraFile', true, {
files: EnhancedExtraFile[];
accept?: string;
maxNumber?: number;
multiple?: boolean;
draggable?: boolean;
theme?: Theme;
tips?: string;
beforeUpload?: (file: File) => Promise<boolean>;
style?: Record<string, string>;
className?: string;
directory?: boolean;
onPreview?: (file: UploadFile<any>) => void;
onDownload?: (file: UploadFile<any>) => void;
showUploadList?: boolean;
children?: JSX.Element;
disableInsert?: boolean;
disableDownload?: boolean;
disableDelete?: boolean;
preview?: boolean;
}, {
onRemove: (file: UploadFile) => void;
addFileByWeb: (file: UploadFile) => void;
}>): JSX.Element;

View File

@ -0,0 +1,144 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = tslib_1.__importStar(require("react"));
const antd_1 = require("antd");
const icons_1 = require("@ant-design/icons");
const classnames_1 = tslib_1.__importDefault(require("classnames"));
const react_dnd_1 = require("react-dnd");
const react_dnd_html5_backend_1 = require("react-dnd-html5-backend");
const react_dnd_touch_backend_1 = require("react-dnd-touch-backend");
const utils_1 = require("oak-frontend-base/es/utils/utils");
const web_module_less_1 = tslib_1.__importDefault(require("./web.module.less"));
function getListType(theme) {
const themeMap = {
file: "text",
image: "picture-card",
"image-flow": "picture",
custom: "text",
};
return themeMap[theme];
}
const type = "DragableUploadList";
const DragableUploadListItem = ({ originNode, moveRow, file, fileList, }) => {
const ref = react_1.default.useRef(null);
const index = fileList.indexOf(file);
const [{ isOver, dropClassName }, drop] = (0, react_dnd_1.useDrop)({
accept: type,
collect: (monitor) => {
const { index: dragIndex } = monitor.getItem() || {};
if (dragIndex === index) {
return {};
}
return {
isOver: monitor.isOver(),
dropClassName: dragIndex < index ? " drop-over-downward" : " drop-over-upward",
};
},
drop: (item) => {
moveRow(item.index, index);
},
});
const [, drag] = (0, react_dnd_1.useDrag)({
type,
item: { index },
collect: (monitor) => ({
isDragging: monitor.isDragging(),
}),
});
drop(drag(ref));
return ((0, jsx_runtime_1.jsx)("div", { ref: ref, className: `ant-upload-draggable-list-item ${isOver ? dropClassName : ""}`, style: { cursor: "move", height: "100%" }, children: originNode }));
};
function render(props) {
const { accept = "image/*", maxNumber = 20, multiple = maxNumber !== 1, draggable = false, theme = "image", tips, beforeUpload, style, className, directory = false, onPreview, onDownload, children, showUploadList = true, files = [], disableInsert = false, disableDownload = false, disableDelete = false, preview = true, } = props.data;
const { t, onRemove, addFileByWeb } = props.methods;
const listType = getListType(theme);
const getUploadButton = () => {
if (children) {
return children;
}
if (listType === "picture-card") {
return ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(icons_1.PlusOutlined, {}), (0, jsx_runtime_1.jsx)("div", { style: { marginTop: 8 }, children: t('choosePicture') })] }));
}
return (0, jsx_runtime_1.jsx)(antd_1.Button, { type: "default", children: t('chooseFile') });
};
const transformToUploadFile = () => {
return files.map((file) => {
let status = undefined;
if (file.$$deleteAt$$) {
status = 'removed';
}
else if (file.fileState) {
switch (file.fileState) {
case 'failed': {
status = 'error';
break;
}
case 'local': {
break;
}
case 'uploaded': {
status = 'done';
break;
}
case 'uploading': {
status = 'uploading';
break;
}
default: {
break;
}
}
}
else {
switch (file.uploadState) {
case 'uploading': {
status = 'uploading';
break;
}
case 'failed': {
status = 'error';
break;
}
case 'success': {
status = 'done';
break;
}
}
}
return {
...file,
status,
name: file.filename,
uid: file.id,
size: file.size,
};
});
};
const moveRow = (0, react_1.useCallback)((dragIndex, hoverIndex) => {
console.log('dragIndex', dragIndex, 'hoverIndex', hoverIndex);
}, [files]);
return ((0, jsx_runtime_1.jsxs)(antd_1.Space, { direction: "vertical", className: web_module_less_1.default["oak-upload"], style: { width: "100%" }, children: [(0, jsx_runtime_1.jsx)(react_dnd_1.DndProvider, { backend: utils_1.isPc ? react_dnd_html5_backend_1.HTML5Backend : react_dnd_touch_backend_1.TouchBackend, children: (0, jsx_runtime_1.jsx)(antd_1.Upload, { className: (0, classnames_1.default)(web_module_less_1.default["oak-upload__upload"], className), style: style, directory: directory, showUploadList: showUploadList
? {
showPreviewIcon: preview,
showRemoveIcon: !disableDelete,
showDownloadIcon: !disableDownload,
}
: false, beforeUpload: async (file) => {
if (typeof beforeUpload === "function") {
const result = await beforeUpload(file);
if (result) {
return false;
}
}
return false;
}, multiple: multiple, accept: accept, listType: listType, fileList: transformToUploadFile(), onChange: ({ file, fileList, event }) => {
if (file instanceof File) {
addFileByWeb(file);
}
}, onRemove: onRemove, onPreview: onPreview, onDownload: onDownload, itemRender: (originNode, currentFile, currentFileList) => {
return ((0, jsx_runtime_1.jsx)(DragableUploadListItem, { originNode: originNode, file: currentFile, fileList: currentFileList, moveRow: moveRow }));
}, children: !disableInsert && files.length < maxNumber ? getUploadButton() : null }) }), tips && (0, jsx_runtime_1.jsx)("small", { className: web_module_less_1.default["oak-upload__tips"], children: tips })] }));
}
exports.default = render;

View File

@ -0,0 +1,7 @@
.oak-upload {
&__tips {
display: block;
color: var(--oak-text-color-placeholder);
margin-top: 8px;
}
}

View File

@ -137,6 +137,18 @@ const i18ns = [
"weChat-authorization-login-successful": "微信授权登录成功"
}
},
{
id: "d294bc3cfdc260f1bcc6b96b4b9b49b0",
namespace: "oak-general-business-c-extraFile-commit",
language: "zh-CN",
module: "oak-general-business",
position: "src/components/extraFile/commit",
data: {
"executing": "%{text}中...",
"upload": "上传",
"uploading": "上传中"
}
},
{
id: "5c2e6feed554bc476664f2a80e09677b",
namespace: "oak-general-business-c-extraFile-forUrl",
@ -164,11 +176,11 @@ const i18ns = [
}
},
{
id: "d30e4a47c1d768d74efae1fade371595",
namespace: "oak-general-business-c-extraFile-gallery2",
id: "a0e4461f6283d6ecd0b6bba64c0560c2",
namespace: "oak-general-business-c-extraFile-upload",
language: "zh-CN",
module: "oak-general-business",
position: "src/components/extraFile/gallery2",
position: "src/components/extraFile/upload",
data: {
"choosePicture": "请选择图片",
"chooseFile": "请选择文件"

View File

@ -0,0 +1,7 @@
{
"component": true,
"usingComponents": {
"l-icon": "@oak-frontend-base/miniprogram_npm/lin-ui/icon/index",
"l-button": "@oak-frontend-base/miniprogram_npm/lin-ui/button/index"
}
}

View File

@ -0,0 +1,64 @@
import assert from 'assert';
import { ButtonProps } from 'antd';
import { ButtonProps as MobileButtonProps } from 'antd-mobile';
import { EntityDict } from '../../../oak-app-domain';
import { FileState } from '../../../features/extraFile2';
export default OakComponent({
formData({ features }) {
const ids: string[] = this.getEfIds();
const states = ids.map(
id => features.extraFile2.getFileState(id)
);
let state: FileState = 'uploaded';
states.forEach(
(ele) => {
if (ele) {
if (['failed', 'local'].includes(ele.state)) {
state = ele.state;
}
else if (ele.state === 'uploading' && state === 'uploaded') {
state = 'uploading';
}
}
}
);
return {
state,
};
},
properties: {
efPaths: [] as string[],
size: 'middle' as ButtonProps['size'] | MobileButtonProps['size'],
block: false as ButtonProps['block'] | MobileButtonProps['block'],
type: 'primary' as ButtonProps['type'] | MobileButtonProps['type'],
executeText: '',
},
methods: {
getEfIds() {
const { efPaths } = this.props;
const { oakFullpath } = this.state;
assert(efPaths);
if (oakFullpath) {
const ids = efPaths.map(
(path) => {
const path2 = path ? `${oakFullpath}.path` : oakFullpath;
const data = this.features.runningTree.getFreshValue(path2);
if (data) {
return (data as EntityDict['extraFile']['OpSchema'][]).map(
ele => ele.id
);
}
}
).flat().filter(
ele => !!ele
) as string[];
return ids;
}
return [];
},
upload() {
}
}
});

View File

@ -0,0 +1,5 @@
{
"executing": "%{text}中...",
"upload": "上传",
"uploading": "上传中"
}

View File

@ -0,0 +1,53 @@
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';
export default function render(props: WebComponentProps<EntityDict, any, true, {
state: FileState;
size?: ButtonProps['size'];
block?: ButtonProps['block'];
type?: ButtonProps['type'];
executeText?: string;
}, {
upload: () => Promise<void>
}>) {
const { state, oakExecutable, oakExecuting, oakDirty,
size, block, type, executeText } = props.data;
const { t, upload, execute } = props.methods;
const disabled = oakExecuting || ['uploading'].includes(state) || (oakExecutable === false && ['uploaded'].includes(state));
let text = executeText || t('common::action.confirm');
if (oakExecuting) {
text = t('executing', { text });
}
else if (oakExecutable === false) {
if (state === 'uploading') {
text = t('uploading');
}
else if (['failed', 'local'].includes(state)) {
text = t('upload');
}
}
return (
<Button
type={type}
size={size}
block={block}
disabled={disabled}
onClick={async () => {
if (oakExecutable) {
await execute();
await upload();
}
else {
await upload();
}
}}
>
{text}
</Button>
);
}

View File

@ -0,0 +1,53 @@
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';
export default function render(props: WebComponentProps<EntityDict, any, true, {
state: FileState;
size?: ButtonProps['size'];
block?: ButtonProps['block'];
type?: ButtonProps['type'];
executeText?: string;
}, {
upload: () => Promise<void>
}>) {
const { state, oakExecutable, oakExecuting, oakDirty,
size, block, type, executeText } = props.data;
const { t, upload, execute } = props.methods;
const disabled = oakExecuting || ['uploading'].includes(state) || (oakExecutable === false && ['uploaded'].includes(state));
let text = executeText || t('common::action.confirm');
if (oakExecuting) {
text = t('executing', { text });
}
else if (oakExecutable === false) {
if (state === 'uploading') {
text = t('uploading');
}
else if (['failed', 'local'].includes(state)) {
text = t('upload');
}
}
return (
<Button
type={type}
size={size}
block={block}
disabled={disabled}
onClick={async () => {
if (oakExecutable) {
await execute();
await upload();
}
else {
await upload();
}
}}
>
{text}
</Button>
);
}

View File

@ -0,0 +1,8 @@
{
"component": true,
"usingComponents": {
"l-icon": "@oak-frontend-base/miniprogram_npm/lin-ui/icon/index",
"l-button": "@oak-frontend-base/miniprogram_npm/lin-ui/button/index",
"oak-display": "../display/index"
}
}

View File

@ -1,4 +1,5 @@
import { generateNewId } from 'oak-domain/lib/utils/uuid';
import { pull } from 'oak-domain/lib/utils/lodash';
import assert from 'assert';
import Dialog from '../../../utils/dialog/index';
import { EntityDict } from '../../../oak-app-domain';
@ -65,6 +66,7 @@ export default OakComponent({
},
],
properties: {
bucket: '',
removeLater: true,
autoUpload: false,
maxNumber: 20,
@ -87,7 +89,6 @@ export default OakComponent({
disableAdd: false,
// 下按按钮隐藏
disableDownload: false,
disabled: false,
type: 'file' as ExtraFile['type'],
origin: 'qiniu' as ExtraFile['origin'],
tag1: '',
@ -152,15 +153,16 @@ export default OakComponent({
};
},
methods: {
onDeleteByWeb(file: EnhancedExtraFile) {
console.log(file);
onRemove(file: EnhancedExtraFile) {
this.removeItem(file.id);
this.features.extraFile2.removeLocalFiles([file.id]);
},
addExtraFileInner(options: {
name: string;
fileType: string;
size: number;
}, file: File | string) {
const { type, origin, tag1, tag2, entity, entityId, autoUpload } = this.props;
const { type, origin, tag1, tag2, entity, entityId, bucket } = this.props;
const { name, fileType, size } = options;
const extension = name.substring(name.lastIndexOf('.') + 1);
const filename = name.substring(0, name.lastIndexOf('.'));
@ -168,6 +170,7 @@ export default OakComponent({
const sort = files.length * 10000;
const id = this.addItem({
bucket,
origin,
type,
tag1,
@ -198,6 +201,7 @@ export default OakComponent({
T2,
true,
{
bucket: string, // 上传的存储桶位置
removeLater: boolean,
autoUpload: boolean,
maxNumber: number,

View File

@ -0,0 +1,7 @@
.oak-upload {
&__tips {
display: block;
color: var(--oak-text-color-placeholder);
margin-top: 8px;
}
}

View File

@ -79,7 +79,6 @@ export default function render(props: WebComponentProps<
theme?: Theme;
tips?: string;
beforeUpload?: (file: File) => Promise<boolean>;
disabled?: boolean;
style?: Record<string, string>;
className?: string;
directory?: boolean;
@ -88,12 +87,11 @@ export default function render(props: WebComponentProps<
showUploadList?: boolean;
children?: JSX.Element;
disableInsert?: boolean;
disableAdd?: boolean;
disableDownload?: boolean;
disableDelete?: boolean;
preview?: boolean;
}, {
onDeleteByWeb: (file: UploadFile) => void;
onRemove: (file: UploadFile) => void;
addFileByWeb: (file: UploadFile) => void;
}>) {
const {
@ -104,7 +102,6 @@ export default function render(props: WebComponentProps<
theme = "image",
tips,
beforeUpload,
disabled,
style,
className,
directory = false,
@ -114,13 +111,12 @@ export default function render(props: WebComponentProps<
showUploadList = true,
files = [],
disableInsert = false,
disableAdd = false,
disableDownload = false,
disableDelete = false,
preview = true,
} = props.data;
const { t, onDeleteByWeb, addFileByWeb } = props.methods;
const { t, onRemove, addFileByWeb } = props.methods;
const listType = getListType(theme);
const getUploadButton = () => {
@ -211,7 +207,6 @@ export default function render(props: WebComponentProps<
<Upload
className={classNames(Style["oak-upload__upload"], className)}
style={style}
disabled={disabled}
directory={directory}
showUploadList={
showUploadList
@ -232,14 +227,15 @@ export default function render(props: WebComponentProps<
return false;
}}
multiple={multiple}
maxCount={maxNumber}
accept={accept}
listType={listType}
fileList={transformToUploadFile()}
onChange={({ file, fileList, event }) => {
addFileByWeb(file);
if (file instanceof File) {
addFileByWeb(file);
}
}}
onRemove={onDeleteByWeb}
onRemove={onRemove}
onPreview={onPreview}
onDownload={onDownload}
itemRender={(originNode, currentFile, currentFileList) => {
@ -253,7 +249,7 @@ export default function render(props: WebComponentProps<
);
}}
>
{!disableInsert && !disableAdd ? getUploadButton() : null}
{!disableInsert && files.length < maxNumber ? getUploadButton() : null}
</Upload>
</DndProvider>

View File

@ -137,6 +137,18 @@ const i18ns: I18n[] = [
"weChat-authorization-login-successful": "微信授权登录成功"
}
},
{
id: "d294bc3cfdc260f1bcc6b96b4b9b49b0",
namespace: "oak-general-business-c-extraFile-commit",
language: "zh-CN",
module: "oak-general-business",
position: "src/components/extraFile/commit",
data: {
"executing": "%{text}中...",
"upload": "上传",
"uploading": "上传中"
}
},
{
id: "5c2e6feed554bc476664f2a80e09677b",
namespace: "oak-general-business-c-extraFile-forUrl",
@ -164,11 +176,11 @@ const i18ns: I18n[] = [
}
},
{
id: "d30e4a47c1d768d74efae1fade371595",
namespace: "oak-general-business-c-extraFile-gallery2",
id: "a0e4461f6283d6ecd0b6bba64c0560c2",
namespace: "oak-general-business-c-extraFile-upload",
language: "zh-CN",
module: "oak-general-business",
position: "src/components/extraFile/gallery2",
position: "src/components/extraFile/upload",
data: {
"choosePicture": "请选择图片",
"chooseFile": "请选择文件"