移除 extrafile下废弃的文件
This commit is contained in:
parent
2eac154a18
commit
1a7547bb52
|
|
@ -1,6 +0,0 @@
|
||||||
/// <reference types="react" />
|
|
||||||
import { EntityDict } from '../../../oak-app-domain';
|
|
||||||
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<EntityDict, "extraFile", false, {
|
|
||||||
mode: string;
|
|
||||||
}>) => import("react").ReactElement<any, string | import("react").JSXElementConstructor<any>>;
|
|
||||||
export default _default;
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
export default OakComponent({
|
|
||||||
entity: 'extraFile',
|
|
||||||
isList: false,
|
|
||||||
projection: {
|
|
||||||
id: 1,
|
|
||||||
tag1: 1,
|
|
||||||
origin: 1,
|
|
||||||
bucket: 1,
|
|
||||||
objectId: 1,
|
|
||||||
filename: 1,
|
|
||||||
extra1: 1,
|
|
||||||
extension: 1,
|
|
||||||
type: 1,
|
|
||||||
entity: 1,
|
|
||||||
entityId: 1,
|
|
||||||
},
|
|
||||||
formData: ({ data: extraFile, features }) => {
|
|
||||||
return {
|
|
||||||
url: features.extraFile.getUrl(extraFile),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
wechatMp: {
|
|
||||||
externalClasses: ['oak-class'],
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
// 图片显示模式
|
|
||||||
mode: 'aspectFit',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
{}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
/** index.wxss **/
|
|
||||||
|
|
||||||
.image {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
border: 1rpx solid #eee;
|
|
||||||
border-radius: 4rpx;
|
|
||||||
}
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
<!-- index.wxml -->
|
|
||||||
<image src="{{url}}" mode="{{mode}}" class="image oak-class"/>
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
export default function render(): import("react/jsx-runtime").JSX.Element;
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
import { jsx as _jsx } from "react/jsx-runtime";
|
|
||||||
export default function render() {
|
|
||||||
return (_jsx("div", { children: "\u672A\u5B9E\u73B0" }));
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
|
|
||||||
|
|
||||||
.container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
@ -1,34 +1,16 @@
|
||||||
import { EntityDict } from '../../../oak-app-domain';
|
import { EntityDict } from '../../../oak-app-domain';
|
||||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/types/Entity';
|
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/types/Entity';
|
||||||
import { ReactComponentProps } from 'oak-frontend-base/lib/types/Page';
|
import { ReactComponentProps } from 'oak-frontend-base/lib/types/Page';
|
||||||
type SourceType = 'album' | 'camera';
|
|
||||||
type Theme = 'file' | 'image' | 'image-flow' | 'custom';
|
|
||||||
type ImgMode = 'scaleToFill' | 'aspectFit' | 'aspectFill' | 'widthFix' | "heightFix" | 'top' | 'bottom' | 'left' | 'right' | 'center' | 'top left' | 'top right' | 'bottom left' | 'bottom right';
|
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, {
|
declare const _default: <ED2 extends EntityDict & BaseEntityDict, T2 extends keyof ED2>(props: ReactComponentProps<ED2, T2, true, {
|
||||||
removeLater: boolean;
|
|
||||||
autoUpload: boolean;
|
|
||||||
maxNumber: number;
|
|
||||||
extension: string[];
|
|
||||||
selectCount: number;
|
|
||||||
sourceType: SourceType[];
|
|
||||||
mediaType: ('image' | 'video')[];
|
|
||||||
mode: ImgMode;
|
mode: ImgMode;
|
||||||
size: number;
|
size: number;
|
||||||
showUploadList: boolean;
|
|
||||||
accept: string;
|
|
||||||
disablePreview: boolean;
|
disablePreview: boolean;
|
||||||
disableDelete: boolean;
|
|
||||||
disableAdd: boolean;
|
|
||||||
disableDownload: boolean;
|
disableDownload: boolean;
|
||||||
disabled: boolean;
|
|
||||||
type: string;
|
|
||||||
origin: string;
|
|
||||||
tag1: string;
|
tag1: string;
|
||||||
tag2: string;
|
tag2: string;
|
||||||
entity: keyof ED2;
|
entity: keyof ED2;
|
||||||
entityId: string;
|
entityId: string;
|
||||||
theme: Theme;
|
style: string;
|
||||||
showUploadProgress: boolean;
|
|
||||||
children?: React.ReactNode;
|
|
||||||
}>) => React.ReactElement;
|
}>) => React.ReactElement;
|
||||||
export default _default;
|
export default _default;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
import { generateNewId } from 'oak-domain/lib/utils/uuid';
|
;
|
||||||
import { assert } from 'oak-domain/lib/utils/assert';
|
|
||||||
import Dialog from '../../../utils/dialog/index';
|
|
||||||
export default OakComponent({
|
export default OakComponent({
|
||||||
entity: 'extraFile',
|
entity: 'extraFile',
|
||||||
isList: true,
|
isList: true,
|
||||||
|
|
@ -22,26 +20,33 @@ export default OakComponent({
|
||||||
isBridge: 1,
|
isBridge: 1,
|
||||||
uploadState: 1,
|
uploadState: 1,
|
||||||
},
|
},
|
||||||
formData({ data: originalFiles, features }) {
|
features: ['extraFile'],
|
||||||
let files = originalFiles
|
formData({ data, features }) {
|
||||||
?.filter((ele) => !ele.$$deleteAt$$)
|
let files = data?.sort((ele1, ele2) => ele1.sort - ele2.sort);
|
||||||
.sort((ele1, ele2) => ele1.sort - ele2.sort);
|
|
||||||
if (this.props.tag1) {
|
if (this.props.tag1) {
|
||||||
files = files?.filter((ele) => ele?.tag1 === this.props.tag1);
|
files = files?.filter((ele) => ele?.tag1 === this.props.tag1);
|
||||||
}
|
}
|
||||||
if (this.props.tag2) {
|
if (this.props.tag2) {
|
||||||
files = files?.filter((ele) => ele?.tag2 === this.props.tag2);
|
files = files?.filter((ele) => ele?.tag2 === this.props.tag2);
|
||||||
}
|
}
|
||||||
|
const files2 = files.map((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,
|
||||||
|
fileName,
|
||||||
|
...ele,
|
||||||
|
};
|
||||||
|
});
|
||||||
return {
|
return {
|
||||||
files,
|
files: files2,
|
||||||
disableInsert: this.props.maxNumber === 0 ||
|
|
||||||
files?.length >= this.props.maxNumber,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
// 根据 size 不同,计算的图片显示大小不同
|
// 根据 size 不同,计算的图片显示大小不同
|
||||||
itemSizePercentage: '',
|
itemSizePercentage: '',
|
||||||
fileList: {},
|
|
||||||
},
|
},
|
||||||
wechatMp: {
|
wechatMp: {
|
||||||
externalClasses: ['oak-class', 'oak-item-class'],
|
externalClasses: ['oak-class', 'oak-item-class'],
|
||||||
|
|
@ -62,48 +67,17 @@ export default OakComponent({
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
properties: {
|
properties: {
|
||||||
removeLater: true,
|
|
||||||
autoUpload: false,
|
|
||||||
maxNumber: 20,
|
|
||||||
extension: [],
|
|
||||||
selectCount: 1,
|
|
||||||
sourceType: ['album', 'camera'],
|
|
||||||
mediaType: ['image'],
|
|
||||||
mode: 'aspectFit',
|
mode: 'aspectFit',
|
||||||
size: 3,
|
size: 3,
|
||||||
showUploadList: true,
|
|
||||||
showUploadProgress: false,
|
|
||||||
accept: 'image/*',
|
|
||||||
disablePreview: false,
|
disablePreview: false,
|
||||||
disableDelete: false,
|
|
||||||
disableAdd: false,
|
|
||||||
disableDownload: false,
|
disableDownload: false,
|
||||||
disabled: false,
|
|
||||||
type: '',
|
|
||||||
origin: '',
|
|
||||||
tag1: '',
|
tag1: '',
|
||||||
tag2: '',
|
tag2: '',
|
||||||
entity: '',
|
entity: '',
|
||||||
entityId: '',
|
entityId: '',
|
||||||
theme: 'image',
|
style: '',
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getUrl(extraFile) {
|
|
||||||
const { fileList } = this.state;
|
|
||||||
if (fileList[extraFile?.id]) {
|
|
||||||
const url = this.features.extraFile.getUrl(Object.assign({}, extraFile, {
|
|
||||||
extra1: fileList[extraFile?.id],
|
|
||||||
}));
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
return this.features.extraFile.getUrl(extraFile);
|
|
||||||
},
|
|
||||||
getFileName(extraFile) {
|
|
||||||
return this.features.extraFile.getFileName(extraFile);
|
|
||||||
},
|
|
||||||
formatBytes(value) {
|
|
||||||
return this.features.extraFile.formatBytes(value);
|
|
||||||
},
|
|
||||||
/**
|
/**
|
||||||
* 获取组件内部节点位置信息(单个)
|
* 获取组件内部节点位置信息(单个)
|
||||||
* @param component 组件实例
|
* @param component 组件实例
|
||||||
|
|
@ -129,378 +103,21 @@ export default OakComponent({
|
||||||
const windowWidth = wx.getSystemInfoSync().windowWidth;
|
const windowWidth = wx.getSystemInfoSync().windowWidth;
|
||||||
return (750 / windowWidth) * px;
|
return (750 / windowWidth) * px;
|
||||||
},
|
},
|
||||||
async chooseMediaByMp() {
|
async onPreviewByMp(event) {
|
||||||
const { selectCount, mediaType, sourceType } = this.props;
|
const files = this.state.files;
|
||||||
try {
|
|
||||||
const { errMsg, tempFiles } = await wx.chooseMedia({
|
|
||||||
count: selectCount,
|
|
||||||
mediaType,
|
|
||||||
sourceType,
|
|
||||||
});
|
|
||||||
if (errMsg !== 'chooseMedia:ok') {
|
|
||||||
this.triggerEvent('error', {
|
|
||||||
level: 'warning',
|
|
||||||
msg: errMsg,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
await Promise.all(tempFiles.map(async (tempExtraFile) => {
|
|
||||||
const { tempFilePath, thumbTempFilePath, fileType, size, } = tempExtraFile;
|
|
||||||
const filePath = tempFilePath || thumbTempFilePath;
|
|
||||||
const fileFullName = filePath.match(/[^/]+(?!.*\/)/g)[0];
|
|
||||||
await this.pushExtraFile({
|
|
||||||
name: fileFullName,
|
|
||||||
fileType,
|
|
||||||
size,
|
|
||||||
extra1: filePath,
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
if (err.errMsg !== 'chooseMedia:fail cancel') {
|
|
||||||
this.triggerEvent('error', {
|
|
||||||
level: 'error',
|
|
||||||
msg: err.errMsg,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async chooseFileByMp() {
|
|
||||||
const { selectCount, extension } = this.props;
|
|
||||||
try {
|
|
||||||
const { errMsg, tempFiles } = await wx.chooseMessageFile({
|
|
||||||
count: selectCount,
|
|
||||||
type: 'all',
|
|
||||||
extension,
|
|
||||||
});
|
|
||||||
if (errMsg !== 'chooseMessageFile:ok') {
|
|
||||||
this.triggerEvent('error', {
|
|
||||||
level: 'warning',
|
|
||||||
msg: errMsg,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
await Promise.all(tempFiles.map(async (tempExtraFile) => {
|
|
||||||
const { path, type, size, name } = tempExtraFile;
|
|
||||||
await this.pushExtraFile({
|
|
||||||
name,
|
|
||||||
fileType: type,
|
|
||||||
size,
|
|
||||||
extra1: path,
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
if (err.errMsg !== 'chooseMessageFile:fail cancel') {
|
|
||||||
this.triggerEvent('error', {
|
|
||||||
level: 'error',
|
|
||||||
msg: err.errMsg,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onPickByMp() {
|
|
||||||
const { theme } = this.props;
|
|
||||||
if (['image', 'image-flow'].includes(theme)) {
|
|
||||||
this.chooseMediaByMp();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.chooseFileByMp();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async onPickByWeb(uploadFiles, callback) {
|
|
||||||
const { files } = this.state;
|
|
||||||
const currentSort = files?.length
|
|
||||||
? files[files.length - 1].sort
|
|
||||||
: 0;
|
|
||||||
await Promise.all(uploadFiles.map(async (uploadFile, index) => {
|
|
||||||
const { name, type, size, originFileObj } = uploadFile;
|
|
||||||
await this.pushExtraFile({
|
|
||||||
name,
|
|
||||||
fileType: type,
|
|
||||||
size,
|
|
||||||
extra1: originFileObj,
|
|
||||||
sort: currentSort + (index + 1) * 100,
|
|
||||||
}, callback);
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
async pushExtraFile(options, callback) {
|
|
||||||
const { type, origin, tag1, tag2, entity, entityId, autoUpload } = this.props;
|
|
||||||
const { name, extra1, fileType, size, sort } = options;
|
|
||||||
const extension = name.substring(name.lastIndexOf('.') + 1);
|
|
||||||
const filename = name.substring(0, name.lastIndexOf('.'));
|
|
||||||
assert(entity, '必须传入entity');
|
|
||||||
assert(origin === 'qiniu', '目前只支持七牛上传'); // 目前只支持七牛上传
|
|
||||||
const updateData = {
|
|
||||||
origin,
|
|
||||||
type: type || 'file',
|
|
||||||
tag1,
|
|
||||||
tag2,
|
|
||||||
objectId: generateNewId(),
|
|
||||||
entity,
|
|
||||||
filename,
|
|
||||||
size,
|
|
||||||
extension,
|
|
||||||
fileType,
|
|
||||||
entityId,
|
|
||||||
sort,
|
|
||||||
};
|
|
||||||
// autoUpload为true, 选择直接上传七牛,再提交extraFile
|
|
||||||
if (autoUpload) {
|
|
||||||
if (callback) {
|
|
||||||
callback(updateData, 'uploading');
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
this.addItem(updateData);
|
|
||||||
await this.execute();
|
|
||||||
if (callback) {
|
|
||||||
callback(updateData, 'success');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
if (callback) {
|
|
||||||
callback(updateData, 'failed');
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const id = this.addItem(updateData);
|
|
||||||
this.setState({
|
|
||||||
fileList: Object.assign(this.state.fileList, {
|
|
||||||
[id]: extra1,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async onItemTapped(event) {
|
|
||||||
const { files } = this.state;
|
|
||||||
const { index } = event.currentTarget.dataset;
|
const { index } = event.currentTarget.dataset;
|
||||||
const imageUrl = this.features.extraFile.getUrl(files[index]);
|
const imageUrl = files[index].url;
|
||||||
const urls = files
|
const urls = files?.filter((ele) => !!ele).map((ele) => ele.url);
|
||||||
?.filter((ele) => !!ele)
|
|
||||||
.map((ele) => this.features.extraFile.getUrl(ele));
|
|
||||||
const detail = {
|
|
||||||
all: files,
|
|
||||||
index,
|
|
||||||
urls,
|
|
||||||
current: imageUrl,
|
|
||||||
};
|
|
||||||
this.triggerEvent('tap', detail);
|
|
||||||
// 预览图片
|
// 预览图片
|
||||||
if (!this.props.disablePreview) {
|
if (!this.props.disablePreview) {
|
||||||
const result = await wx.previewImage({
|
const result = await wx.previewImage({
|
||||||
urls: urls,
|
urls: urls,
|
||||||
current: imageUrl,
|
current: imageUrl,
|
||||||
});
|
});
|
||||||
this.triggerEvent('preview', detail);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async onDeleteByMp(event) {
|
|
||||||
const { value } = event.currentTarget.dataset;
|
|
||||||
const { id, bucket, origin, uploadState } = value;
|
|
||||||
const { removeLater } = this.props;
|
|
||||||
const { fileList } = this.state;
|
|
||||||
if (removeLater || !uploadState) {
|
|
||||||
this.removeItem(id);
|
|
||||||
Object.assign(fileList, {
|
|
||||||
[id]: null,
|
|
||||||
});
|
|
||||||
this.setState({
|
|
||||||
fileList,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const result = await wx.showModal({
|
|
||||||
title: '确认删除吗',
|
|
||||||
content: '删除现有文件',
|
|
||||||
});
|
|
||||||
const { confirm } = result;
|
|
||||||
if (confirm) {
|
|
||||||
this.removeItem(id);
|
|
||||||
Object.assign(fileList, {
|
|
||||||
id: null,
|
|
||||||
});
|
|
||||||
this.setState({
|
|
||||||
fileList,
|
|
||||||
});
|
|
||||||
await this.execute();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async onDeleteByWeb(value) {
|
|
||||||
const { id, bucket, origin, uploadState } = value;
|
|
||||||
const { removeLater = true } = this.props;
|
|
||||||
const { fileList } = this.state;
|
|
||||||
// 如果 removeLater为true 或 origin === 'qiniu' 且 bucket不存在
|
|
||||||
if (removeLater || !uploadState) {
|
|
||||||
this.removeItem(id);
|
|
||||||
Object.assign(fileList, {
|
|
||||||
id: null,
|
|
||||||
});
|
|
||||||
this.setState({
|
|
||||||
fileList,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const confirm = Dialog.confirm({
|
|
||||||
title: '确认删除当前文件?',
|
|
||||||
content: '删除后,文件不可恢复',
|
|
||||||
cancelText: '取消',
|
|
||||||
okText: '确定',
|
|
||||||
onOk: async (e) => {
|
|
||||||
this.removeItem(id);
|
|
||||||
Object.assign(fileList, {
|
|
||||||
id: null,
|
|
||||||
});
|
|
||||||
this.setState({
|
|
||||||
fileList,
|
|
||||||
});
|
|
||||||
await this.execute();
|
|
||||||
confirm.destroy();
|
|
||||||
},
|
|
||||||
onCancel: (e) => {
|
|
||||||
confirm.destroy();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async onDownloadByMp(event) {
|
|
||||||
const { value } = event.currentTarget.dataset;
|
|
||||||
const fileUrl = this.features.extraFile.getUrl(value);
|
|
||||||
const name = this.features.extraFile.getFileName(value);
|
|
||||||
const that = this;
|
|
||||||
wx.showLoading({
|
|
||||||
title: '下载请求中...',
|
|
||||||
});
|
|
||||||
wx.downloadFile({
|
|
||||||
url: fileUrl,
|
|
||||||
success: function (res) {
|
|
||||||
const filePath = res.tempFilePath || res.filePath;
|
|
||||||
const fs = wx.getFileSystemManager();
|
|
||||||
fs.saveFile({
|
|
||||||
tempFilePath: filePath,
|
|
||||||
success: (res) => {
|
|
||||||
wx.hideLoading();
|
|
||||||
const savedFilePath = res.savedFilePath;
|
|
||||||
// 打开文件
|
|
||||||
wx.openDocument({
|
|
||||||
filePath: savedFilePath,
|
|
||||||
showMenu: true,
|
|
||||||
success: function (res) {
|
|
||||||
// console.log('打开文档成功');
|
|
||||||
},
|
|
||||||
fail: function (res) {
|
|
||||||
const { errMsg } = res;
|
|
||||||
if (errMsg.includes('fail filetype not supported')) {
|
|
||||||
that.setMessage({
|
|
||||||
type: 'error',
|
|
||||||
content: '该文件类型不支持打开',
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
that.setMessage({
|
|
||||||
type: 'error',
|
|
||||||
content: '该文件类型打开失败',
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
fail: function (res) {
|
|
||||||
wx.hideLoading();
|
|
||||||
that.setMessage({
|
|
||||||
type: 'error',
|
|
||||||
content: '保存文件失败',
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
fail: function (res) {
|
|
||||||
wx.hideLoading();
|
|
||||||
that.setMessage({
|
|
||||||
type: 'error',
|
|
||||||
content: '下载文件失败',
|
|
||||||
});
|
|
||||||
},
|
|
||||||
complete: function (res) { },
|
|
||||||
});
|
|
||||||
},
|
|
||||||
async onOpenByMp(event) {
|
|
||||||
const { value } = event.currentTarget.dataset;
|
|
||||||
const fileUrl = this.features.extraFile.getUrl(value);
|
|
||||||
const that = this;
|
|
||||||
let extension = value.extension.toLowerCase();
|
|
||||||
let extensions = [
|
|
||||||
'doc',
|
|
||||||
'docx',
|
|
||||||
'xls',
|
|
||||||
'xlsx',
|
|
||||||
'ppt',
|
|
||||||
'pptx',
|
|
||||||
'pdf',
|
|
||||||
]; //openDocument fileType目前只支持范围
|
|
||||||
if (!extensions.includes(extension)) {
|
|
||||||
this.setMessage({
|
|
||||||
type: 'error',
|
|
||||||
content: `目前仅支持打开${extensions.join(',')}类型的文件`,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
wx.showLoading({
|
|
||||||
title: '下载请求中...',
|
|
||||||
});
|
|
||||||
wx.downloadFile({
|
|
||||||
url: fileUrl,
|
|
||||||
success: function (res) {
|
|
||||||
const filePath = res.tempFilePath || res.filePath;
|
|
||||||
wx.hideLoading();
|
|
||||||
wx.openDocument({
|
|
||||||
//打开文件
|
|
||||||
filePath: filePath,
|
|
||||||
fileType: extension,
|
|
||||||
showMenu: true,
|
|
||||||
success: function () {
|
|
||||||
//console.log(`打开文件成功`);
|
|
||||||
},
|
|
||||||
fail: function (res) {
|
|
||||||
const { errMsg } = res;
|
|
||||||
if (errMsg.includes('fail filetype not supported')) {
|
|
||||||
that.setMessage({
|
|
||||||
type: 'error',
|
|
||||||
content: '该文件类型不支持打开',
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
that.setMessage({
|
|
||||||
type: 'error',
|
|
||||||
content: '该文件类型打开失败',
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
fail: function (res) {
|
|
||||||
wx.hideLoading();
|
|
||||||
that.setMessage({
|
|
||||||
type: 'error',
|
|
||||||
content: '下载文件失败',
|
|
||||||
});
|
|
||||||
},
|
|
||||||
complete: function (res) { },
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
listeners: {
|
listeners: {
|
||||||
maxNumber(prev, next) {
|
|
||||||
if (this.state.oakFullpath) {
|
|
||||||
if (prev.maxNumber !== next.maxNumber) {
|
|
||||||
this.reRender();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async size(prev, next) {
|
async size(prev, next) {
|
||||||
if (process.env.OAK_PLATFORM === 'wechatMp') {
|
if (process.env.OAK_PLATFORM === 'wechatMp') {
|
||||||
const size = next.size;
|
const size = next.size;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,4 @@
|
||||||
{
|
{
|
||||||
"component": true,
|
"component": true,
|
||||||
"usingComponents": {
|
"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"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,94 +59,3 @@ each(range(2, 10), {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-list__remove {
|
|
||||||
position: absolute;
|
|
||||||
right: 10rpx;
|
|
||||||
top: 10rpx;
|
|
||||||
height: 40rpx;
|
|
||||||
width: 40rpx;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: rgb(0 0 0 / 40%);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-list__item--add {
|
|
||||||
border: 1rpx solid #eee;
|
|
||||||
border-radius: 4rpx;
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-list__image--add {
|
|
||||||
visibility: hidden;
|
|
||||||
position: absolute;
|
|
||||||
width: 50%;
|
|
||||||
height: 50%;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-list__item-slot-wrapper {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-list__item-slot-wrapper:empty+.file-list__image--add {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.file-list-flow__container {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
.file-list-flow__item {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
margin: 10rpx 10rpx 10rpx 0;
|
|
||||||
}
|
|
||||||
.file-list-flow__item--name {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
fleX: 1;
|
|
||||||
font-size: 28rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-list-flow__item--btns {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-list-flow__download {
|
|
||||||
width: 48rpx;
|
|
||||||
height: 48rpx;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-list-flow__remove {
|
|
||||||
width: 48rpx;
|
|
||||||
height: 48rpx;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
@ -1,46 +1,9 @@
|
||||||
<block wx:if="{{ theme === 'image' || theme === 'image-flow' }}">
|
<view class="file-list__container oak-class">
|
||||||
<view class="file-list__container oak-class">
|
|
||||||
<block wx:for="{{files}}" wx:key="index">
|
|
||||||
<block wx:if="{{item}}">
|
|
||||||
<view class="file-list__item file-list__item--{{size}} oak-item-class" style="{{itemSizePercentage?'width:'+itemSizePercentage+'padding-bottom:'+itemSizePercentage:'xxx'}}">
|
|
||||||
<oak-display data-index="{{index}}" bind:tap="onItemTapped" mode="{{mode}}" oakId="{{item.id}}" oakAutoUnmount="{{true}}" oakPath="{{oakFullpath}}.{{item.id}}" />
|
|
||||||
<view wx:if="{{!disableDelete}}" mut-bind:tap="onDeleteByMp" class="file-list__remove" data-value="{{item}}">
|
|
||||||
<l-icon name="close" color="#ffffff" size="18" />
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</block>
|
|
||||||
</block>
|
|
||||||
<view class="file-list__item file-list__item--add file-list__item--{{size}} oak-item-class" style="{{itemSizePercentage?'width:'+itemSizePercentage+'padding-bottom:'+itemSizePercentage:''}}" wx:if="{{!disableInsert && !disableAdd}}" bind:tap="onPickByMp">
|
|
||||||
<view class="file-list__item-slot-wrapper">
|
|
||||||
<slot />
|
|
||||||
</view>
|
|
||||||
<view class="file-list__image--add">
|
|
||||||
<l-icon name="add" size="80" />
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</block>
|
|
||||||
<block wx:else >
|
|
||||||
<view class="file-list-flow__container oak-class">
|
|
||||||
<view class="file-list-flow__item--add oak-item-add-class" wx:if="{{!disableInsert && !disableAdd}}">
|
|
||||||
<l-button bind:lintap="onPickByMp" plain="{{true}}" type="default">选择文件</l-button>
|
|
||||||
</view>
|
|
||||||
<block wx:for="{{files}}" wx:key="index">
|
<block wx:for="{{files}}" wx:key="index">
|
||||||
<block wx:if="{{item}}">
|
<block wx:if="{{item}}">
|
||||||
<view class="file-list-flow__item">
|
<view class="file-list__item file-list__item--{{size}} oak-item-class" style="{{itemSizePercentage?'width:'+itemSizePercentage+'padding-bottom:'+itemSizePercentage:'xxx'}}">
|
||||||
<view class="file-list-flow__item--name" mut-bind:tap="onOpenByMp" data-value="{{item}}">
|
<image data-index="{{index}}" bind:tap="onPreviewByMp" src="{{item.thumbUrl}}" mode="{{mode}}" class="file-list__image"/>
|
||||||
{{ item.filename }}{{ item.extension ? '.' + item.extension : '' }}
|
</view>
|
||||||
</view>
|
|
||||||
<view class="file-list-flow__item--btns">
|
|
||||||
<!-- <view wx:if="{{!disableDownload}}" mut-bind:tap="onDownloadByMp" class="file-list-flow__download" data-value="{{item}}">
|
|
||||||
<l-icon name="download" size="36" />
|
|
||||||
</view> -->
|
|
||||||
<view wx:if="{{!disableDelete}}" mut-bind:tap="onDeleteByMp" class="file-list-flow__remove" data-value="{{item}}">
|
|
||||||
<l-icon name="delete" size="36" />
|
|
||||||
</view>
|
|
||||||
<view>
|
|
||||||
</view>
|
|
||||||
</block>
|
</block>
|
||||||
</block>
|
</block>
|
||||||
</view>
|
</view>
|
||||||
</block>
|
|
||||||
|
|
@ -1,5 +1 @@
|
||||||
{
|
{}
|
||||||
"waiting": "等待上传",
|
|
||||||
"success": "上传成功",
|
|
||||||
"uploading": "上传中"
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,37 +1,18 @@
|
||||||
import { UploadFile } from "antd";
|
import { WebComponentProps } from 'oak-frontend-base';
|
||||||
import { WebComponentProps } from "oak-frontend-base";
|
import { EntityDict } from '../../../oak-app-domain';
|
||||||
import { EntityDict } from "../../../oak-app-domain";
|
type ExtraFile = EntityDict['extraFile']['OpSchema'];
|
||||||
interface NewUploadFile extends UploadFile {
|
interface EnhancedExtraFile extends ExtraFile {
|
||||||
id?: string;
|
url: string;
|
||||||
|
thumbUrl: string;
|
||||||
|
fileFullName: string;
|
||||||
}
|
}
|
||||||
type Theme = "file" | "image" | "image-flow" | "custom";
|
|
||||||
export default function render(props: WebComponentProps<EntityDict, 'extraFile', true, {
|
export default function render(props: WebComponentProps<EntityDict, 'extraFile', true, {
|
||||||
accept?: string;
|
files: EnhancedExtraFile[];
|
||||||
maxNumber?: number;
|
|
||||||
multiple?: boolean;
|
|
||||||
draggable?: boolean;
|
|
||||||
theme?: Theme;
|
|
||||||
tips?: string;
|
|
||||||
beforeUpload?: (file: File) => Promise<boolean> | boolean;
|
|
||||||
disabled?: boolean;
|
|
||||||
style?: Record<string, string>;
|
style?: Record<string, string>;
|
||||||
className?: string;
|
className?: string;
|
||||||
directory?: boolean;
|
onDownload?: (file: EnhancedExtraFile) => void;
|
||||||
onPreview?: (file: UploadFile<any>) => void;
|
|
||||||
onDownload?: (file: UploadFile<any>) => void;
|
|
||||||
showUploadList?: boolean;
|
showUploadList?: boolean;
|
||||||
children?: JSX.Element;
|
|
||||||
files?: EntityDict['extraFile']['OpSchema'][];
|
|
||||||
disableInsert?: boolean;
|
|
||||||
disableAdd?: boolean;
|
|
||||||
disableDownload?: boolean;
|
disableDownload?: boolean;
|
||||||
disableDelete?: boolean;
|
|
||||||
disablePreview?: boolean;
|
disablePreview?: boolean;
|
||||||
}, {
|
}, {}>): import("react/jsx-runtime").JSX.Element;
|
||||||
onPickByWeb: (files: UploadFile[], callback?: (file: NewUploadFile, status: string) => void) => void;
|
|
||||||
onDeleteByWeb: (file: UploadFile) => void;
|
|
||||||
getUrl: (extraFile: EntityDict['extraFile']['OpSchema']) => string;
|
|
||||||
getFileName: (extraFile: EntityDict['extraFile']['OpSchema']) => string;
|
|
||||||
formatBytes: (value: number) => string;
|
|
||||||
}>): import("react/jsx-runtime").JSX.Element;
|
|
||||||
export {};
|
export {};
|
||||||
|
|
|
||||||
|
|
@ -1,238 +1,15 @@
|
||||||
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
||||||
import React, { useState, useEffect, useCallback } from "react";
|
import { useState, useRef } from 'react';
|
||||||
import { Space, Upload, Tag, Button, Table } from "antd";
|
import { Image, ImageViewer, Space } from 'antd-mobile';
|
||||||
import { PlusOutlined } from "@ant-design/icons";
|
|
||||||
import { file2Obj } from "antd/es/upload/utils";
|
|
||||||
import classNames from "classnames";
|
|
||||||
import Style from "./web.module.less";
|
|
||||||
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";
|
|
||||||
function getListType(theme) {
|
|
||||||
const themeMap = {
|
|
||||||
file: "text",
|
|
||||||
image: "picture-card",
|
|
||||||
"image-flow": "picture",
|
|
||||||
custom: "text",
|
|
||||||
};
|
|
||||||
return themeMap[theme];
|
|
||||||
}
|
|
||||||
const type = 'DraggableUploadList';
|
|
||||||
const DraggableUploadListItem = ({ 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) {
|
export default function render(props) {
|
||||||
const { accept = 'image/*', maxNumber = 20, multiple = maxNumber !== 1, draggable = false, theme = 'image', tips, beforeUpload, disabled, style, className, directory = false, onPreview, onDownload, children, showUploadList = true, files, disableInsert = false, disableAdd = false, disableDownload = false, disableDelete = false, disablePreview = false, } = props.data;
|
const { style, className, onDownload, files = [], disableDownload = false, disablePreview = false, } = props.data;
|
||||||
const { onPickByWeb, onDeleteByWeb, updateItem, t, getFileName, getUrl, formatBytes, } = props.methods;
|
const { t } = props.methods;
|
||||||
const [newFiles, setNewFiles] = useState([]);
|
const [visible, setVisible] = useState(false);
|
||||||
const [newUploadFiles, setNewUploadFiles] = useState([]);
|
const imageViewerMultiRef = useRef(null);
|
||||||
const listType = getListType(theme);
|
return (_jsxs(_Fragment, { children: [_jsx(Space, { children: files?.map((ele, index) => (_jsx(Image, { src: ele.thumbUrl, width: 100, height: 100, fit: "contain", onClick: !disablePreview ? () => {
|
||||||
useEffect(() => {
|
setVisible(true);
|
||||||
if (files && files.length > 0) {
|
imageViewerMultiRef.current.swipeTo(index);
|
||||||
setNewFiles(files);
|
} : undefined }))) }), _jsx(ImageViewer.Multi, { ref: imageViewerMultiRef, images: files?.map((ele) => ele.url) || [], visible: visible, onClose: () => {
|
||||||
}
|
setVisible(false);
|
||||||
else {
|
} })] }));
|
||||||
setNewFiles([]);
|
|
||||||
}
|
|
||||||
}, [files]);
|
|
||||||
const extraFileToUploadFile = (extraFile) => {
|
|
||||||
let status = undefined;
|
|
||||||
switch (extraFile.uploadState) {
|
|
||||||
case 'uploading': {
|
|
||||||
status = 'uploading';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'failed': {
|
|
||||||
status = 'error';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'success': {
|
|
||||||
status = 'done';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Object.assign({}, extraFile, {
|
|
||||||
id: extraFile.id,
|
|
||||||
url: getUrl(extraFile),
|
|
||||||
thumbUrl: getUrl(extraFile),
|
|
||||||
name: getFileName(extraFile),
|
|
||||||
fileName: getFileName(extraFile),
|
|
||||||
size: extraFile.size,
|
|
||||||
type: extraFile.fileType,
|
|
||||||
uid: extraFile.id,
|
|
||||||
status,
|
|
||||||
percent: status === 'uploading' ? 50 : undefined,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const setNewUploadFilesByStatus = (file, status) => {
|
|
||||||
const { fileName, size, id } = file;
|
|
||||||
const file2 = newUploadFiles.find((ele) => ele.name?.includes(fileName) && ele.size === size);
|
|
||||||
if (file2) {
|
|
||||||
Object.assign(file2, {
|
|
||||||
status,
|
|
||||||
id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
setNewUploadFiles(newUploadFiles);
|
|
||||||
};
|
|
||||||
const customDelete = (index) => {
|
|
||||||
const arr = [...newUploadFiles];
|
|
||||||
arr.splice(index, 1);
|
|
||||||
setNewUploadFiles(arr);
|
|
||||||
};
|
|
||||||
const getUploadButton = () => {
|
|
||||||
if (children) {
|
|
||||||
return children;
|
|
||||||
}
|
|
||||||
if (listType === 'picture-card') {
|
|
||||||
return (_jsxs("div", { children: [_jsx(PlusOutlined, {}), _jsx("div", { style: { marginTop: 8 }, children: "\u8BF7\u9009\u62E9\u56FE\u7247" })] }));
|
|
||||||
}
|
|
||||||
return _jsx(Button, { type: "default", children: "\u9009\u62E9\u6587\u4EF6" });
|
|
||||||
};
|
|
||||||
const checkLimit = (num) => {
|
|
||||||
const pattern = /^\d+\.(?:9+)$/;
|
|
||||||
return pattern.test(num.toString());
|
|
||||||
};
|
|
||||||
const moveRow = useCallback((dragIndex, hoverIndex) => {
|
|
||||||
let dragRow = newFiles[dragIndex];
|
|
||||||
let sort;
|
|
||||||
if (hoverIndex === dragIndex) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (hoverIndex > dragIndex) {
|
|
||||||
if (hoverIndex === newFiles.length - 1) {
|
|
||||||
sort = newFiles[hoverIndex].sort + 100;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
sort =
|
|
||||||
(newFiles[hoverIndex].sort +
|
|
||||||
newFiles[hoverIndex + 1].sort) /
|
|
||||||
2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (hoverIndex === 0) {
|
|
||||||
sort = newFiles[hoverIndex].sort / 2;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
sort =
|
|
||||||
(newFiles[hoverIndex].sort +
|
|
||||||
newFiles[hoverIndex - 1].sort) /
|
|
||||||
2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (checkLimit(sort)) {
|
|
||||||
alert('当前的sort值为:' + sort);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
updateItem({ sort }, dragRow.id);
|
|
||||||
}, [newFiles]);
|
|
||||||
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, disabled: disabled, directory: directory, showUploadList: showUploadList
|
|
||||||
? {
|
|
||||||
showPreviewIcon: !disablePreview,
|
|
||||||
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, maxCount: maxNumber, accept: accept, listType: listType, fileList: theme === 'custom'
|
|
||||||
? []
|
|
||||||
: newFiles?.map((ele) => extraFileToUploadFile(ele)), onChange: ({ file, fileList, event }) => {
|
|
||||||
// id不存在就是file对象
|
|
||||||
if (!file.id) {
|
|
||||||
if (theme !== 'custom') {
|
|
||||||
onPickByWeb([file2Obj(file)]);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setNewUploadFiles(fileList);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, onRemove: onDeleteByWeb, onPreview: onPreview, onDownload: onDownload, itemRender: (originNode, currentFile, currentFileList) => {
|
|
||||||
return (_jsx(DraggableUploadListItem, { originNode: originNode, file: currentFile, fileList: currentFileList, moveRow: moveRow }));
|
|
||||||
}, children: !disableInsert && !disableAdd ? getUploadButton() : null }) }), tips && (_jsx("small", { className: Style['oak-upload__tips'], children: tips })), theme === 'custom' && (_jsxs(_Fragment, { children: [_jsx(Table, { dataSource: newUploadFiles || [], rowKey: "id", columns: [
|
|
||||||
{
|
|
||||||
align: 'center',
|
|
||||||
dataIndex: 'tableIndex',
|
|
||||||
title: '#',
|
|
||||||
render: (value, record, index) => index + 1,
|
|
||||||
width: 50,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
dataIndex: 'name',
|
|
||||||
title: '文件名',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
dataIndex: 'size',
|
|
||||||
title: '文件大小',
|
|
||||||
render: (value, record, index) => {
|
|
||||||
return formatBytes(value);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
dataIndex: 'status',
|
|
||||||
title: '状态',
|
|
||||||
render: (value, record, index) => {
|
|
||||||
switch (value) {
|
|
||||||
case 'success':
|
|
||||||
return (_jsx(Tag, { color: "success", children: t('success') }));
|
|
||||||
case 'uploading':
|
|
||||||
return (_jsx(Tag, { color: "processing", children: t('uploading') }));
|
|
||||||
default:
|
|
||||||
return (_jsx(Tag, { color: "warning", children: t('waiting') }));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
dataIndex: 'op',
|
|
||||||
width: 300,
|
|
||||||
title: '操作',
|
|
||||||
align: 'center',
|
|
||||||
render: (value, record, index) => {
|
|
||||||
// 只处理state的文件 这时候可以直接删除
|
|
||||||
return (_jsx(_Fragment, { children: !record.id && (_jsx(Button, { type: "link", onClick: () => {
|
|
||||||
customDelete(index);
|
|
||||||
}, children: "\u5220\u9664" })) }));
|
|
||||||
},
|
|
||||||
fixed: 'right',
|
|
||||||
},
|
|
||||||
] }), _jsx("div", { style: { display: 'flex', justifyContent: 'flex-end' }, children: _jsxs(Space, { children: [_jsx(Button, { danger: true, type: "default", onClick: () => setNewUploadFiles([]), children: "\u6E05\u7A7A" }), _jsx(Button, { type: "primary", onClick: () => {
|
|
||||||
onPickByWeb(newUploadFiles, (file, status) => {
|
|
||||||
setNewUploadFilesByStatus(file, status);
|
|
||||||
});
|
|
||||||
}, children: "\u4E0A\u4F20" })] }) })] }))] }));
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,3 @@
|
||||||
.oak-upload {
|
.container {
|
||||||
&__tips {
|
display: flex;
|
||||||
display: block;
|
|
||||||
color: var(--oak-text-color-placeholder);
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
import { EntityDict } from '../../../oak-app-domain';
|
|
||||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/types/Entity';
|
|
||||||
import { ReactComponentProps } from 'oak-frontend-base/lib/types/Page';
|
|
||||||
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, {
|
|
||||||
mode: ImgMode;
|
|
||||||
size: number;
|
|
||||||
disablePreview: boolean;
|
|
||||||
disableDownload: boolean;
|
|
||||||
tag1: string;
|
|
||||||
tag2: string;
|
|
||||||
entity: keyof ED2;
|
|
||||||
entityId: string;
|
|
||||||
style: string;
|
|
||||||
}>) => React.ReactElement;
|
|
||||||
export default _default;
|
|
||||||
|
|
@ -1,139 +0,0 @@
|
||||||
;
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
features: ['extraFile'],
|
|
||||||
formData({ data, features }) {
|
|
||||||
let files = data?.sort((ele1, ele2) => ele1.sort - ele2.sort);
|
|
||||||
if (this.props.tag1) {
|
|
||||||
files = files?.filter((ele) => ele?.tag1 === this.props.tag1);
|
|
||||||
}
|
|
||||||
if (this.props.tag2) {
|
|
||||||
files = files?.filter((ele) => ele?.tag2 === this.props.tag2);
|
|
||||||
}
|
|
||||||
const files2 = files.map((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,
|
|
||||||
fileName,
|
|
||||||
...ele,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
files: files2,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
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: {
|
|
||||||
mode: 'aspectFit',
|
|
||||||
size: 3,
|
|
||||||
disablePreview: false,
|
|
||||||
disableDownload: false,
|
|
||||||
tag1: '',
|
|
||||||
tag2: '',
|
|
||||||
entity: '',
|
|
||||||
entityId: '',
|
|
||||||
style: '',
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
/**
|
|
||||||
* 获取组件内部节点位置信息(单个)
|
|
||||||
* @param component 组件实例
|
|
||||||
* @param selector {String} css选择器
|
|
||||||
* @returns boundingClientRect() 回调函数的值
|
|
||||||
*/
|
|
||||||
async getNodeRectFromComponent(component, selector) {
|
|
||||||
return await new Promise((resolve) => {
|
|
||||||
component
|
|
||||||
.createSelectorQuery()
|
|
||||||
.select(selector)
|
|
||||||
.boundingClientRect((res) => {
|
|
||||||
resolve(res);
|
|
||||||
})
|
|
||||||
.exec();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
// * px 转 rpx
|
|
||||||
// * @param px 像素值
|
|
||||||
// */
|
|
||||||
px2rpx(px) {
|
|
||||||
const windowWidth = wx.getSystemInfoSync().windowWidth;
|
|
||||||
return (750 / windowWidth) * px;
|
|
||||||
},
|
|
||||||
async onPreviewByMp(event) {
|
|
||||||
const files = this.state.files;
|
|
||||||
const { index } = event.currentTarget.dataset;
|
|
||||||
const imageUrl = files[index].url;
|
|
||||||
const urls = files?.filter((ele) => !!ele).map((ele) => ele.url);
|
|
||||||
// 预览图片
|
|
||||||
if (!this.props.disablePreview) {
|
|
||||||
const result = await wx.previewImage({
|
|
||||||
urls: urls,
|
|
||||||
current: imageUrl,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
listeners: {
|
|
||||||
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,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
{
|
|
||||||
"component": true,
|
|
||||||
"usingComponents": {}
|
|
||||||
}
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
@import "../../../config/styles/mp/index.less";
|
|
||||||
@import "../../../config/styles/mp/mixins.less";
|
|
||||||
|
|
||||||
.file-list__container {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-list__item {
|
|
||||||
position: relative;
|
|
||||||
width: 220rpx;
|
|
||||||
padding-bottom: 220rpx;
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// size 不同时,对应的图片间距设置
|
|
||||||
// size 仅支持 1-10
|
|
||||||
each(range(2, 10), {
|
|
||||||
@valuePlusOne : @value+1;
|
|
||||||
|
|
||||||
.file-list__item--@{value}:nth-of-type(n+@{valuePlusOne}) {
|
|
||||||
margin-top : 20rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-list__item--@{value}:not(:nth-of-type(@{value}n+1)) {
|
|
||||||
margin-left : 20rpx;
|
|
||||||
}
|
|
||||||
}) // 当 size 为 null,每行会显示 3 张图片
|
|
||||||
|
|
||||||
.file-list__item--null:nth-of-type(n+4) {
|
|
||||||
margin-top: 20rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-list__item--null:not(:nth-of-type(3n+1)) {
|
|
||||||
margin-left: 20rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-list__image {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
border: 1rpx solid #eee;
|
|
||||||
border-radius: 4rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-list__item--selected {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: 10;
|
|
||||||
background-color: #000;
|
|
||||||
filter: Alpha(Opacity=50);
|
|
||||||
opacity: 0.5;
|
|
||||||
position: absolute;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-around;
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
<view class="file-list__container oak-class">
|
|
||||||
<block wx:for="{{files}}" wx:key="index">
|
|
||||||
<block wx:if="{{item}}">
|
|
||||||
<view class="file-list__item file-list__item--{{size}} oak-item-class" style="{{itemSizePercentage?'width:'+itemSizePercentage+'padding-bottom:'+itemSizePercentage:'xxx'}}">
|
|
||||||
<image data-index="{{index}}" bind:tap="onPreviewByMp" src="{{item.thumbUrl}}" mode="{{mode}}" class="file-list__image"/>
|
|
||||||
</view>
|
|
||||||
</block>
|
|
||||||
</block>
|
|
||||||
</view>
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
{}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
import { WebComponentProps } from 'oak-frontend-base';
|
|
||||||
import { EntityDict } from '../../../oak-app-domain';
|
|
||||||
type ExtraFile = EntityDict['extraFile']['OpSchema'];
|
|
||||||
interface EnhancedExtraFile extends ExtraFile {
|
|
||||||
url: string;
|
|
||||||
thumbUrl: string;
|
|
||||||
fileFullName: string;
|
|
||||||
}
|
|
||||||
export default function render(props: WebComponentProps<EntityDict, 'extraFile', true, {
|
|
||||||
files: EnhancedExtraFile[];
|
|
||||||
style?: Record<string, string>;
|
|
||||||
className?: string;
|
|
||||||
onDownload?: (file: EnhancedExtraFile) => void;
|
|
||||||
showUploadList?: boolean;
|
|
||||||
disableDownload?: boolean;
|
|
||||||
disablePreview?: boolean;
|
|
||||||
}, {}>): import("react/jsx-runtime").JSX.Element;
|
|
||||||
export {};
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
||||||
import { useState, useRef } from 'react';
|
|
||||||
import { Image, ImageViewer, Space } from 'antd-mobile';
|
|
||||||
export default function render(props) {
|
|
||||||
const { style, className, onDownload, files = [], disableDownload = false, disablePreview = false, } = props.data;
|
|
||||||
const { t } = props.methods;
|
|
||||||
const [visible, setVisible] = useState(false);
|
|
||||||
const imageViewerMultiRef = useRef(null);
|
|
||||||
return (_jsxs(_Fragment, { children: [_jsx(Space, { children: files?.map((ele, index) => (_jsx(Image, { src: ele.thumbUrl, width: 100, height: 100, fit: "contain", onClick: !disablePreview ? () => {
|
|
||||||
setVisible(true);
|
|
||||||
imageViewerMultiRef.current.swipeTo(index);
|
|
||||||
} : undefined }))) }), _jsx(ImageViewer.Multi, { ref: imageViewerMultiRef, images: files?.map((ele) => ele.url) || [], visible: visible, onClose: () => {
|
|
||||||
setVisible(false);
|
|
||||||
} })] }));
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
.container {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { generateNewId } from 'oak-domain';
|
import { generateNewId } from 'oak-domain/lib/utils/uuid';
|
||||||
import { assert } from 'oak-domain/lib/utils/assert';
|
import { assert } from 'oak-domain/lib/utils/assert';
|
||||||
;
|
;
|
||||||
export default OakComponent({
|
export default OakComponent({
|
||||||
|
|
|
||||||
|
|
@ -70,18 +70,6 @@ const i18ns = [
|
||||||
language: "zh-CN",
|
language: "zh-CN",
|
||||||
module: "oak-general-business",
|
module: "oak-general-business",
|
||||||
position: "src/components/extraFile/gallery",
|
position: "src/components/extraFile/gallery",
|
||||||
data: {
|
|
||||||
"waiting": "等待上传",
|
|
||||||
"success": "上传成功",
|
|
||||||
"uploading": "上传中"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "d30e4a47c1d768d74efae1fade371595",
|
|
||||||
namespace: "oak-general-business-c-extraFile-gallery2",
|
|
||||||
language: "zh-CN",
|
|
||||||
module: "oak-general-business",
|
|
||||||
position: "src/components/extraFile/gallery2",
|
|
||||||
data: {}
|
data: {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -72,18 +72,6 @@ const i18ns = [
|
||||||
language: "zh-CN",
|
language: "zh-CN",
|
||||||
module: "oak-general-business",
|
module: "oak-general-business",
|
||||||
position: "src/components/extraFile/gallery",
|
position: "src/components/extraFile/gallery",
|
||||||
data: {
|
|
||||||
"waiting": "等待上传",
|
|
||||||
"success": "上传成功",
|
|
||||||
"uploading": "上传中"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "d30e4a47c1d768d74efae1fade371595",
|
|
||||||
namespace: "oak-general-business-c-extraFile-gallery2",
|
|
||||||
language: "zh-CN",
|
|
||||||
module: "oak-general-business",
|
|
||||||
position: "src/components/extraFile/gallery2",
|
|
||||||
data: {}
|
data: {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
/** index.wxss **/
|
|
||||||
|
|
||||||
.image {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
border: 1rpx solid #eee;
|
|
||||||
border-radius: 4rpx;
|
|
||||||
}
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
import { EntityDict } from '../../../oak-app-domain';
|
|
||||||
|
|
||||||
export default OakComponent({
|
|
||||||
entity: 'extraFile',
|
|
||||||
isList: false,
|
|
||||||
projection: {
|
|
||||||
id: 1,
|
|
||||||
tag1: 1,
|
|
||||||
origin: 1,
|
|
||||||
bucket: 1,
|
|
||||||
objectId: 1,
|
|
||||||
filename: 1,
|
|
||||||
extra1: 1,
|
|
||||||
extension: 1,
|
|
||||||
type: 1,
|
|
||||||
entity: 1,
|
|
||||||
entityId: 1,
|
|
||||||
},
|
|
||||||
formData: ({ data: extraFile, features }) => {
|
|
||||||
return {
|
|
||||||
url: features.extraFile.getUrl(
|
|
||||||
extraFile as EntityDict['extraFile']['Schema']
|
|
||||||
),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
wechatMp: {
|
|
||||||
externalClasses: ['oak-class'],
|
|
||||||
},
|
|
||||||
properties: {
|
|
||||||
// 图片显示模式
|
|
||||||
mode: 'aspectFit' as string,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
<!-- index.wxml -->
|
|
||||||
<image src="{{url}}" mode="{{mode}}" class="image oak-class"/>
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
|
|
||||||
|
|
||||||
.container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
import React, { Component } from 'react';
|
|
||||||
|
|
||||||
export default function render() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
未实现
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +1,4 @@
|
||||||
{
|
{
|
||||||
"component": true,
|
"component": true,
|
||||||
"usingComponents": {
|
"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"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,94 +59,3 @@ each(range(2, 10), {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-list__remove {
|
|
||||||
position: absolute;
|
|
||||||
right: 10rpx;
|
|
||||||
top: 10rpx;
|
|
||||||
height: 40rpx;
|
|
||||||
width: 40rpx;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: rgb(0 0 0 / 40%);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-list__item--add {
|
|
||||||
border: 1rpx solid #eee;
|
|
||||||
border-radius: 4rpx;
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-list__image--add {
|
|
||||||
visibility: hidden;
|
|
||||||
position: absolute;
|
|
||||||
width: 50%;
|
|
||||||
height: 50%;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-list__item-slot-wrapper {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-list__item-slot-wrapper:empty+.file-list__image--add {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.file-list-flow__container {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
.file-list-flow__item {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
margin: 10rpx 10rpx 10rpx 0;
|
|
||||||
}
|
|
||||||
.file-list-flow__item--name {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
fleX: 1;
|
|
||||||
font-size: 28rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-list-flow__item--btns {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-list-flow__download {
|
|
||||||
width: 48rpx;
|
|
||||||
height: 48rpx;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-list-flow__remove {
|
|
||||||
width: 48rpx;
|
|
||||||
height: 48rpx;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
@ -1,12 +1,15 @@
|
||||||
import { generateNewId } from 'oak-domain/lib/utils/uuid';
|
|
||||||
import { assert } from 'oak-domain/lib/utils/assert';
|
|
||||||
import Dialog from '../../../utils/dialog/index';
|
|
||||||
import { EntityDict } from '../../../oak-app-domain';
|
import { EntityDict } from '../../../oak-app-domain';
|
||||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/types/Entity';
|
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/types/Entity';
|
||||||
import { ReactComponentProps } from 'oak-frontend-base/lib/types/Page';
|
import { ReactComponentProps } from 'oak-frontend-base/lib/types/Page';
|
||||||
|
|
||||||
type SourceType = 'album' | 'camera';
|
type ExtraFile = EntityDict['extraFile']['OpSchema'];
|
||||||
type Theme = 'file' | 'image' | 'image-flow' | 'custom';
|
|
||||||
|
interface EnhancedExtraFile extends ExtraFile {
|
||||||
|
url: string;
|
||||||
|
thumbUrl: string;
|
||||||
|
fileName: string;
|
||||||
|
};
|
||||||
|
|
||||||
type ImgMode = 'scaleToFill' | 'aspectFit' | 'aspectFill' | 'widthFix' | "heightFix" | 'top' | 'bottom' | 'left'
|
type ImgMode = 'scaleToFill' | 'aspectFit' | 'aspectFill' | 'widthFix' | "heightFix" | 'top' | 'bottom' | 'left'
|
||||||
| 'right' | 'center' | 'top left' | 'top right' | 'bottom left' | 'bottom right';
|
| 'right' | 'center' | 'top left' | 'top right' | 'bottom left' | 'bottom right';
|
||||||
|
|
||||||
|
|
@ -31,29 +34,37 @@ export default OakComponent({
|
||||||
isBridge: 1,
|
isBridge: 1,
|
||||||
uploadState: 1,
|
uploadState: 1,
|
||||||
},
|
},
|
||||||
formData({ data: originalFiles, features }) {
|
features: ['extraFile'],
|
||||||
let files = (
|
formData({ data, features }) {
|
||||||
originalFiles as Array<EntityDict['extraFile']['OpSchema']>
|
let files = data?.sort((ele1, ele2) => ele1.sort! - ele2.sort!);
|
||||||
)
|
|
||||||
?.filter((ele) => !ele.$$deleteAt$$)
|
|
||||||
.sort((ele1, ele2) => ele1.sort! - ele2.sort!);
|
|
||||||
if (this.props.tag1) {
|
if (this.props.tag1) {
|
||||||
files = files?.filter((ele) => ele?.tag1 === this.props.tag1);
|
files = files?.filter((ele) => ele?.tag1 === this.props.tag1);
|
||||||
}
|
}
|
||||||
if (this.props.tag2) {
|
if (this.props.tag2) {
|
||||||
files = files?.filter((ele) => ele?.tag2 === this.props.tag2);
|
files = files?.filter((ele) => ele?.tag2 === this.props.tag2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const files2 = files.map((ele) => {
|
||||||
|
const url = features.extraFile.getUrl(ele as ExtraFile);
|
||||||
|
const thumbUrl = features.extraFile.getUrl(
|
||||||
|
ele as ExtraFile,
|
||||||
|
this.props.style
|
||||||
|
);
|
||||||
|
const fileName = features.extraFile.getFileName(ele as ExtraFile);
|
||||||
|
return {
|
||||||
|
url,
|
||||||
|
thumbUrl,
|
||||||
|
fileName,
|
||||||
|
...ele,
|
||||||
|
} as EnhancedExtraFile;
|
||||||
|
});
|
||||||
return {
|
return {
|
||||||
files,
|
files: files2,
|
||||||
disableInsert:
|
|
||||||
this.props.maxNumber === 0 ||
|
|
||||||
files?.length >= this.props.maxNumber!,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
// 根据 size 不同,计算的图片显示大小不同
|
// 根据 size 不同,计算的图片显示大小不同
|
||||||
itemSizePercentage: '',
|
itemSizePercentage: '',
|
||||||
fileList: {} as Record<string, File | string>,
|
|
||||||
},
|
},
|
||||||
wechatMp: {
|
wechatMp: {
|
||||||
externalClasses: ['oak-class', 'oak-item-class'],
|
externalClasses: ['oak-class', 'oak-item-class'],
|
||||||
|
|
@ -74,51 +85,18 @@ export default OakComponent({
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
properties: {
|
properties: {
|
||||||
removeLater: true,
|
|
||||||
autoUpload: false,
|
|
||||||
maxNumber: 20,
|
|
||||||
extension: [] as string[], //小程序独有 chooseMessageFile
|
|
||||||
selectCount: 1, // 每次打开图片时,可选中的数量 小程序独有
|
|
||||||
sourceType: ['album', 'camera'] as SourceType[], // 小程序独有 chooseMedia
|
|
||||||
mediaType: ['image'] as ('image' | 'video')[], // 小程序独有 chooseMedia
|
|
||||||
mode: 'aspectFit' as ImgMode, // 图片显示模式
|
mode: 'aspectFit' as ImgMode, // 图片显示模式
|
||||||
size: 3, // 每行可显示的个数 小程序独有
|
size: 3, // 每行可显示的个数 小程序独有
|
||||||
showUploadList: true, //web独有
|
|
||||||
showUploadProgress: false, // web独有
|
|
||||||
accept: 'image/*', // web独有
|
|
||||||
disablePreview: false, // 图片是否可预览
|
disablePreview: false, // 图片是否可预览
|
||||||
disableDelete: false, // 图片是否可删除
|
|
||||||
disableAdd: false, // 上传按钮隐藏
|
|
||||||
disableDownload: false, // 下载按钮隐藏
|
disableDownload: false, // 下载按钮隐藏
|
||||||
disabled: false,
|
|
||||||
type: '',
|
|
||||||
origin: '',
|
|
||||||
tag1: '',
|
tag1: '',
|
||||||
tag2: '',
|
tag2: '',
|
||||||
entity: '' as keyof EntityDict,
|
entity: '' as keyof EntityDict,
|
||||||
entityId: '',
|
entityId: '',
|
||||||
theme: 'image' as Theme,
|
style: '',
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
getUrl(extraFile: EntityDict['extraFile']['OpSchema']) {
|
|
||||||
const { fileList } = this.state;
|
|
||||||
if (fileList[extraFile?.id]) {
|
|
||||||
const url = this.features.extraFile.getUrl(
|
|
||||||
Object.assign({}, extraFile, {
|
|
||||||
extra1: fileList[extraFile?.id],
|
|
||||||
})
|
|
||||||
);
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
return this.features.extraFile.getUrl(extraFile);
|
|
||||||
},
|
|
||||||
getFileName(extraFile: EntityDict['extraFile']['OpSchema']) {
|
|
||||||
return this.features.extraFile.getFileName(extraFile);
|
|
||||||
},
|
|
||||||
formatBytes(value: number) {
|
|
||||||
return this.features.extraFile.formatBytes(value);
|
|
||||||
},
|
|
||||||
/**
|
/**
|
||||||
* 获取组件内部节点位置信息(单个)
|
* 获取组件内部节点位置信息(单个)
|
||||||
* @param component 组件实例
|
* @param component 组件实例
|
||||||
|
|
@ -144,407 +122,22 @@ export default OakComponent({
|
||||||
const windowWidth = wx.getSystemInfoSync().windowWidth;
|
const windowWidth = wx.getSystemInfoSync().windowWidth;
|
||||||
return (750 / windowWidth) * px;
|
return (750 / windowWidth) * px;
|
||||||
},
|
},
|
||||||
async chooseMediaByMp() {
|
|
||||||
const { selectCount, mediaType, sourceType } = this.props;
|
|
||||||
try {
|
|
||||||
const { errMsg, tempFiles } = await wx.chooseMedia({
|
|
||||||
count: selectCount,
|
|
||||||
mediaType,
|
|
||||||
sourceType,
|
|
||||||
});
|
|
||||||
if (errMsg !== 'chooseMedia:ok') {
|
|
||||||
this.triggerEvent('error', {
|
|
||||||
level: 'warning',
|
|
||||||
msg: errMsg,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
await Promise.all(
|
|
||||||
tempFiles.map(async (tempExtraFile) => {
|
|
||||||
const {
|
|
||||||
tempFilePath,
|
|
||||||
thumbTempFilePath,
|
|
||||||
fileType,
|
|
||||||
size,
|
|
||||||
} = tempExtraFile;
|
|
||||||
const filePath = tempFilePath || thumbTempFilePath;
|
|
||||||
const fileFullName =
|
|
||||||
filePath.match(/[^/]+(?!.*\/)/g)![0];
|
|
||||||
await this.pushExtraFile({
|
|
||||||
name: fileFullName,
|
|
||||||
fileType,
|
|
||||||
size,
|
|
||||||
extra1: filePath,
|
|
||||||
});
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (err: any) {
|
|
||||||
console.error(err);
|
|
||||||
if (err.errMsg !== 'chooseMedia:fail cancel') {
|
|
||||||
this.triggerEvent('error', {
|
|
||||||
level: 'error',
|
|
||||||
msg: err.errMsg,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async chooseFileByMp() {
|
|
||||||
const { selectCount, extension } = this.props;
|
|
||||||
try {
|
|
||||||
const { errMsg, tempFiles } = await wx.chooseMessageFile({
|
|
||||||
count: selectCount!,
|
|
||||||
type: 'all',
|
|
||||||
extension,
|
|
||||||
});
|
|
||||||
if (errMsg !== 'chooseMessageFile:ok') {
|
|
||||||
this.triggerEvent('error', {
|
|
||||||
level: 'warning',
|
|
||||||
msg: errMsg,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
await Promise.all(
|
|
||||||
tempFiles.map(async (tempExtraFile) => {
|
|
||||||
const { path, type, size, name } = tempExtraFile;
|
|
||||||
await this.pushExtraFile({
|
|
||||||
name,
|
|
||||||
fileType: type,
|
|
||||||
size,
|
|
||||||
extra1: path,
|
|
||||||
});
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (err: any) {
|
|
||||||
console.error(err);
|
|
||||||
if (err.errMsg !== 'chooseMessageFile:fail cancel') {
|
|
||||||
this.triggerEvent('error', {
|
|
||||||
level: 'error',
|
|
||||||
msg: err.errMsg,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onPickByMp() {
|
|
||||||
const { theme } = this.props;
|
|
||||||
if (['image', 'image-flow'].includes(theme!)) {
|
|
||||||
this.chooseMediaByMp();
|
|
||||||
} else {
|
|
||||||
this.chooseFileByMp();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async onPickByWeb(
|
|
||||||
uploadFiles: any[],
|
|
||||||
callback?: (file: any, status: string) => void
|
|
||||||
) {
|
|
||||||
const { files } = this.state;
|
|
||||||
const currentSort = files?.length
|
|
||||||
? files[files.length - 1].sort
|
|
||||||
: 0;
|
|
||||||
await Promise.all(
|
|
||||||
uploadFiles.map(async (uploadFile, index) => {
|
|
||||||
const { name, type, size, originFileObj } = uploadFile;
|
|
||||||
await this.pushExtraFile(
|
|
||||||
{
|
|
||||||
name,
|
|
||||||
fileType: type,
|
|
||||||
size,
|
|
||||||
extra1: originFileObj,
|
|
||||||
sort: currentSort + (index + 1) * 100,
|
|
||||||
},
|
|
||||||
callback
|
|
||||||
);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
},
|
|
||||||
async pushExtraFile(
|
|
||||||
options: {
|
|
||||||
name: string;
|
|
||||||
extra1: any;
|
|
||||||
fileType: string;
|
|
||||||
size: number;
|
|
||||||
sort: number;
|
|
||||||
},
|
|
||||||
callback?: (file: any, status: string) => void
|
|
||||||
) {
|
|
||||||
const { type, origin, tag1, tag2, entity, entityId, autoUpload } =
|
|
||||||
this.props;
|
|
||||||
const { name, extra1, fileType, size, sort } = options;
|
|
||||||
const extension = name.substring(name.lastIndexOf('.') + 1);
|
|
||||||
const filename = name.substring(0, name.lastIndexOf('.'));
|
|
||||||
assert(entity, '必须传入entity');
|
|
||||||
assert(origin === 'qiniu', '目前只支持七牛上传'); // 目前只支持七牛上传
|
|
||||||
const updateData = {
|
|
||||||
origin,
|
|
||||||
type: type || 'file',
|
|
||||||
tag1,
|
|
||||||
tag2,
|
|
||||||
objectId: generateNewId(),
|
|
||||||
entity,
|
|
||||||
filename,
|
|
||||||
size,
|
|
||||||
extension,
|
|
||||||
fileType,
|
|
||||||
entityId,
|
|
||||||
sort,
|
|
||||||
} as EntityDict['extraFile']['CreateSingle']['data'];
|
|
||||||
// autoUpload为true, 选择直接上传七牛,再提交extraFile
|
|
||||||
if (autoUpload) {
|
|
||||||
if (callback) {
|
|
||||||
callback(updateData, 'uploading');
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
this.addItem(updateData);
|
|
||||||
await this.execute();
|
|
||||||
if (callback) {
|
|
||||||
callback(updateData, 'success');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
if (callback) {
|
|
||||||
callback(updateData, 'failed');
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const id = this.addItem(updateData);
|
|
||||||
this.setState({
|
|
||||||
fileList: Object.assign(this.state.fileList, {
|
|
||||||
[id]: extra1,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async onItemTapped(event: WechatMiniprogram.Touch) {
|
|
||||||
const { files } = this.state;
|
|
||||||
const { index } = event.currentTarget.dataset;
|
|
||||||
const imageUrl = this.features.extraFile.getUrl(files[index]);
|
|
||||||
const urls = files
|
|
||||||
?.filter((ele: EntityDict['extraFile']['Schema']) => !!ele)
|
|
||||||
.map((ele: EntityDict['extraFile']['Schema']) =>
|
|
||||||
this.features.extraFile.getUrl(ele)
|
|
||||||
);
|
|
||||||
|
|
||||||
const detail = {
|
async onPreviewByMp(event: WechatMiniprogram.Touch) {
|
||||||
all: files,
|
const files = this.state.files as EnhancedExtraFile[];
|
||||||
index,
|
const { index } = event.currentTarget.dataset;
|
||||||
urls,
|
const imageUrl = files[index].url;
|
||||||
current: imageUrl,
|
const urls = files?.filter((ele) => !!ele).map((ele) => ele.url);
|
||||||
};
|
|
||||||
this.triggerEvent('tap', detail);
|
|
||||||
// 预览图片
|
// 预览图片
|
||||||
if (!this.props.disablePreview) {
|
if (!this.props.disablePreview) {
|
||||||
const result = await wx.previewImage({
|
const result = await wx.previewImage({
|
||||||
urls: urls,
|
urls: urls,
|
||||||
current: imageUrl,
|
current: imageUrl,
|
||||||
});
|
});
|
||||||
this.triggerEvent('preview', detail);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async onDeleteByMp(event: WechatMiniprogram.Touch) {
|
|
||||||
const { value } = event.currentTarget.dataset;
|
|
||||||
const { id, bucket, origin, uploadState } = value;
|
|
||||||
const { removeLater } = this.props;
|
|
||||||
const { fileList } = this.state;
|
|
||||||
if (removeLater || !uploadState) {
|
|
||||||
this.removeItem(id);
|
|
||||||
Object.assign(fileList, {
|
|
||||||
[id]: null,
|
|
||||||
});
|
|
||||||
this.setState({
|
|
||||||
fileList,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const result = await wx.showModal({
|
|
||||||
title: '确认删除吗',
|
|
||||||
content: '删除现有文件',
|
|
||||||
});
|
|
||||||
const { confirm } = result;
|
|
||||||
if (confirm) {
|
|
||||||
this.removeItem(id);
|
|
||||||
Object.assign(fileList, {
|
|
||||||
id: null,
|
|
||||||
});
|
|
||||||
this.setState({
|
|
||||||
fileList,
|
|
||||||
});
|
|
||||||
await this.execute();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async onDeleteByWeb(value: any) {
|
|
||||||
const { id, bucket, origin, uploadState } = value;
|
|
||||||
const { removeLater = true } = this.props;
|
|
||||||
const { fileList } = this.state;
|
|
||||||
// 如果 removeLater为true 或 origin === 'qiniu' 且 bucket不存在
|
|
||||||
if (removeLater || !uploadState) {
|
|
||||||
this.removeItem(id);
|
|
||||||
Object.assign(fileList, {
|
|
||||||
id: null,
|
|
||||||
});
|
|
||||||
this.setState({
|
|
||||||
fileList,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const confirm = Dialog.confirm({
|
|
||||||
title: '确认删除当前文件?',
|
|
||||||
content: '删除后,文件不可恢复',
|
|
||||||
cancelText: '取消',
|
|
||||||
okText: '确定',
|
|
||||||
onOk: async (e: any) => {
|
|
||||||
this.removeItem(id);
|
|
||||||
Object.assign(fileList, {
|
|
||||||
id: null,
|
|
||||||
});
|
|
||||||
this.setState({
|
|
||||||
fileList,
|
|
||||||
});
|
|
||||||
await this.execute();
|
|
||||||
confirm.destroy();
|
|
||||||
},
|
|
||||||
onCancel: (e: any) => {
|
|
||||||
confirm.destroy();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async onDownloadByMp(event: WechatMiniprogram.Touch) {
|
|
||||||
const { value } = event.currentTarget.dataset;
|
|
||||||
const fileUrl = this.features.extraFile.getUrl(value);
|
|
||||||
const name = this.features.extraFile.getFileName(value);
|
|
||||||
const that = this;
|
|
||||||
wx.showLoading({
|
|
||||||
title: '下载请求中...',
|
|
||||||
});
|
|
||||||
wx.downloadFile({
|
|
||||||
url: fileUrl,
|
|
||||||
success: function (res) {
|
|
||||||
const filePath = res.tempFilePath || res.filePath;
|
|
||||||
const fs = wx.getFileSystemManager();
|
|
||||||
fs.saveFile({
|
|
||||||
tempFilePath: filePath,
|
|
||||||
success: (res) => {
|
|
||||||
wx.hideLoading();
|
|
||||||
const savedFilePath = res.savedFilePath;
|
|
||||||
// 打开文件
|
|
||||||
wx.openDocument({
|
|
||||||
filePath: savedFilePath,
|
|
||||||
showMenu: true,
|
|
||||||
success: function (res) {
|
|
||||||
// console.log('打开文档成功');
|
|
||||||
},
|
|
||||||
fail: function (res) {
|
|
||||||
const { errMsg } = res;
|
|
||||||
if (
|
|
||||||
errMsg.includes(
|
|
||||||
'fail filetype not supported'
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
that.setMessage({
|
|
||||||
type: 'error',
|
|
||||||
content: '该文件类型不支持打开',
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
that.setMessage({
|
|
||||||
type: 'error',
|
|
||||||
content: '该文件类型打开失败',
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
fail: function (res) {
|
|
||||||
wx.hideLoading();
|
|
||||||
that.setMessage({
|
|
||||||
type: 'error',
|
|
||||||
content: '保存文件失败',
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
fail: function (res) {
|
|
||||||
wx.hideLoading();
|
|
||||||
that.setMessage({
|
|
||||||
type: 'error',
|
|
||||||
content: '下载文件失败',
|
|
||||||
});
|
|
||||||
},
|
|
||||||
complete: function (res) {},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
async onOpenByMp(event: WechatMiniprogram.Touch) {
|
|
||||||
const { value } = event.currentTarget.dataset;
|
|
||||||
const fileUrl = this.features.extraFile.getUrl(value);
|
|
||||||
const that = this;
|
|
||||||
let extension = value.extension.toLowerCase();
|
|
||||||
let extensions = [
|
|
||||||
'doc',
|
|
||||||
'docx',
|
|
||||||
'xls',
|
|
||||||
'xlsx',
|
|
||||||
'ppt',
|
|
||||||
'pptx',
|
|
||||||
'pdf',
|
|
||||||
]; //openDocument fileType目前只支持范围
|
|
||||||
if (!extensions.includes(extension)) {
|
|
||||||
this.setMessage({
|
|
||||||
type: 'error',
|
|
||||||
content: `目前仅支持打开${extensions.join(',')}类型的文件`,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
wx.showLoading({
|
|
||||||
title: '下载请求中...',
|
|
||||||
});
|
|
||||||
wx.downloadFile({
|
|
||||||
url: fileUrl,
|
|
||||||
success: function (res) {
|
|
||||||
const filePath = res.tempFilePath || res.filePath;
|
|
||||||
wx.hideLoading();
|
|
||||||
wx.openDocument({
|
|
||||||
//打开文件
|
|
||||||
filePath: filePath,
|
|
||||||
fileType: extension,
|
|
||||||
showMenu: true, // 是否显示右上角菜单按钮 默认为false(看自身需求,可要可不要。后期涉及到右上角分享功能)
|
|
||||||
success: function () {
|
|
||||||
//console.log(`打开文件成功`);
|
|
||||||
},
|
|
||||||
fail: function (res) {
|
|
||||||
const { errMsg } = res;
|
|
||||||
if (
|
|
||||||
errMsg.includes('fail filetype not supported')
|
|
||||||
) {
|
|
||||||
that.setMessage({
|
|
||||||
type: 'error',
|
|
||||||
content: '该文件类型不支持打开',
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
that.setMessage({
|
|
||||||
type: 'error',
|
|
||||||
content: '该文件类型打开失败',
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
fail: function (res) {
|
|
||||||
wx.hideLoading();
|
|
||||||
that.setMessage({
|
|
||||||
type: 'error',
|
|
||||||
content: '下载文件失败',
|
|
||||||
});
|
|
||||||
},
|
|
||||||
complete: function (res) {},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
listeners: {
|
listeners: {
|
||||||
maxNumber(prev, next) {
|
|
||||||
if (this.state.oakFullpath) {
|
|
||||||
if (prev.maxNumber !== next.maxNumber) {
|
|
||||||
this.reRender();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async size(prev, next) {
|
async size(prev, next) {
|
||||||
if (process.env.OAK_PLATFORM === 'wechatMp') {
|
if (process.env.OAK_PLATFORM === 'wechatMp') {
|
||||||
const size = next.size;
|
const size = next.size;
|
||||||
|
|
@ -575,37 +168,19 @@ export default OakComponent({
|
||||||
T2,
|
T2,
|
||||||
true,
|
true,
|
||||||
{
|
{
|
||||||
removeLater: boolean;
|
|
||||||
autoUpload: boolean;
|
|
||||||
maxNumber: number;
|
|
||||||
extension: string[];
|
|
||||||
selectCount: number;
|
|
||||||
sourceType: SourceType[];
|
|
||||||
mediaType: ('image' | 'video')[];
|
|
||||||
// 图片显示模式
|
// 图片显示模式
|
||||||
mode: ImgMode;
|
mode: ImgMode;
|
||||||
// 每行可显示的个数
|
// 每行可显示的个数
|
||||||
size: number;
|
size: number;
|
||||||
showUploadList: boolean;
|
|
||||||
accept: string;
|
|
||||||
// 图片是否可预览
|
// 图片是否可预览
|
||||||
disablePreview: boolean;
|
disablePreview: boolean;
|
||||||
// 图片是否可删除
|
|
||||||
disableDelete: boolean;
|
|
||||||
// 上传按钮隐藏
|
|
||||||
disableAdd: boolean;
|
|
||||||
// 下按按钮隐藏
|
// 下按按钮隐藏
|
||||||
disableDownload: boolean;
|
disableDownload: boolean;
|
||||||
disabled: boolean;
|
|
||||||
type: string;
|
|
||||||
origin: string;
|
|
||||||
tag1: string;
|
tag1: string;
|
||||||
tag2: string;
|
tag2: string;
|
||||||
entity: keyof ED2;
|
entity: keyof ED2;
|
||||||
entityId: string;
|
entityId: string;
|
||||||
theme: Theme;
|
style: string;
|
||||||
showUploadProgress: boolean;
|
|
||||||
children?: React.ReactNode;
|
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
) => React.ReactElement;
|
) => React.ReactElement;
|
||||||
|
|
|
||||||
|
|
@ -1,46 +1,9 @@
|
||||||
<block wx:if="{{ theme === 'image' || theme === 'image-flow' }}">
|
<view class="file-list__container oak-class">
|
||||||
<view class="file-list__container oak-class">
|
|
||||||
<block wx:for="{{files}}" wx:key="index">
|
|
||||||
<block wx:if="{{item}}">
|
|
||||||
<view class="file-list__item file-list__item--{{size}} oak-item-class" style="{{itemSizePercentage?'width:'+itemSizePercentage+'padding-bottom:'+itemSizePercentage:'xxx'}}">
|
|
||||||
<oak-display data-index="{{index}}" bind:tap="onItemTapped" mode="{{mode}}" oakId="{{item.id}}" oakAutoUnmount="{{true}}" oakPath="{{oakFullpath}}.{{item.id}}" />
|
|
||||||
<view wx:if="{{!disableDelete}}" mut-bind:tap="onDeleteByMp" class="file-list__remove" data-value="{{item}}">
|
|
||||||
<l-icon name="close" color="#ffffff" size="18" />
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</block>
|
|
||||||
</block>
|
|
||||||
<view class="file-list__item file-list__item--add file-list__item--{{size}} oak-item-class" style="{{itemSizePercentage?'width:'+itemSizePercentage+'padding-bottom:'+itemSizePercentage:''}}" wx:if="{{!disableInsert && !disableAdd}}" bind:tap="onPickByMp">
|
|
||||||
<view class="file-list__item-slot-wrapper">
|
|
||||||
<slot />
|
|
||||||
</view>
|
|
||||||
<view class="file-list__image--add">
|
|
||||||
<l-icon name="add" size="80" />
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</block>
|
|
||||||
<block wx:else >
|
|
||||||
<view class="file-list-flow__container oak-class">
|
|
||||||
<view class="file-list-flow__item--add oak-item-add-class" wx:if="{{!disableInsert && !disableAdd}}">
|
|
||||||
<l-button bind:lintap="onPickByMp" plain="{{true}}" type="default">选择文件</l-button>
|
|
||||||
</view>
|
|
||||||
<block wx:for="{{files}}" wx:key="index">
|
<block wx:for="{{files}}" wx:key="index">
|
||||||
<block wx:if="{{item}}">
|
<block wx:if="{{item}}">
|
||||||
<view class="file-list-flow__item">
|
<view class="file-list__item file-list__item--{{size}} oak-item-class" style="{{itemSizePercentage?'width:'+itemSizePercentage+'padding-bottom:'+itemSizePercentage:'xxx'}}">
|
||||||
<view class="file-list-flow__item--name" mut-bind:tap="onOpenByMp" data-value="{{item}}">
|
<image data-index="{{index}}" bind:tap="onPreviewByMp" src="{{item.thumbUrl}}" mode="{{mode}}" class="file-list__image"/>
|
||||||
{{ item.filename }}{{ item.extension ? '.' + item.extension : '' }}
|
</view>
|
||||||
</view>
|
|
||||||
<view class="file-list-flow__item--btns">
|
|
||||||
<!-- <view wx:if="{{!disableDownload}}" mut-bind:tap="onDownloadByMp" class="file-list-flow__download" data-value="{{item}}">
|
|
||||||
<l-icon name="download" size="36" />
|
|
||||||
</view> -->
|
|
||||||
<view wx:if="{{!disableDelete}}" mut-bind:tap="onDeleteByMp" class="file-list-flow__remove" data-value="{{item}}">
|
|
||||||
<l-icon name="delete" size="36" />
|
|
||||||
</view>
|
|
||||||
<view>
|
|
||||||
</view>
|
|
||||||
</block>
|
</block>
|
||||||
</block>
|
</block>
|
||||||
</view>
|
</view>
|
||||||
</block>
|
|
||||||
|
|
@ -1,6 +1,2 @@
|
||||||
|
|
||||||
{
|
{}
|
||||||
"waiting": "等待上传",
|
|
||||||
"success": "上传成功",
|
|
||||||
"uploading": "上传中"
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +1,3 @@
|
||||||
.oak-upload {
|
.container {
|
||||||
&__tips {
|
display: flex;
|
||||||
display: block;
|
|
||||||
color: var(--oak-text-color-placeholder);
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,463 +1,73 @@
|
||||||
import React, { useState, useEffect, useCallback } from "react";
|
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
||||||
|
|
||||||
import { Space, Upload, UploadFile, Tag, Button, Table } from "antd";
|
import { Image, ImageViewer, Space } from 'antd-mobile';
|
||||||
import { Html5Filled, PlusOutlined } from "@ant-design/icons";
|
import classNames from 'classnames';
|
||||||
import { file2Obj } from "antd/es/upload/utils";
|
import Style from './mobile.module.less';
|
||||||
import { RcFile } from "antd/es/upload/interface";
|
import { WebComponentProps } from 'oak-frontend-base';
|
||||||
import classNames from "classnames";
|
import { EntityDict } from '../../../oak-app-domain';
|
||||||
import Style from "./web.module.less";
|
|
||||||
import { WebComponentProps } from "oak-frontend-base";
|
|
||||||
import { EntityDict } from "../../../oak-app-domain";
|
|
||||||
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";
|
|
||||||
|
|
||||||
interface NewUploadFile extends UploadFile {
|
type ExtraFile = EntityDict['extraFile']['OpSchema'];
|
||||||
id?: string;
|
|
||||||
|
interface EnhancedExtraFile extends ExtraFile {
|
||||||
|
url: string;
|
||||||
|
thumbUrl: string;
|
||||||
|
fileFullName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Theme = "file" | "image" | "image-flow" | "custom";
|
|
||||||
type ListType = "text" | "picture" | "picture-card";
|
|
||||||
|
|
||||||
function getListType(theme: Theme): ListType {
|
|
||||||
const themeMap: Record<Theme, ListType> = {
|
|
||||||
file: "text",
|
|
||||||
image: "picture-card",
|
|
||||||
"image-flow": "picture",
|
|
||||||
custom: "text",
|
|
||||||
};
|
|
||||||
return themeMap[theme];
|
|
||||||
}
|
|
||||||
|
|
||||||
const type = 'DraggableUploadList';
|
|
||||||
const DraggableUploadListItem = ({
|
|
||||||
originNode,
|
|
||||||
moveRow,
|
|
||||||
file,
|
|
||||||
fileList,
|
|
||||||
}: any) => {
|
|
||||||
const ref = React.useRef(null);
|
|
||||||
const index = fileList.indexOf(file);
|
|
||||||
const [{ isOver, dropClassName }, drop] = useDrop({
|
|
||||||
accept: type,
|
|
||||||
collect: (monitor: any) => {
|
|
||||||
const { index: dragIndex } = monitor.getItem() || {};
|
|
||||||
if (dragIndex === index) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
isOver: monitor.isOver(),
|
|
||||||
dropClassName:
|
|
||||||
dragIndex < index
|
|
||||||
? ' drop-over-downward'
|
|
||||||
: ' drop-over-upward',
|
|
||||||
};
|
|
||||||
},
|
|
||||||
drop: (item: any) => {
|
|
||||||
moveRow(item.index, index);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const [, drag] = useDrag({
|
|
||||||
type,
|
|
||||||
item: { index },
|
|
||||||
collect: (monitor) => ({
|
|
||||||
isDragging: monitor.isDragging(),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
drop(drag(ref));
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
ref={ref}
|
|
||||||
className={`ant-upload-draggable-list-item ${
|
|
||||||
isOver ? dropClassName : ''
|
|
||||||
}`}
|
|
||||||
style={{ cursor: 'move', height: '100%' }}
|
|
||||||
>
|
|
||||||
{originNode}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function render(
|
export default function render(
|
||||||
props: WebComponentProps<
|
props: WebComponentProps<
|
||||||
EntityDict,
|
EntityDict,
|
||||||
'extraFile',
|
'extraFile',
|
||||||
true,
|
true,
|
||||||
{
|
{
|
||||||
accept?: string;
|
files: EnhancedExtraFile[];
|
||||||
maxNumber?: number;
|
|
||||||
multiple?: boolean;
|
|
||||||
draggable?: boolean;
|
|
||||||
theme?: Theme;
|
|
||||||
tips?: string;
|
|
||||||
beforeUpload?: (file: File) => Promise<boolean> | boolean;
|
|
||||||
disabled?: boolean;
|
|
||||||
style?: Record<string, string>;
|
style?: Record<string, string>;
|
||||||
className?: string;
|
className?: string;
|
||||||
directory?: boolean;
|
onDownload?: (file: EnhancedExtraFile) => void;
|
||||||
onPreview?: (file: UploadFile<any>) => void;
|
|
||||||
onDownload?: (file: UploadFile<any>) => void;
|
|
||||||
showUploadList?: boolean;
|
showUploadList?: boolean;
|
||||||
children?: JSX.Element;
|
|
||||||
files?: EntityDict['extraFile']['OpSchema'][];
|
|
||||||
disableInsert?: boolean;
|
|
||||||
disableAdd?: boolean;
|
|
||||||
disableDownload?: boolean;
|
disableDownload?: boolean;
|
||||||
disableDelete?: boolean;
|
|
||||||
disablePreview?: boolean;
|
disablePreview?: boolean;
|
||||||
},
|
},
|
||||||
{
|
{}
|
||||||
onPickByWeb: (
|
|
||||||
files: UploadFile[],
|
|
||||||
callback?: (file: NewUploadFile, status: string) => void
|
|
||||||
) => void;
|
|
||||||
onDeleteByWeb: (file: UploadFile) => void;
|
|
||||||
getUrl: (extraFile: EntityDict['extraFile']['OpSchema']) => string;
|
|
||||||
getFileName: (
|
|
||||||
extraFile: EntityDict['extraFile']['OpSchema']
|
|
||||||
) => string;
|
|
||||||
formatBytes: (value: number) => string;
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
) {
|
) {
|
||||||
const {
|
const {
|
||||||
accept = 'image/*',
|
|
||||||
maxNumber = 20,
|
|
||||||
multiple = maxNumber !== 1,
|
|
||||||
draggable = false,
|
|
||||||
theme = 'image',
|
|
||||||
tips,
|
|
||||||
beforeUpload,
|
|
||||||
disabled,
|
|
||||||
style,
|
style,
|
||||||
className,
|
className,
|
||||||
directory = false,
|
|
||||||
onPreview,
|
|
||||||
onDownload,
|
onDownload,
|
||||||
children,
|
files = [],
|
||||||
showUploadList = true,
|
|
||||||
files,
|
|
||||||
disableInsert = false,
|
|
||||||
disableAdd = false,
|
|
||||||
disableDownload = false,
|
disableDownload = false,
|
||||||
disableDelete = false,
|
|
||||||
disablePreview = false,
|
disablePreview = false,
|
||||||
} = props.data;
|
} = props.data;
|
||||||
const {
|
const { t } = props.methods;
|
||||||
onPickByWeb,
|
const [visible, setVisible] = useState(false);
|
||||||
onDeleteByWeb,
|
const imageViewerMultiRef = useRef(null);
|
||||||
updateItem,
|
|
||||||
t,
|
|
||||||
getFileName,
|
|
||||||
getUrl,
|
|
||||||
formatBytes,
|
|
||||||
} = props.methods;
|
|
||||||
const [newFiles, setNewFiles] = useState<
|
|
||||||
EntityDict['extraFile']['OpSchema'][]
|
|
||||||
>([]);
|
|
||||||
const [newUploadFiles, setNewUploadFiles] = useState<NewUploadFile[]>([]);
|
|
||||||
|
|
||||||
const listType = getListType(theme);
|
|
||||||
useEffect(() => {
|
|
||||||
if (files && files.length > 0) {
|
|
||||||
setNewFiles(files);
|
|
||||||
} else {
|
|
||||||
setNewFiles([]);
|
|
||||||
}
|
|
||||||
}, [files]);
|
|
||||||
|
|
||||||
const extraFileToUploadFile = (
|
|
||||||
extraFile: EntityDict['extraFile']['OpSchema']
|
|
||||||
): NewUploadFile => {
|
|
||||||
let status = undefined as NewUploadFile['status'];
|
|
||||||
switch (extraFile.uploadState) {
|
|
||||||
case 'uploading': {
|
|
||||||
status = 'uploading';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'failed': {
|
|
||||||
status = 'error';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'success': {
|
|
||||||
status = 'done';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Object.assign({}, extraFile, {
|
|
||||||
id: extraFile.id,
|
|
||||||
url: getUrl(extraFile),
|
|
||||||
thumbUrl: getUrl(extraFile),
|
|
||||||
name: getFileName(extraFile)!,
|
|
||||||
fileName: getFileName(extraFile)!,
|
|
||||||
size: extraFile.size!,
|
|
||||||
type: extraFile.fileType!,
|
|
||||||
uid: extraFile.id, //upload 组件需要uid来维护fileList
|
|
||||||
status,
|
|
||||||
percent: status === 'uploading' ? 50 : undefined,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const setNewUploadFilesByStatus = (file: NewUploadFile, status: string) => {
|
|
||||||
const { fileName, size, id } = file;
|
|
||||||
const file2 = newUploadFiles.find(
|
|
||||||
(ele: NewUploadFile) =>
|
|
||||||
ele.name?.includes(fileName!) && ele.size === size
|
|
||||||
);
|
|
||||||
if (file2) {
|
|
||||||
Object.assign(file2, {
|
|
||||||
status,
|
|
||||||
id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setNewUploadFiles(newUploadFiles);
|
|
||||||
};
|
|
||||||
|
|
||||||
const customDelete = (index: number) => {
|
|
||||||
const arr = [...newUploadFiles];
|
|
||||||
arr.splice(index, 1);
|
|
||||||
setNewUploadFiles(arr);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getUploadButton = () => {
|
|
||||||
if (children) {
|
|
||||||
return children;
|
|
||||||
}
|
|
||||||
if (listType === 'picture-card') {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<PlusOutlined />
|
|
||||||
<div style={{ marginTop: 8 }}>请选择图片</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return <Button type="default">选择文件</Button>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const checkLimit = (num: number) => {
|
|
||||||
const pattern = /^\d+\.(?:9+)$/;
|
|
||||||
return pattern.test(num.toString());
|
|
||||||
};
|
|
||||||
|
|
||||||
const moveRow = useCallback(
|
|
||||||
(dragIndex: number, hoverIndex: number) => {
|
|
||||||
let dragRow = newFiles[dragIndex];
|
|
||||||
let sort;
|
|
||||||
if (hoverIndex === dragIndex) {
|
|
||||||
return;
|
|
||||||
} else if (hoverIndex > dragIndex) {
|
|
||||||
if (hoverIndex === newFiles.length - 1) {
|
|
||||||
sort = newFiles[hoverIndex]!.sort! + 100;
|
|
||||||
} else {
|
|
||||||
sort =
|
|
||||||
(newFiles[hoverIndex]!.sort! +
|
|
||||||
newFiles[hoverIndex + 1]!.sort!) /
|
|
||||||
2;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (hoverIndex === 0) {
|
|
||||||
sort = newFiles[hoverIndex]!.sort! / 2;
|
|
||||||
} else {
|
|
||||||
sort =
|
|
||||||
(newFiles[hoverIndex]!.sort! +
|
|
||||||
newFiles[hoverIndex - 1]!.sort!) /
|
|
||||||
2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (checkLimit(sort)) {
|
|
||||||
alert('当前的sort值为:' + sort);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
updateItem({ sort }, dragRow.id);
|
|
||||||
},
|
|
||||||
[newFiles]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Space
|
<>
|
||||||
direction="vertical"
|
<Space>
|
||||||
className={Style['oak-upload']}
|
{files?.map((ele, index) => (
|
||||||
style={{ width: '100%' }}
|
<Image
|
||||||
>
|
src={ele.thumbUrl}
|
||||||
<DndProvider backend={isPc ? HTML5Backend : TouchBackend}>
|
width={100}
|
||||||
<Upload
|
height={100}
|
||||||
className={classNames(
|
fit="contain"
|
||||||
Style['oak-upload__upload'],
|
onClick={!disablePreview ? () => {
|
||||||
className
|
setVisible(true);
|
||||||
)}
|
(imageViewerMultiRef.current as any).swipeTo(index);
|
||||||
style={style}
|
} : undefined}
|
||||||
disabled={disabled}
|
|
||||||
directory={directory}
|
|
||||||
showUploadList={
|
|
||||||
showUploadList
|
|
||||||
? {
|
|
||||||
showPreviewIcon: !disablePreview,
|
|
||||||
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}
|
|
||||||
maxCount={maxNumber}
|
|
||||||
accept={accept}
|
|
||||||
listType={listType}
|
|
||||||
fileList={
|
|
||||||
theme === 'custom'
|
|
||||||
? []
|
|
||||||
: newFiles?.map((ele) => extraFileToUploadFile(ele))
|
|
||||||
}
|
|
||||||
onChange={({ file, fileList, event }) => {
|
|
||||||
// id不存在就是file对象
|
|
||||||
if (!(file as NewUploadFile).id) {
|
|
||||||
if (theme !== 'custom') {
|
|
||||||
onPickByWeb([file2Obj(file as RcFile)]);
|
|
||||||
} else {
|
|
||||||
setNewUploadFiles(fileList);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onRemove={onDeleteByWeb}
|
|
||||||
onPreview={onPreview}
|
|
||||||
onDownload={onDownload}
|
|
||||||
itemRender={(originNode, currentFile, currentFileList) => {
|
|
||||||
return (
|
|
||||||
<DraggableUploadListItem
|
|
||||||
originNode={originNode}
|
|
||||||
file={currentFile}
|
|
||||||
fileList={currentFileList}
|
|
||||||
moveRow={moveRow}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{!disableInsert && !disableAdd ? getUploadButton() : null}
|
|
||||||
</Upload>
|
|
||||||
</DndProvider>
|
|
||||||
|
|
||||||
{tips && (
|
|
||||||
<small className={Style['oak-upload__tips']}>{tips}</small>
|
|
||||||
)}
|
|
||||||
{theme === 'custom' && (
|
|
||||||
<>
|
|
||||||
<Table
|
|
||||||
dataSource={newUploadFiles || []}
|
|
||||||
rowKey="id"
|
|
||||||
columns={[
|
|
||||||
{
|
|
||||||
align: 'center',
|
|
||||||
dataIndex: 'tableIndex',
|
|
||||||
title: '#',
|
|
||||||
render: (value, record, index) => index + 1,
|
|
||||||
width: 50,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
dataIndex: 'name',
|
|
||||||
title: '文件名',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
dataIndex: 'size',
|
|
||||||
title: '文件大小',
|
|
||||||
render: (value, record, index) => {
|
|
||||||
return formatBytes(value as number);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
dataIndex: 'status',
|
|
||||||
title: '状态',
|
|
||||||
render: (value, record, index) => {
|
|
||||||
switch (value) {
|
|
||||||
case 'success':
|
|
||||||
return (
|
|
||||||
<Tag color="success">
|
|
||||||
{t('success')}
|
|
||||||
</Tag>
|
|
||||||
);
|
|
||||||
case 'uploading':
|
|
||||||
return (
|
|
||||||
<Tag color="processing">
|
|
||||||
{t('uploading')}
|
|
||||||
</Tag>
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
return (
|
|
||||||
<Tag color="warning">
|
|
||||||
{t('waiting')}
|
|
||||||
</Tag>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
dataIndex: 'op',
|
|
||||||
width: 300,
|
|
||||||
title: '操作',
|
|
||||||
align: 'center',
|
|
||||||
render: (value, record, index) => {
|
|
||||||
// 只处理state的文件 这时候可以直接删除
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{!record.id && (
|
|
||||||
<Button
|
|
||||||
type="link"
|
|
||||||
onClick={() => {
|
|
||||||
customDelete(index);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
删除
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
fixed: 'right',
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
/>
|
||||||
<div
|
))}
|
||||||
style={{ display: 'flex', justifyContent: 'flex-end' }}
|
</Space>
|
||||||
>
|
|
||||||
<Space>
|
<ImageViewer.Multi
|
||||||
<Button
|
ref={imageViewerMultiRef}
|
||||||
danger
|
images={files?.map((ele) => ele.url) || []}
|
||||||
type="default"
|
visible={visible}
|
||||||
onClick={() => setNewUploadFiles([])}
|
onClose={() => {
|
||||||
>
|
setVisible(false);
|
||||||
清空
|
}}
|
||||||
</Button>
|
/>
|
||||||
<Button
|
</>
|
||||||
type="primary"
|
|
||||||
onClick={() => {
|
|
||||||
onPickByWeb(
|
|
||||||
newUploadFiles,
|
|
||||||
(
|
|
||||||
file: NewUploadFile,
|
|
||||||
status: string
|
|
||||||
) => {
|
|
||||||
setNewUploadFilesByStatus(
|
|
||||||
file,
|
|
||||||
status
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
上传
|
|
||||||
</Button>
|
|
||||||
</Space>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Space>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
{
|
|
||||||
"component": true,
|
|
||||||
"usingComponents": {}
|
|
||||||
}
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
@import "../../../config/styles/mp/index.less";
|
|
||||||
@import "../../../config/styles/mp/mixins.less";
|
|
||||||
|
|
||||||
.file-list__container {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-list__item {
|
|
||||||
position: relative;
|
|
||||||
width: 220rpx;
|
|
||||||
padding-bottom: 220rpx;
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// size 不同时,对应的图片间距设置
|
|
||||||
// size 仅支持 1-10
|
|
||||||
each(range(2, 10), {
|
|
||||||
@valuePlusOne : @value+1;
|
|
||||||
|
|
||||||
.file-list__item--@{value}:nth-of-type(n+@{valuePlusOne}) {
|
|
||||||
margin-top : 20rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-list__item--@{value}:not(:nth-of-type(@{value}n+1)) {
|
|
||||||
margin-left : 20rpx;
|
|
||||||
}
|
|
||||||
}) // 当 size 为 null,每行会显示 3 张图片
|
|
||||||
|
|
||||||
.file-list__item--null:nth-of-type(n+4) {
|
|
||||||
margin-top: 20rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-list__item--null:not(:nth-of-type(3n+1)) {
|
|
||||||
margin-left: 20rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-list__image {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
border: 1rpx solid #eee;
|
|
||||||
border-radius: 4rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-list__item--selected {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: 10;
|
|
||||||
background-color: #000;
|
|
||||||
filter: Alpha(Opacity=50);
|
|
||||||
opacity: 0.5;
|
|
||||||
position: absolute;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-around;
|
|
||||||
}
|
|
||||||
|
|
@ -1,186 +0,0 @@
|
||||||
import { EntityDict } from '../../../oak-app-domain';
|
|
||||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/types/Entity';
|
|
||||||
import { ReactComponentProps } from 'oak-frontend-base/lib/types/Page';
|
|
||||||
|
|
||||||
type ExtraFile = EntityDict['extraFile']['OpSchema'];
|
|
||||||
|
|
||||||
interface EnhancedExtraFile extends ExtraFile {
|
|
||||||
url: string;
|
|
||||||
thumbUrl: string;
|
|
||||||
fileName: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type ImgMode = 'scaleToFill' | 'aspectFit' | 'aspectFill' | 'widthFix' | "heightFix" | 'top' | 'bottom' | 'left'
|
|
||||||
| 'right' | 'center' | 'top left' | 'top right' | 'bottom left' | 'bottom right';
|
|
||||||
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
features: ['extraFile'],
|
|
||||||
formData({ data, features }) {
|
|
||||||
let files = data?.sort((ele1, ele2) => ele1.sort! - ele2.sort!);
|
|
||||||
if (this.props.tag1) {
|
|
||||||
files = files?.filter((ele) => ele?.tag1 === this.props.tag1);
|
|
||||||
}
|
|
||||||
if (this.props.tag2) {
|
|
||||||
files = files?.filter((ele) => ele?.tag2 === this.props.tag2);
|
|
||||||
}
|
|
||||||
|
|
||||||
const files2 = files.map((ele) => {
|
|
||||||
const url = features.extraFile.getUrl(ele as ExtraFile);
|
|
||||||
const thumbUrl = features.extraFile.getUrl(
|
|
||||||
ele as ExtraFile,
|
|
||||||
this.props.style
|
|
||||||
);
|
|
||||||
const fileName = features.extraFile.getFileName(ele as ExtraFile);
|
|
||||||
return {
|
|
||||||
url,
|
|
||||||
thumbUrl,
|
|
||||||
fileName,
|
|
||||||
...ele,
|
|
||||||
} as EnhancedExtraFile;
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
files: files2,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
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: {
|
|
||||||
mode: 'aspectFit' as ImgMode, // 图片显示模式
|
|
||||||
size: 3, // 每行可显示的个数 小程序独有
|
|
||||||
disablePreview: false, // 图片是否可预览
|
|
||||||
disableDownload: false, // 下载按钮隐藏
|
|
||||||
tag1: '',
|
|
||||||
tag2: '',
|
|
||||||
entity: '' as keyof EntityDict,
|
|
||||||
entityId: '',
|
|
||||||
style: '',
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
/**
|
|
||||||
* 获取组件内部节点位置信息(单个)
|
|
||||||
* @param component 组件实例
|
|
||||||
* @param selector {String} css选择器
|
|
||||||
* @returns boundingClientRect() 回调函数的值
|
|
||||||
*/
|
|
||||||
async getNodeRectFromComponent(component: any, selector: any) {
|
|
||||||
return await new Promise((resolve) => {
|
|
||||||
component
|
|
||||||
.createSelectorQuery()
|
|
||||||
.select(selector)
|
|
||||||
.boundingClientRect((res: any) => {
|
|
||||||
resolve(res);
|
|
||||||
})
|
|
||||||
.exec();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
// * px 转 rpx
|
|
||||||
// * @param px 像素值
|
|
||||||
// */
|
|
||||||
px2rpx(px: number) {
|
|
||||||
const windowWidth = wx.getSystemInfoSync().windowWidth;
|
|
||||||
return (750 / windowWidth) * px;
|
|
||||||
},
|
|
||||||
|
|
||||||
async onPreviewByMp(event: WechatMiniprogram.Touch) {
|
|
||||||
const files = this.state.files as EnhancedExtraFile[];
|
|
||||||
const { index } = event.currentTarget.dataset;
|
|
||||||
const imageUrl = files[index].url;
|
|
||||||
const urls = files?.filter((ele) => !!ele).map((ele) => ele.url);
|
|
||||||
// 预览图片
|
|
||||||
if (!this.props.disablePreview) {
|
|
||||||
const result = await wx.previewImage({
|
|
||||||
urls: urls,
|
|
||||||
current: imageUrl,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
listeners: {
|
|
||||||
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: any = 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,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}) as <ED2 extends EntityDict & BaseEntityDict, T2 extends keyof ED2>(
|
|
||||||
props: ReactComponentProps<
|
|
||||||
ED2,
|
|
||||||
T2,
|
|
||||||
true,
|
|
||||||
{
|
|
||||||
// 图片显示模式
|
|
||||||
mode: ImgMode;
|
|
||||||
// 每行可显示的个数
|
|
||||||
size: number;
|
|
||||||
// 图片是否可预览
|
|
||||||
disablePreview: boolean;
|
|
||||||
// 下按按钮隐藏
|
|
||||||
disableDownload: boolean;
|
|
||||||
tag1: string;
|
|
||||||
tag2: string;
|
|
||||||
entity: keyof ED2;
|
|
||||||
entityId: string;
|
|
||||||
style: string;
|
|
||||||
}
|
|
||||||
>
|
|
||||||
) => React.ReactElement;
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
<view class="file-list__container oak-class">
|
|
||||||
<block wx:for="{{files}}" wx:key="index">
|
|
||||||
<block wx:if="{{item}}">
|
|
||||||
<view class="file-list__item file-list__item--{{size}} oak-item-class" style="{{itemSizePercentage?'width:'+itemSizePercentage+'padding-bottom:'+itemSizePercentage:'xxx'}}">
|
|
||||||
<image data-index="{{index}}" bind:tap="onPreviewByMp" src="{{item.thumbUrl}}" mode="{{mode}}" class="file-list__image"/>
|
|
||||||
</view>
|
|
||||||
</block>
|
|
||||||
</block>
|
|
||||||
</view>
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
|
|
||||||
{}
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
.container {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
@ -1,73 +0,0 @@
|
||||||
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
|
||||||
|
|
||||||
import { Image, ImageViewer, Space } from 'antd-mobile';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import Style from './mobile.module.less';
|
|
||||||
import { WebComponentProps } from 'oak-frontend-base';
|
|
||||||
import { EntityDict } from '../../../oak-app-domain';
|
|
||||||
|
|
||||||
type ExtraFile = EntityDict['extraFile']['OpSchema'];
|
|
||||||
|
|
||||||
interface EnhancedExtraFile extends ExtraFile {
|
|
||||||
url: string;
|
|
||||||
thumbUrl: string;
|
|
||||||
fileFullName: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function render(
|
|
||||||
props: WebComponentProps<
|
|
||||||
EntityDict,
|
|
||||||
'extraFile',
|
|
||||||
true,
|
|
||||||
{
|
|
||||||
files: EnhancedExtraFile[];
|
|
||||||
style?: Record<string, string>;
|
|
||||||
className?: string;
|
|
||||||
onDownload?: (file: EnhancedExtraFile) => void;
|
|
||||||
showUploadList?: boolean;
|
|
||||||
disableDownload?: boolean;
|
|
||||||
disablePreview?: boolean;
|
|
||||||
},
|
|
||||||
{}
|
|
||||||
>
|
|
||||||
) {
|
|
||||||
const {
|
|
||||||
style,
|
|
||||||
className,
|
|
||||||
onDownload,
|
|
||||||
files = [],
|
|
||||||
disableDownload = false,
|
|
||||||
disablePreview = false,
|
|
||||||
} = props.data;
|
|
||||||
const { t } = props.methods;
|
|
||||||
const [visible, setVisible] = useState(false);
|
|
||||||
const imageViewerMultiRef = useRef(null);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Space>
|
|
||||||
{files?.map((ele, index) => (
|
|
||||||
<Image
|
|
||||||
src={ele.thumbUrl}
|
|
||||||
width={100}
|
|
||||||
height={100}
|
|
||||||
fit="contain"
|
|
||||||
onClick={!disablePreview ? () => {
|
|
||||||
setVisible(true);
|
|
||||||
(imageViewerMultiRef.current as any).swipeTo(index);
|
|
||||||
} : undefined}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</Space>
|
|
||||||
|
|
||||||
<ImageViewer.Multi
|
|
||||||
ref={imageViewerMultiRef}
|
|
||||||
images={files?.map((ele) => ele.url) || []}
|
|
||||||
visible={visible}
|
|
||||||
onClose={() => {
|
|
||||||
setVisible(false);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { EntityDict } from '../../../oak-app-domain';
|
||||||
import { FileState } from '../../../features/extraFile';
|
import { FileState } from '../../../features/extraFile';
|
||||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/types/Entity';
|
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/types/Entity';
|
||||||
import { ReactComponentProps } from 'oak-frontend-base/lib/types/Page';
|
import { ReactComponentProps } from 'oak-frontend-base/lib/types/Page';
|
||||||
import { generateNewId } from 'oak-domain';
|
import { generateNewId } from 'oak-domain/lib/utils/uuid';
|
||||||
import { assert } from 'oak-domain/lib/utils/assert';
|
import { assert } from 'oak-domain/lib/utils/assert';
|
||||||
|
|
||||||
type ExtraFile = EntityDict['extraFile']['OpSchema'];
|
type ExtraFile = EntityDict['extraFile']['OpSchema'];
|
||||||
|
|
|
||||||
|
|
@ -72,18 +72,6 @@ const i18ns: I18n[] = [
|
||||||
language: "zh-CN",
|
language: "zh-CN",
|
||||||
module: "oak-general-business",
|
module: "oak-general-business",
|
||||||
position: "src/components/extraFile/gallery",
|
position: "src/components/extraFile/gallery",
|
||||||
data: {
|
|
||||||
"waiting": "等待上传",
|
|
||||||
"success": "上传成功",
|
|
||||||
"uploading": "上传中"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "d30e4a47c1d768d74efae1fade371595",
|
|
||||||
namespace: "oak-general-business-c-extraFile-gallery2",
|
|
||||||
language: "zh-CN",
|
|
||||||
module: "oak-general-business",
|
|
||||||
position: "src/components/extraFile/gallery2",
|
|
||||||
data: {}
|
data: {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@
|
||||||
"**/*.spec.ts",
|
"**/*.spec.ts",
|
||||||
"package.json",
|
"package.json",
|
||||||
"tsconfig.json",
|
"tsconfig.json",
|
||||||
"tsconfig.es.json",
|
"tsconfig.lib.json",
|
||||||
"package-lock.json",
|
"package-lock.json",
|
||||||
"test",
|
"test",
|
||||||
"scripts"
|
"scripts"
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,7 @@
|
||||||
"package.json",
|
"package.json",
|
||||||
"tsconfig.json",
|
"tsconfig.json",
|
||||||
"tsconfig.es.json",
|
"tsconfig.es.json",
|
||||||
|
"tsconfig.lib.json",
|
||||||
"package-lock.json",
|
"package-lock.json",
|
||||||
"test",
|
"test",
|
||||||
"scripts"
|
"scripts"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue