feat: 评论接口对接

This commit is contained in:
landaiqing
2024-07-19 18:58:47 +08:00
parent 359ef60128
commit 6e8b2ae643
38 changed files with 5184 additions and 2096 deletions

View File

@@ -17,7 +17,7 @@ VITE_TITLE_NAME='五味子云存储'
VITE_APP_TOKEN_KEY='schisandra' VITE_APP_TOKEN_KEY='schisandra'
# the upload url # the upload url
VITE_UPLOAD_URL='http://127.0.0.1:3000' VITE_UPLOAD_URL='http://127.0.0.1:5050'
# the websocket url # the websocket url
VITE_WEB_SOCKET_URL='ws://127.0.0.1:3010/wx/socket' VITE_WEB_SOCKET_URL='ws://127.0.0.1:3010/wx/socket'

View File

@@ -1,4 +1,3 @@
node_modules node_modules
.eslintrc.cjs .eslintrc.cjs
dist dist
/src/components/Main/Home/index.tsx

View File

@@ -23,6 +23,7 @@
"antd": "^5.19.1", "antd": "^5.19.1",
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.19",
"axios": "^1.7.2", "axios": "^1.7.2",
"base-64": "^1.0.0",
"core-js": "^3.37.1", "core-js": "^3.37.1",
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",
"echarts-for-react": "^3.0.2", "echarts-for-react": "^3.0.2",

7
pnpm-lock.yaml generated
View File

@@ -44,6 +44,9 @@ dependencies:
axios: axios:
specifier: ^1.7.2 specifier: ^1.7.2
version: 1.7.2 version: 1.7.2
base-64:
specifier: ^1.0.0
version: 1.0.0
core-js: core-js:
specifier: ^3.37.1 specifier: ^3.37.1
version: 3.37.1 version: 3.37.1
@@ -4647,6 +4650,10 @@ packages:
resolution: {integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==} resolution: {integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==}
dev: true dev: true
/base-64@1.0.0:
resolution: {integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==}
dev: false
/base64-js@1.5.1: /base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
dev: true dev: true

View File

@@ -5,13 +5,15 @@ import web from "@/utils/axios/web.ts";
/** /**
* 初始化ali oss * 初始化ali oss
* @param data 用户id * @param data 用户id
* @param Id id
*/ */
export const initAliOSS = (data: any) => { export const initAliOSS = (data: any, Id: any) => {
return web.request({ return web.request({
url: "/oss/oss/ali/init", url: "/oss/oss/ali/init",
method: "post", method: "get",
params: { params: {
userId: data, userId: data,
Id: Id,
}, },
}); });
}; };
@@ -59,3 +61,74 @@ export const getAllAliOSSConfig = (userId: any) => {
}, },
}); });
}; };
/**
* 新增阿里云oss配置
* @param data
*/
export const addAliOSSConfig = (data: API.AliOSSConfigRequest) => {
return web.request({
url: `/oss/oss/ali/add`,
method: "post",
headers: {
"Content-Type": "application/json;charset=UTF-8",
},
data: data,
});
};
/**
* 注销阿里云oss配置
* @param userId
* @param Id
*/
export const setAliShutdown = (userId: any, Id: any) => {
return web.request({
url: `/oss/oss/ali/shutdown`,
method: "post",
params: {
userId: userId,
Id: Id,
},
});
};
/**
* 删除阿里云oss配置
* @param data
*/
export const deleteAliConfig = (data: any) => {
return web.request({
url: `/oss/oss/ali/delete`,
method: "post",
headers: {
"Content-Type": "application/json;charset=UTF-8",
},
data: data,
});
};
/**
* 修改阿里云oss配置
* @param data
*/
export const updateAliConfig = (data: any) => {
return web.request({
url: `/oss/oss/ali/update`,
method: "post",
headers: {
"Content-Type": "application/json;charset=UTF-8",
},
data: data,
});
};
/**
* 获取阿里云oss配置详情
* @param id
*/
export const getAliConfigDetailById = (id: any) => {
return web.request({
url: `/oss/oss/ali/one`,
method: "get",
params: {
id: id,
},
});
};

View File

@@ -167,3 +167,191 @@ export const getUserRecentPreviewFile = (userId: any) => {
}, },
}); });
}; };
/**
* 获取存储桶
* @param userId
* @param bucket
* @param type
*/
export const deleteBucket = (userId: any, bucket: any, type: any) => {
return web.request({
url: `/oss/oss/` + type + `/deleteBucket`,
method: "post",
params: {
userId: userId,
bucket: bucket,
},
});
};
/**
* 获取存储桶大小
* @param userId
* @param bucket
* @param type
*/
export const getBucketSize = (userId: any, bucket: any, type: any) => {
return web.request({
url: `/oss/oss/` + type + `/getBucketSize`,
method: "post",
params: {
userId: userId,
bucket: bucket,
},
});
};
/**
* 创建存储桶
* @param userId
* @param bucket
* @param type
*/
export const creatBucket = (userId: any, bucket: any, type: any) => {
return web.request({
url: `/oss/oss/` + type + `/createBucket`,
method: "post",
params: {
userId: userId,
bucket: bucket,
},
});
};
/**
* 删除文件
* @param userId
* @param bucket
* @param type
* @param filePath
*/
export const deleteFile = (userId: any, bucket: any, type: any, filePath: any) => {
return web.request({
url: `/oss/oss/` + type + `/deleteFile`,
method: "post",
params: {
userId: userId,
bucket: bucket,
filePath: filePath,
},
});
};
/**
* 下载文件
* @param userId
* @param bucket
* @param type
* @param listObjectsArgs
*/
export const downloadFiles = (userId: any, bucket: any, type: any, listObjectsArgs: any) => {
return web.request({
url: `/oss/oss/` + type + `/downloadFile`,
method: "get",
responseType: "blob",
params: {
userId: userId,
bucket: bucket,
listObjectsArgs: listObjectsArgs,
},
});
};
/**
* 预览文件
* @param userId
* @param bucket
* @param type
* @param filePath
*/
export const previewFile = (userId: any, bucket: any, type: any, filePath: any) => {
return web.request({
url: `/oss/oss/` + type + `/previewFile`,
method: "post",
params: {
userId: userId,
bucket: bucket,
filePath: filePath,
},
});
};
/**
* 重命名文件
* @param userId
* @param bucket
* @param type
* @param oldFileName
* @param newFileName
*/
export const renameFile = (
userId: any,
bucket: any,
type: any,
oldFileName: any,
newFileName: any,
) => {
return web.request({
url: `/oss/oss/` + type + `/renameFile`,
method: "post",
params: {
userId: userId,
bucket: bucket,
oldFileName: oldFileName,
newFileName: newFileName,
},
});
};
/**
* 拷贝文件
* @param userId
* @param bucket
* @param type
* @param oldFilePath
* @param newFilePath
*/
export const copyFile = (
userId: any,
bucket: any,
type: any,
oldFilePath: any,
newFilePath: any,
) => {
return web.request({
url: `/oss/oss/` + type + `/copyFile`,
method: "post",
params: {
userId: userId,
bucket: bucket,
oldFilePath: oldFilePath,
newFilePath: newFilePath,
},
});
};
/**
* 创建存储桶
* @param userId
* @param bucket
* @param type
*/
export const createBucket = (userId: any, bucket: any, type: any) => {
return web.request({
url: `/oss/oss/` + type + `/createBucket`,
method: "post",
params: {
userId: userId,
bucket: bucket,
},
});
};
/**
* 上传文件
* @param type
* @param data
* @param peram
*/
export const uploadFiles = (type: any, data: any, peram: any) => {
return web.request({
url: `/oss/oss/` + type + `/uploadFile`,
method: "post",
headers: {
"Content-Type": "multipart/form-data",
},
data: data,
params: peram,
});
};

View File

@@ -5,13 +5,15 @@ import web from "@/utils/axios/web.ts";
/** /**
* 初始化minio * 初始化minio
* @param data 用户id * @param data 用户id
* @param id
*/ */
export const initMinioOSS = (data: any) => { export const initMinioOSS = (data: any, id: any) => {
return web.request({ return web.request({
url: "/oss/oss/minio/init", url: "/oss/oss/minio/init",
method: "post", method: "post",
params: { params: {
userId: data, userId: data,
Id: id,
}, },
}); });
}; };
@@ -58,3 +60,75 @@ export const getAllMinioConfig = (userId: any) => {
}, },
}); });
}; };
/**
* 新增阿里云oss配置
* @param data
*/
export const addMinioOSSConfig = (data: API.MinioOSSConfigRequest) => {
return web.request({
url: `/oss/oss/minio/add`,
method: "post",
headers: {
"Content-Type": "application/json;charset=UTF-8",
},
data: data,
});
};
/**
* 注销MinIo oss配置
* @param userId
* @param Id
*/
export const setMinioShutdown = (userId: any, Id: any) => {
return web.request({
url: `/oss/oss/minio/shutdown`,
method: "post",
params: {
userId: userId,
Id: Id,
},
});
};
/**
* 删除Minio oss配置
* @param data
*/
export const deleteMinioConfig = (data: any) => {
return web.request({
url: `/oss/oss/minio/delete`,
method: "post",
headers: {
"Content-Type": "application/json;charset=UTF-8",
},
data: data,
});
};
/**
* 删除Minio oss配置
* @param data
*/
export const updateMinioConfig = (data: any) => {
return web.request({
url: `/oss/oss/minio/update`,
method: "post",
headers: {
"Content-Type": "application/json;charset=UTF-8",
},
data: data,
});
};
/**
* 获取Minio oss配置详情
* @param id
*/
export const getMinioConfigDetailById = (id: any) => {
return web.request({
url: `/oss/oss/minio/one`,
method: "get",
params: {
Id: id,
},
});
};

View File

@@ -5,13 +5,15 @@ import web from "@/utils/axios/web.ts";
/** /**
* 初始化qiniu * 初始化qiniu
* @param data 用户id * @param data 用户id
* @param id
*/ */
export const initQiniuOSS = (data: any) => { export const initQiniuOSS = (data: any, id: any) => {
return web.request({ return web.request({
url: "/oss/oss/qiniu/init", url: "/oss/oss/qiniu/init",
method: "post", method: "post",
params: { params: {
userId: data, userId: data,
Id: id,
}, },
}); });
}; };
@@ -55,3 +57,77 @@ export const getAllQiniuConfigs = (userId: any) => {
}, },
}); });
}; };
/**
* 新增七牛oss配置
* @param data
*/
export const addQiniuOSSConfig = (data: API.QiniuOSSConfigRequest) => {
return web.request({
url: `/oss/oss/qiniu/add`,
method: "post",
headers: {
"Content-Type": "application/json;charset=UTF-8",
},
data: data,
});
};
/**
* 注销 Qiniu oss配置
* @param userId
* @param Id
*/
export const setQiniuShutdown = (userId: any, Id: any) => {
return web.request({
url: `/oss/oss/qiniu/shutdown`,
method: "post",
params: {
userId: userId,
Id: Id,
},
});
};
/**
* 删除Qiniu oss配置
* @param data
*/
export const deleteQiniuConfig = (data: any) => {
return web.request({
url: `/oss/oss/qiniu/delete`,
method: "post",
headers: {
"Content-Type": "application/json;charset=UTF-8",
},
data: data,
});
};
/**
* 更新Qiniu oss配置
* @param data
*/
export const updateQiniuConfig = (data: any) => {
return web.request({
url: `/oss/oss/qiniu/update`,
method: "post",
headers: {
"Content-Type": "application/json;charset=UTF-8",
},
data: data,
});
};
/**
* 获取Qiniu oss配置
* @param id
*/
export const getQiniuConfigDetailById = (id: any) => {
return web.request({
url: `/oss/oss/qiniu/one`,
method: "get",
params: {
Id: id,
},
});
};

View File

@@ -5,13 +5,15 @@ import web from "@/utils/axios/web.ts";
/** /**
* 初始化Tencent oss * 初始化Tencent oss
* @param data 用户id * @param data 用户id
* @param id
*/ */
export const initTencentOSS = (data: any) => { export const initTencentOSS = (data: any, id: any) => {
return web.request({ return web.request({
url: "/oss/oss/ali/tencent", url: "/oss/oss/tencent/init",
method: "post", method: "post",
params: { params: {
userId: data, userId: data,
Id: id,
}, },
}); });
}; };
@@ -58,3 +60,76 @@ export const getAllTencentOSsConfig = (userId: any) => {
}, },
}); });
}; };
/**
* 新增腾讯云oss
* @param data
*/
export const addTencentOSSConfig = (data: API.TencentOSSConfigRequest) => {
return web.request({
url: `/oss/oss/tencent/add`,
method: "post",
headers: {
"Content-Type": "application/json;charset=UTF-8",
},
data: data,
});
};
/**
* 注销 Tencent oss配置
* @param userId
* @param Id
*/
export const setTencentShutdown = (userId: any, Id: any) => {
return web.request({
url: `/oss/oss/tencent/shutdown`,
method: "post",
params: {
userId: userId,
Id: Id,
},
});
};
/**
* 删除Tencent oss配置
* @param data
*/
export const deleteTencentConfig = (data: any) => {
return web.request({
url: `/oss/oss/tencent/delete`,
method: "post",
headers: {
"Content-Type": "application/json;charset=UTF-8",
},
data: data,
});
};
/**
* 更新Tencent oss配置
* @param data
*/
export const updateTencentConfig = (data: any) => {
return web.request({
url: `/oss/oss/tencent/update`,
method: "post",
headers: {
"Content-Type": "application/json;charset=UTF-8",
},
data: data,
});
};
/**
* 获取腾讯云oss配置详情
* @param id
*/
export const getTencentConfigDetailById = (id: any) => {
return web.request({
url: `/oss/oss/tencent/one`,
method: "get",
params: {
Id: id,
},
});
};

123
src/api/share/index.ts Normal file
View File

@@ -0,0 +1,123 @@
/** @format */
import web from "@/utils/axios/web.ts";
/**
* 获取分享圈列表
*/
export const getShareCircleList = () => {
return web.request({
url: `/share/share/circle/sharelist`,
method: "get",
});
};
/**
* 新增分享圈
* @param data
* @constructor
*/
export const addShareCircle = (data: any) => {
return web.request({
url: `/share/share/circle/add`,
method: "post",
data: data,
});
};
/**
* 分享圈详情列表
* @param circleId
*/
export const shareDetailList = (circleId: any) => {
return web.request({
url: `/share/share/detail/list`,
method: "post",
params: {
circleId: circleId,
},
});
};
/**
* 分享圈详情
* @param id
*/
export const getShareDetail = (id: any) => {
return web.request({
url: `/share/share/detail/get_detail`,
method: "post",
params: {
Id: id,
},
});
};
/**
* 新增分享圈详情
* @param data
*/
export const addShareDetail = (data: any) => {
return web.request({
url: `/share/share/detail/add_detail`,
method: "post",
data: data,
});
};
/**
* 分享圈详情评论列表
* @param detailId
*/
export const listComment = (detailId: any) => {
return web.request({
url: `/share/share/comment/reply/listcomment`,
method: "get",
params: {
detailId: detailId,
},
});
};
/**
* 分享圈详情回复列表
* @param commentId
*/
export const listReply = (commentId: any) => {
return web.request({
url: `/share/share/comment/reply/listreply`,
method: "get",
params: {
commentId: commentId,
},
});
};
/**
* 新增评论
* @param data
*/
export const addComment = (data: any) => {
return web.request({
url: `/share/share/comment/reply/addcomment`,
method: "post",
data: data,
});
};
/**
* 新增回复
* @param data
*/
export const addReply = (data: any) => {
return web.request({
url: `/share/share/comment/reply/addreply`,
method: "post",
data: data,
});
};
/**
* 返回评论总数
* @param detailId
*/
export const returnAllCommentAndReplyCount = (detailId: any) => {
return web.request({
url: `/share/share/comment/reply/returncount`,
method: "post",
params: {
detailId: detailId,
},
});
};

View File

@@ -1,29 +1,20 @@
/** @format */ /** @format */
import { Avatar, Button, message, Skeleton } from "antd"; import { Avatar, Button, Form, Input, message, Modal, Popconfirm, Popover, Skeleton } from "antd";
import { DrawerForm, ProCard, ProForm, ProFormText } from "@ant-design/pro-components"; import { ProCard } from "@ant-design/pro-components";
import React, { createContext, useContext, useEffect, useState } from "react"; import { useEffect, useState } from "react";
import styles from "./index.module.less"; import { ClusterOutlined, DeleteOutlined, ReloadOutlined } from "@ant-design/icons";
import bucket from "@/assets/icons/bucket.svg"; import bucket from "@/assets/icons/bucket.svg";
import styles from "./index.module.less";
import { creatBucket, deleteBucket, getBucketSize } from "@/api/oss";
import { getAllAliOSsBucket } from "@/api/oss/ali"; import { getAllAliOSsBucket } from "@/api/oss/ali";
import { EditOutlined, EllipsisOutlined, ReloadOutlined, SettingOutlined } from "@ant-design/icons";
const DrawerContext = createContext<{
drawerVisit: boolean;
setDrawerVisit: React.Dispatch<React.SetStateAction<boolean>>;
}>({} as any);
const AliDrawer = () => { const AliDrawer = () => {
const [drawerVisit, setDrawerVisit] = useState(false);
return (
<DrawerContext.Provider value={{ drawerVisit, setDrawerVisit }}>
<ProCardComponent />
</DrawerContext.Provider>
);
};
const ProCardComponent = () => {
const { drawerVisit, setDrawerVisit } = useContext(DrawerContext);
const [buckets, setBuckets] = useState<any>([]); const [buckets, setBuckets] = useState<any>([]);
const [loading, setLoading] = useState<boolean>(true); const [loading, setLoading] = useState<boolean>(true);
const [bucketSize, setBucketSize] = useState<string>("");
const [form] = Form.useForm();
const [open, setOpen] = useState<boolean>(false);
async function getAllBucket() { async function getAllBucket() {
getAllAliOSsBucket("2").then((res: any) => { getAllAliOSsBucket("2").then((res: any) => {
@@ -37,10 +28,11 @@ const ProCardComponent = () => {
useEffect(() => { useEffect(() => {
getAllBucket().then(); getAllBucket().then();
}, []); }, []);
const bucketList = () => { const bucketList = () => {
return ( return (
<ProCard <ProCard
title={"阿里云OSS存储桶列表"} title={"MinIO存储桶列表"}
headerBordered headerBordered
extra={ extra={
<> <>
@@ -53,7 +45,7 @@ const ProCardComponent = () => {
getAllBucket().then(); getAllBucket().then();
}} }}
icon={<ReloadOutlined />}></Button> icon={<ReloadOutlined />}></Button>
<Button type="primary" onClick={() => setDrawerVisit(true)}> <Button type="primary" onClick={() => setOpen(true)}>
</Button> </Button>
</> </>
@@ -63,33 +55,90 @@ const ProCardComponent = () => {
{buckets && {buckets &&
Array.from(buckets).map((item: any, index: number) => { Array.from(buckets).map((item: any, index: number) => {
return ( return (
<>
<div key={index}> <div key={index}>
<ProCard <ProCard
headStyle={{ backgroundColor: "#f0f2f5" }} headStyle={{ backgroundColor: "#f0f2f5" }}
hoverable hoverable
bordered bordered
title={<b>{item.name}</b>} // title={}
style={{ style={{
width: "180px", width: "180px",
height: 150,
borderWidth: "1px",
marginLeft: 10, marginLeft: 10,
}} }}
actions={[ actions={[
<SettingOutlined key="setting" />, // eslint-disable-next-line react/jsx-key
<EditOutlined key="edit" />, <Popover
<EllipsisOutlined key="ellipsis" />, content={bucketSize}
title={`size:`}
trigger="click">
<ClusterOutlined
key={"calculate"}
onClick={() => {
getBucketSize(
1,
item.name,
"minio",
).then((res: any) => {
if (
res &&
res.success &&
res.data
) {
setBucketSize(res.data);
} else {
message
.open({
content: "计算出错!",
type: "error",
})
.then();
}
});
}}
/>
</Popover>,
// eslint-disable-next-line react/jsx-key
<Popconfirm
title={`删除存储桶:${item.name}`}
description="你知道你要做什么吗?"
onConfirm={() => {
deleteBucket(1, item.name, "minio").then(
(res: any) => {
if (
res &&
res.data &&
res.data === "success"
) {
message
.open({
content: "删除成功!",
type: "success",
})
.then();
} else {
message
.open({
content: res.data,
type: "error",
})
.then();
}
},
);
}}
okText="Yes"
cancelText="No">
<DeleteOutlined key="delete" />
</Popconfirm>,
]}> ]}>
<Avatar <Avatar
shape={"square"} shape={"square"}
src={bucket as any} src={bucket as any}
size={"large"} size={"large"}
/> />
<b style={{ marginLeft: 10 }}>{item.size}</b> <b style={{ marginLeft: 5 }}>{item.name}</b>
</ProCard> </ProCard>
</div> </div>
</>
); );
})} })}
</Skeleton> </Skeleton>
@@ -101,32 +150,49 @@ const ProCardComponent = () => {
return ( return (
<> <>
{bucketList()} {bucketList()}
<DrawerForm <Modal
title="创建存储桶" title="创建存储桶"
open={drawerVisit} width={"30%"}
onOpenChange={setDrawerVisit} open={open}
onFinish={async (values) => { closable={true}
console.log(values.name); onOk={() => {
message.success("提交成功"); const bucketName = form.getFieldValue("name" as any);
return true; if (bucketName === "" || bucketName === null) {
}}> message.open({
<ProForm.Group> content: " 请输入存储桶名称!",
<ProFormText type: "warning",
width="md" });
} else {
creatBucket(1, bucketName, "ali").then((res: any) => {
if (res && res.success) {
message
.open({
content: "创建成功!",
type: "success",
})
.then();
setOpen(false);
} else {
message
.open({
content: res.data,
type: "error",
})
.then();
}
});
}
}}
onCancel={() => setOpen(false)}>
<Form layout="vertical" form={form}>
<Form.Item
name="name" name="name"
label="存储桶名" label="存储桶名"
tooltip="最长为 24 位" rules={[{ required: true, message: "请输入存储桶名称!" }]}>
placeholder="请输入名称" <Input placeholder="请输入存储桶名称" />
/> </Form.Item>
</Form>
<ProFormText </Modal>
width="md"
name="capacity"
label="我方公司名称"
placeholder="请输入名称"
/>
</ProForm.Group>
</DrawerForm>
</> </>
); );
}; };

View File

@@ -1,29 +1,20 @@
/** @format */ /** @format */
import { Avatar, Button, message, Skeleton } from "antd"; import { Avatar, Button, Form, Input, message, Modal, Popconfirm, Popover, Skeleton } from "antd";
import { DrawerForm, ProCard, ProForm, ProFormText } from "@ant-design/pro-components"; import { ProCard } from "@ant-design/pro-components";
import React, { createContext, useContext, useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { EditOutlined, EllipsisOutlined, ReloadOutlined, SettingOutlined } from "@ant-design/icons"; import { ClusterOutlined, DeleteOutlined, ReloadOutlined } from "@ant-design/icons";
import bucket from "@/assets/icons/bucket.svg"; import bucket from "@/assets/icons/bucket.svg";
import styles from "./index.module.less"; import styles from "./index.module.less";
import { getAllMinioBucket } from "@/api/oss/minio"; import { getAllMinioBucket } from "@/api/oss/minio";
import { creatBucket, deleteBucket, getBucketSize } from "@/api/oss";
const DrawerContext = createContext<{
drawerVisit: boolean;
setDrawerVisit: React.Dispatch<React.SetStateAction<boolean>>;
}>({} as any);
const MinioDrawer = () => { const MinioDrawer = () => {
const [drawerVisit, setDrawerVisit] = useState(false);
return (
<DrawerContext.Provider value={{ drawerVisit, setDrawerVisit }}>
<ProCardComponent />
</DrawerContext.Provider>
);
};
const ProCardComponent = () => {
const { drawerVisit, setDrawerVisit } = useContext(DrawerContext);
const [buckets, setBuckets] = useState<any>([]); const [buckets, setBuckets] = useState<any>([]);
const [loading, setLoading] = useState<boolean>(true); const [loading, setLoading] = useState<boolean>(true);
const [bucketSize, setBucketSize] = useState<string>("");
const [form] = Form.useForm();
const [open, setOpen] = useState<boolean>(false);
async function getAllBucket() { async function getAllBucket() {
getAllMinioBucket("1").then((res: any) => { getAllMinioBucket("1").then((res: any) => {
@@ -53,7 +44,7 @@ const ProCardComponent = () => {
getAllBucket().then(); getAllBucket().then();
}} }}
icon={<ReloadOutlined />}></Button> icon={<ReloadOutlined />}></Button>
<Button type="primary" onClick={() => setDrawerVisit(true)}> <Button type="primary" onClick={() => setOpen(true)}>
</Button> </Button>
</> </>
@@ -68,23 +59,83 @@ const ProCardComponent = () => {
headStyle={{ backgroundColor: "#f0f2f5" }} headStyle={{ backgroundColor: "#f0f2f5" }}
hoverable hoverable
bordered bordered
title={<b>{item.name}</b>} // title={}
style={{ style={{
width: "180px", width: "200px",
height: "150px", marginLeft: 20,
marginLeft: 10,
}} }}
actions={[ actions={[
<SettingOutlined key="setting" />, // eslint-disable-next-line react/jsx-key
<EditOutlined key="edit" />, <Popover
<EllipsisOutlined key="ellipsis" />, content={bucketSize}
title={`size:`}
trigger="click">
<ClusterOutlined
key={"calculate"}
onClick={() => {
getBucketSize(
1,
item.name,
"minio",
).then((res: any) => {
if (
res &&
res.success &&
res.data
) {
setBucketSize(res.data);
} else {
message
.open({
content: "计算出错!",
type: "error",
})
.then();
}
});
}}
/>
</Popover>,
// eslint-disable-next-line react/jsx-key
<Popconfirm
title={`删除存储桶:${item.name}`}
description="你知道你要做什么吗?"
onConfirm={() => {
deleteBucket(1, item.name, "minio").then(
(res: any) => {
if (
res &&
res.data &&
res.data === "success"
) {
message
.open({
content: "删除成功!",
type: "success",
})
.then();
} else {
message
.open({
content: res.data,
type: "error",
})
.then();
}
},
);
}}
okText="Yes"
cancelText="No">
<DeleteOutlined key="delete" />
</Popconfirm>,
]}> ]}>
<Avatar <Avatar
shape={"square"} shape={"square"}
src={bucket as any} src={bucket as any}
size={"large"} size={"large"}
/> />
<b style={{ marginLeft: 10 }}>{item.size}</b> <b style={{ marginLeft: 5 }}>{item.name}</b>
</ProCard> </ProCard>
</div> </div>
); );
@@ -98,32 +149,49 @@ const ProCardComponent = () => {
return ( return (
<> <>
{bucketList()} {bucketList()}
<DrawerForm <Modal
title="创建存储桶" title="创建存储桶"
open={drawerVisit} width={"30%"}
onOpenChange={setDrawerVisit} open={open}
onFinish={async (values) => { closable={true}
console.log(values.name); onOk={() => {
message.success("提交成功"); const bucketName = form.getFieldValue("name" as any);
return true; if (bucketName === "" || bucketName === null) {
}}> message.open({
<ProForm.Group> content: " 请输入存储桶名称!",
<ProFormText type: "warning",
width="md" });
} else {
creatBucket(1, bucketName, "minio").then((res: any) => {
if (res && res.success) {
message
.open({
content: "创建成功!",
type: "success",
})
.then();
setOpen(false);
} else {
message
.open({
content: res.data,
type: "error",
})
.then();
}
});
}
}}
onCancel={() => setOpen(false)}>
<Form layout="vertical" form={form}>
<Form.Item
name="name" name="name"
label="存储桶名" label="存储桶名"
tooltip="最长为 24 位" rules={[{ required: true, message: "请输入存储桶名称!" }]}>
placeholder="请输入名称" <Input placeholder="请输入存储桶名称" />
/> </Form.Item>
</Form>
<ProFormText </Modal>
width="md"
name="capacity"
label="我方公司名称"
placeholder="请输入名称"
/>
</ProForm.Group>
</DrawerForm>
</> </>
); );
}; };

View File

@@ -6,7 +6,6 @@ import React, { createContext, useContext, useEffect, useState } from "react";
import { EditOutlined, EllipsisOutlined, ReloadOutlined, SettingOutlined } from "@ant-design/icons"; import { EditOutlined, EllipsisOutlined, ReloadOutlined, SettingOutlined } from "@ant-design/icons";
import bucket from "@/assets/icons/bucket.svg"; import bucket from "@/assets/icons/bucket.svg";
import styles from "./index.module.less"; import styles from "./index.module.less";
import { getAllMinioBucket } from "@/api/oss/minio";
import { getAllQiniuBucket } from "@/api/oss/qiniu"; import { getAllQiniuBucket } from "@/api/oss/qiniu";
const DrawerContext = createContext<{ const DrawerContext = createContext<{

View File

@@ -1,83 +1,147 @@
/** @format */ /** @format */
import { Avatar, Button, message } from "antd"; import { Avatar, Button, Form, Input, message, Modal, Popconfirm, Popover, Skeleton } from "antd";
import { import { ProCard } from "@ant-design/pro-components";
DrawerForm, import { useEffect, useState } from "react";
ProCard, import { ClusterOutlined, DeleteOutlined, ReloadOutlined } from "@ant-design/icons";
ProForm, import bucket from "@/assets/icons/bucket.svg";
ProFormText,
} from "@ant-design/pro-components";
import React, { createContext, useContext, useState } from "react";
import { EditOutlined, EllipsisOutlined, SettingOutlined } from "@ant-design/icons";
import bucket from "../../../../assets/icons/bucket.svg";
import styles from "./index.module.less"; import styles from "./index.module.less";
import { getAllMinioBucket } from "@/api/oss/minio";
import { creatBucket, deleteBucket, getBucketSize } from "@/api/oss";
const DrawerContext = createContext<{
drawerVisit: boolean;
setDrawerVisit: React.Dispatch<React.SetStateAction<boolean>>;
}>({} as any);
const QiniuDrawer = () => { const QiniuDrawer = () => {
const [drawerVisit, setDrawerVisit] = useState(false); const [buckets, setBuckets] = useState<any>([]);
return ( const [loading, setLoading] = useState<boolean>(true);
<DrawerContext.Provider value={{ drawerVisit, setDrawerVisit }}> const [bucketSize, setBucketSize] = useState<string>("");
<ProCardComponent /> const [form] = Form.useForm();
</DrawerContext.Provider> const [open, setOpen] = useState<boolean>(false);
);
}; async function getAllBucket() {
const ProCardComponent = () => { getAllMinioBucket("1").then((res: any) => {
const { drawerVisit, setDrawerVisit } = useContext(DrawerContext); if (res && res.success) {
const list = [ setBuckets(res.data);
"test1", setLoading(false);
"test2", }
"test3", });
"test1", }
"test2",
"test3", useEffect(() => {
"test1", getAllBucket().then();
"test2", }, []);
"test3",
"test1",
"test2",
"test3",
"test3",
"test1",
"test2",
"test3",
];
const bucketList = () => { const bucketList = () => {
return ( return (
<ProCard <ProCard
title={"存储桶列表"} title={"MinIO存储桶列表"}
headerBordered headerBordered
extra={ extra={
<Button type="primary" onClick={() => setDrawerVisit(true)}> <>
<Button
type="default"
shape={"circle"}
style={{ marginRight: 20 }}
onClick={() => {
setLoading(false);
getAllBucket().then();
}}
icon={<ReloadOutlined />}></Button>
<Button type="primary" onClick={() => setOpen(true)}>
</Button> </Button>
</>
}> }>
<div className={styles.div_proCard}> <div className={styles.div_proCard}>
{list.map((item, index) => { <Skeleton loading={loading} paragraph={{ rows: 13 }} active>
{buckets &&
Array.from(buckets).map((item: any, index: number) => {
return ( return (
<div key={index}>
<ProCard <ProCard
headStyle={{ backgroundColor: "#f0f2f5" }} headStyle={{ backgroundColor: "#f0f2f5" }}
hoverable hoverable
bordered bordered
key={index} // title={}
title={<b>{item}</b>}
style={{ style={{
width: "180px", width: "180px",
borderWidth: "1px", marginLeft: 10,
}} }}
actions={[ actions={[
<SettingOutlined key="setting" />, // eslint-disable-next-line react/jsx-key
<EditOutlined key="edit" />, <Popover
<EllipsisOutlined key="ellipsis" />, content={bucketSize}
title={`size:`}
trigger="click">
<ClusterOutlined
key={"calculate"}
onClick={() => {
getBucketSize(
1,
item.name,
"minio",
).then((res: any) => {
if (
res &&
res.success &&
res.data
) {
setBucketSize(res.data);
} else {
message
.open({
content: "计算出错!",
type: "error",
})
.then();
}
});
}}
/>
</Popover>,
// eslint-disable-next-line react/jsx-key
<Popconfirm
title={`删除存储桶:${item.name}`}
description="你知道你要做什么吗?"
onConfirm={() => {
deleteBucket(1, item.name, "minio").then(
(res: any) => {
if (
res &&
res.data &&
res.data === "success"
) {
message
.open({
content: "删除成功!",
type: "success",
})
.then();
} else {
message
.open({
content: res.data,
type: "error",
})
.then();
}
},
);
}}
okText="Yes"
cancelText="No">
<DeleteOutlined key="delete" />
</Popconfirm>,
]}> ]}>
<Avatar shape={"square"} src={bucket} size={"large"} /> <Avatar
<b style={{ marginLeft: 10 }}> size</b> shape={"square"}
src={bucket as any}
size={"large"}
/>
<b style={{ marginLeft: 5 }}>{item.name}</b>
</ProCard> </ProCard>
</div>
); );
})} })}
</Skeleton>
</div> </div>
</ProCard> </ProCard>
); );
@@ -86,32 +150,49 @@ const ProCardComponent = () => {
return ( return (
<> <>
{bucketList()} {bucketList()}
<DrawerForm <Modal
title="创建存储桶" title="创建存储桶"
open={drawerVisit} width={"30%"}
onOpenChange={setDrawerVisit} open={open}
onFinish={async (values) => { closable={true}
console.log(values.name); onOk={() => {
message.success("提交成功"); const bucketName = form.getFieldValue("name" as any);
return true; if (bucketName === "" || bucketName === null) {
}}> message.open({
<ProForm.Group> content: " 请输入存储桶名称!",
<ProFormText type: "warning",
width="md" });
} else {
creatBucket(1, bucketName, "qiniu").then((res: any) => {
if (res && res.success) {
message
.open({
content: "创建成功!",
type: "success",
})
.then();
setOpen(false);
} else {
message
.open({
content: res.data,
type: "error",
})
.then();
}
});
}
}}
onCancel={() => setOpen(false)}>
<Form layout="vertical" form={form}>
<Form.Item
name="name" name="name"
label="存储桶名" label="存储桶名"
tooltip="最长为 24 位" rules={[{ required: true, message: "请输入存储桶名称!" }]}>
placeholder="请输入名称" <Input placeholder="请输入存储桶名称" />
/> </Form.Item>
</Form>
<ProFormText </Modal>
width="md"
name="capacity"
label="我方公司名称"
placeholder="请输入名称"
/>
</ProForm.Group>
</DrawerForm>
</> </>
); );
}; };

View File

@@ -1,32 +1,23 @@
/** @format */ /** @format */
import { Avatar, Button, message, Skeleton } from "antd"; import { Avatar, Button, Form, Input, message, Modal, Popconfirm, Popover, Skeleton } from "antd";
import { DrawerForm, ProCard, ProForm, ProFormText } from "@ant-design/pro-components"; import { ProCard } from "@ant-design/pro-components";
import React, { createContext, useContext, useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { EditOutlined, EllipsisOutlined, SettingOutlined } from "@ant-design/icons"; import { ClusterOutlined, DeleteOutlined, ReloadOutlined } from "@ant-design/icons";
import bucket from "../../../../assets/icons/bucket.svg"; import bucket from "@/assets/icons/bucket.svg";
import styles from "./index.module.less"; import styles from "./index.module.less";
import { getAllTencentOSsBucket } from "@/api/oss/tencent"; import { getAllMinioBucket } from "@/api/oss/minio";
import { creatBucket, deleteBucket, getBucketSize } from "@/api/oss";
const DrawerContext = createContext<{
drawerVisit: boolean;
setDrawerVisit: React.Dispatch<React.SetStateAction<boolean>>;
}>({} as any);
const TencentDrawer = () => { const TencentDrawer = () => {
const [drawerVisit, setDrawerVisit] = useState(false);
return (
<DrawerContext.Provider value={{ drawerVisit, setDrawerVisit }}>
<ProCardComponent />
</DrawerContext.Provider>
);
};
const ProCardComponent = () => {
const { drawerVisit, setDrawerVisit } = useContext(DrawerContext);
const [buckets, setBuckets] = useState<any>([]); const [buckets, setBuckets] = useState<any>([]);
const [loading, setLoading] = useState<boolean>(true); const [loading, setLoading] = useState<boolean>(true);
const [bucketSize, setBucketSize] = useState<string>("");
const [form] = Form.useForm();
const [open, setOpen] = useState<boolean>(false);
async function getAllBucket() { async function getAllBucket() {
getAllTencentOSsBucket("1").then((res: any) => { getAllMinioBucket("1").then((res: any) => {
if (res && res.success) { if (res && res.success) {
setBuckets(res.data); setBuckets(res.data);
setLoading(false); setLoading(false);
@@ -41,12 +32,23 @@ const ProCardComponent = () => {
const bucketList = () => { const bucketList = () => {
return ( return (
<ProCard <ProCard
title={"腾讯云COS存储桶列表"} title={"MinIO存储桶列表"}
headerBordered headerBordered
extra={ extra={
<Button type="primary" onClick={() => setDrawerVisit(true)}> <>
<Button
type="default"
shape={"circle"}
style={{ marginRight: 20 }}
onClick={() => {
setLoading(false);
getAllBucket().then();
}}
icon={<ReloadOutlined />}></Button>
<Button type="primary" onClick={() => setOpen(true)}>
</Button> </Button>
</>
}> }>
<div className={styles.div_proCard}> <div className={styles.div_proCard}>
<Skeleton loading={loading} paragraph={{ rows: 13 }} active> <Skeleton loading={loading} paragraph={{ rows: 13 }} active>
@@ -58,23 +60,83 @@ const ProCardComponent = () => {
headStyle={{ backgroundColor: "#f0f2f5" }} headStyle={{ backgroundColor: "#f0f2f5" }}
hoverable hoverable
bordered bordered
title={<b>{item.name}</b>} // title={}
style={{ style={{
width: "180px", width: "180px",
height: "150px",
marginLeft: 10, marginLeft: 10,
}} }}
actions={[ actions={[
<SettingOutlined key="setting" />, // eslint-disable-next-line react/jsx-key
<EditOutlined key="edit" />, <Popover
<EllipsisOutlined key="ellipsis" />, content={bucketSize}
title={`size:`}
trigger="click">
<ClusterOutlined
key={"calculate"}
onClick={() => {
getBucketSize(
1,
item.name,
"minio",
).then((res: any) => {
if (
res &&
res.success &&
res.data
) {
setBucketSize(res.data);
} else {
message
.open({
content: "计算出错!",
type: "error",
})
.then();
}
});
}}
/>
</Popover>,
// eslint-disable-next-line react/jsx-key
<Popconfirm
title={`删除存储桶:${item.name}`}
description="你知道你要做什么吗?"
onConfirm={() => {
deleteBucket(1, item.name, "minio").then(
(res: any) => {
if (
res &&
res.data &&
res.data === "success"
) {
message
.open({
content: "删除成功!",
type: "success",
})
.then();
} else {
message
.open({
content: res.data,
type: "error",
})
.then();
}
},
);
}}
okText="Yes"
cancelText="No">
<DeleteOutlined key="delete" />
</Popconfirm>,
]}> ]}>
<Avatar <Avatar
shape={"square"} shape={"square"}
src={bucket as any} src={bucket as any}
size={"large"} size={"large"}
/> />
<b style={{ marginLeft: 10 }}>{item.size}</b> <b style={{ marginLeft: 5 }}>{item.name}</b>
</ProCard> </ProCard>
</div> </div>
); );
@@ -88,32 +150,49 @@ const ProCardComponent = () => {
return ( return (
<> <>
{bucketList()} {bucketList()}
<DrawerForm <Modal
title="创建存储桶" title="创建存储桶"
open={drawerVisit} width={"30%"}
onOpenChange={setDrawerVisit} open={open}
onFinish={async (values) => { closable={true}
console.log(values.name); onOk={() => {
message.success("提交成功"); const bucketName = form.getFieldValue("name" as any);
return true; if (bucketName === "" || bucketName === null) {
}}> message.open({
<ProForm.Group> content: " 请输入存储桶名称!",
<ProFormText type: "warning",
width="md" });
} else {
creatBucket(1, bucketName, "tencent").then((res: any) => {
if (res && res.success) {
message
.open({
content: "创建成功!",
type: "success",
})
.then();
setOpen(false);
} else {
message
.open({
content: res.data,
type: "error",
})
.then();
}
});
}
}}
onCancel={() => setOpen(false)}>
<Form layout="vertical" form={form}>
<Form.Item
name="name" name="name"
label="存储桶名" label="存储桶名"
tooltip="最长为 24 位" rules={[{ required: true, message: "请输入存储桶名称!" }]}>
placeholder="请输入名称" <Input placeholder="请输入存储桶名称" />
/> </Form.Item>
</Form>
<ProFormText </Modal>
width="md"
name="capacity"
label="我方公司名称"
placeholder="请输入名称"
/>
</ProForm.Group>
</DrawerForm>
</> </>
); );
}; };

View File

@@ -1,34 +1,68 @@
/** @format */ /** @format */
import React from "react"; import React, { useEffect, useState } from "react";
import { Avatar, Card, Flex, Input, message, Select, Upload } from "antd"; import { Avatar, Card, Flex, Input, message, Progress, Select, Upload } from "antd";
import { CloudUploadOutlined } from "@ant-design/icons"; import { CloudUploadOutlined } from "@ant-design/icons";
import { ProCard } from "@ant-design/pro-components"; import { ProCard } from "@ant-design/pro-components";
import selectOptions from "@/components/Main/Settings/settings.ts";
import StorageIcon from "@/constant/stroage-icon.ts"; import StorageIcon from "@/constant/stroage-icon.ts";
import { getStorageBuckets, uploadFiles } from "@/api/oss";
import useStore from "@/utils/store/useStore.tsx";
import axios from "axios";
const { Dragger } = Upload; const { Dragger } = Upload;
const props: any = {
name: "file", const FileUpload: React.FC = (props: any) => {
multiple: true, const [buckets, setBuckets] = useState<any[]>([]);
directory: true, const store = useStore("file");
action: "https://660d2bd96ddfa2943b33731c.mockapi.io/api/upload", const [defaultFileList, setDefaultFileList] = useState([]);
onChange(info: any) { async function getBuckets(type: any) {
const { status } = info.file; getStorageBuckets("1", type).then((res: any) => {
if (status !== "uploading") { if (res && res.success) {
console.log(info.file, info.fileList); setBuckets(res.data);
} }
if (status === "done") { });
message.success(`${info.file.name} file uploaded successfully.`);
} else if (status === "error") {
message.error(`${info.file.name} file upload failed.`);
} }
}, const handleOnChange = ({ fileList }) => {
onDrop(e: any) { setDefaultFileList(fileList);
console.log("Dropped files", e.dataTransfer.files); };
},
}; const uploadFile = async (options) => {
const FileUpload: React.FC = () => { const { onSuccess, onError, file, onProgress } = options;
if (
store.getUploadFilePath() === null ||
(store.getUploadFileBucket() === null && store.getUploadFileStorage() === null)
) {
message.open({
content: "请选择存储桶和存储路径",
type: "error",
});
return;
}
const fileData = new FormData();
fileData.append("file", file);
const formData: any = {
userId: "1",
bucketName: store.getUploadFileBucket(),
path: store.getUploadFilePath(),
};
console.log(formData);
uploadFiles(store.getUploadFileStorage(), fileData, formData).then((res: any) => {
if (res && res.success) {
message.open({
content: "上传成功",
type: "success",
});
onSuccess(res.success);
} else {
message.open({
content: "上传失败",
type: "error",
});
setDefaultFileList([]);
onError(res.success);
}
});
};
return ( return (
<> <>
<Flex vertical={false} align={"center"}> <Flex vertical={false} align={"center"}>
@@ -45,11 +79,24 @@ const FileUpload: React.FC = () => {
}} }}
showSearch={true} showSearch={true}
allowClear={true} allowClear={true}
notFoundContent={"未找到,请先配置存储商"} onSelect={(value: any) => {
placeholder={"请选择存储商"}> store.setUploadFileStorage(value);
{selectOptions.map((storage: any, index: any) => { getBuckets(value).then();
}}
onClear={() => {
store.setUploadFileStorage("");
setBuckets([]);
}}
onChange={(value: any) => {
store.setUploadFileStorage(value);
}}
fieldNames={{
label: "name",
value: "ossType",
}}
labelRender={(label: any) => {
return ( return (
<Select.Option value={storage.value} key={index}> <>
<Card <Card
bordered={false} bordered={false}
style={{ style={{
@@ -59,22 +106,46 @@ const FileUpload: React.FC = () => {
flexDirection: "row", flexDirection: "row",
}} }}
size={"small"}> size={"small"}>
<Avatar <Avatar src={StorageIcon[label.value]} size={"small"} />
src={StorageIcon[storage.value]}
size={"small"}
/>{" "}
<span <span
style={{ style={{
marginLeft: "10px", marginLeft: "10px",
fontWeight: "bolder", fontWeight: "bolder",
}}> }}>
{storage.name} {label.label}
</span> </span>
</Card> </Card>
</Select.Option> </>
); );
})} }}
</Select> options={props.userStorage}
notFoundContent={"未找到,请先配置存储商"}
optionRender={(item: any) => {
return (
<>
<Card
bordered={false}
style={{
height: 35,
display: "flex",
alignItems: "center",
flexDirection: "row",
}}
size={"small"}>
<Avatar src={StorageIcon[item.value]} size={"small"} />
<span
style={{
marginLeft: "10px",
fontWeight: "bolder",
}}>
{item.label}
</span>
</Card>
</>
);
}}
placeholder={"请选择存储商"}></Select>
<Select <Select
size="large" size="large"
status="warning" status="warning"
@@ -85,13 +156,55 @@ const FileUpload: React.FC = () => {
alignItems: "center", alignItems: "center",
marginLeft: "10px", marginLeft: "10px",
}} }}
onSelect={(value: any) => {
store.setUploadFileBucket(value);
}}
onClear={() => {
store.setUploadFileBucket("");
setBuckets([]);
}}
onChange={(value: any) => {
store.setUploadFileBucket(value);
}}
showSearch={true} showSearch={true}
allowClear={true} allowClear={true}
notFoundContent={"未找到,请先配置存储商"} notFoundContent={"未找到,请先配置存储商"}
placeholder={"请选择存储桶"}> fieldNames={{
{selectOptions.map((storage: any, index: any) => { label: "name",
value: "name",
}}
labelRender={(label: any) => {
return ( return (
<Select.Option value={storage.value} key={index}> <>
<Card
bordered={false}
style={{
height: 35,
display: "flex",
alignItems: "center",
flexDirection: "row",
}}
size={"small"}>
<Avatar
src={StorageIcon["bucket"]}
shape={"square"}
size={"small"}
/>
<span
style={{
marginLeft: "10px",
fontWeight: "bolder",
}}>
{label.label}
</span>
</Card>
</>
);
}}
options={buckets}
optionRender={(item: any) => {
return (
<>
<Card <Card
bordered={false} bordered={false}
style={{ style={{
@@ -111,24 +224,41 @@ const FileUpload: React.FC = () => {
marginLeft: "10px", marginLeft: "10px",
fontWeight: "bolder", fontWeight: "bolder",
}}> }}>
{storage.name} {item.label}
</span> </span>
</Card> </Card>
</Select.Option> </>
); );
})} }}
</Select> placeholder={"请选择存储桶"}></Select>
<Input placeholder={"请输入路径/默认当前打开的路径"} style={{ marginLeft: 10,width: "40%" }}></Input> <Input
placeholder={"请输入路径/默认当前打开的路径"}
onChange={(e: any) => {
store.setUploadFilePath(e.target.value);
}}
style={{ marginLeft: 10, width: "40%" }}></Input>
</Flex> </Flex>
</ProCard> </ProCard>
</Flex> </Flex>
<ProCard> <ProCard>
<Dragger {...props}> <Dragger
name={"file"}
multiple={false}
directory={false}
maxCount={1}
defaultFileList={defaultFileList}
headers={{
ContentType: "multipart/form-data",
}}
onChange={handleOnChange}
customRequest={uploadFile}
onDrop={() => uploadFile}>
<p className="ant-upload-drag-icon"> <p className="ant-upload-drag-icon">
<CloudUploadOutlined /> <CloudUploadOutlined />
</p> </p>
<p className="ant-upload-text"></p> <p className="ant-upload-text"></p>
<p className="ant-upload-hint"></p> <p className="ant-upload-hint"></p>
</Dragger> </Dragger>
</ProCard> </ProCard>
</> </>

View File

@@ -1,5 +1,5 @@
/** @format */ /** @format */
import React, { FunctionComponent, useEffect, useState } from "react"; import { FunctionComponent, useEffect, useState } from "react";
import { CheckCard, ProCard } from "@ant-design/pro-components"; import { CheckCard, ProCard } from "@ant-design/pro-components";
import styles from "./index.module.less"; import styles from "./index.module.less";
import { import {
@@ -14,23 +14,26 @@ import {
Input, Input,
message, message,
Modal, Modal,
Popover,
QRCode,
Select, Select,
Skeleton,
Tooltip, Tooltip,
} from "antd"; } from "antd";
import bucket from "@/assets/icons/bucket.svg"; import bucket from "@/assets/icons/bucket.svg";
import { import {
CheckOutlined,
CloudUploadOutlined, CloudUploadOutlined,
CopyOutlined, CopyOutlined,
DeleteOutlined, DeleteOutlined,
DownloadOutlined, DownloadOutlined,
EditOutlined,
EllipsisOutlined, EllipsisOutlined,
EyeOutlined, EyeOutlined,
LeftOutlined, LeftOutlined,
QrcodeOutlined, QrcodeOutlined,
RedoOutlined, RedoOutlined,
ReloadOutlined, ReloadOutlined,
ScissorOutlined,
SearchOutlined,
SnippetsOutlined, SnippetsOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
import standard_dir from "@/assets/icons/standard_directory.svg"; import standard_dir from "@/assets/icons/standard_directory.svg";
@@ -39,34 +42,39 @@ import { observer } from "mobx-react";
import FileIcon from "@/constant/file-icon.ts"; import FileIcon from "@/constant/file-icon.ts";
import file_icon from "@/assets/icons/files/file.svg"; import file_icon from "@/assets/icons/files/file.svg";
import FileUpload from "@/components/Main/File/components/FileUpload.tsx"; import FileUpload from "@/components/Main/File/components/FileUpload.tsx";
import { getAllStorage, getBucketFiles, getStorageBuckets } from "@/api/oss"; import logo from "@/assets/images/logo.png";
import {
copyFile,
deleteFile,
downloadFiles,
getAllStorage,
getBucketFiles,
getStorageBuckets,
previewFile,
renameFile,
} from "@/api/oss";
import StorageIcon from "@/constant/stroage-icon.ts"; import StorageIcon from "@/constant/stroage-icon.ts";
const fileList: any = [ import { useNavigate } from "react-router-dom";
{ import Base64 from "base-64";
name: "test1.png",
isDir: false,
path: "test1.png",
length: "1766511",
createTime: "2024-06-29T10:28:03.224Z",
},
{
name: "schisandra",
isDir: true,
path: "schisandra/",
},
];
const File: FunctionComponent = () => { const File: FunctionComponent = () => {
const store = useStore("file"); const store = useStore("file");
const navigate = useNavigate();
const [files, setFiles] = useState<any>([]); const [files, setFiles] = useState<any>([]);
const [loading, setLoading] = useState<boolean>(true); const [loading, setLoading] = useState<boolean>(true);
const [bucketLoading, setBucketLoading] = useState<boolean>(true); const [loadingFile, setLoadingFile] = useState<boolean>(true);
const [open, setOpen] = useState<boolean>(false); const [open, setOpen] = useState<boolean>(false);
const [openRename, setOpenRename] = useState<boolean>(false);
const [userStorage, setUserStorage] = useState([]); const [userStorage, setUserStorage] = useState([]);
const [buckets, setBuckets] = useState<any[]>([]); const [buckets, setBuckets] = useState<any[]>([]);
const [currentStorage, setCurrentStorage] = useState<any>(null); const [newFileName, setNewFileName] = useState<any>(null);
const [currentBucket, setCurrentBucket] = useState<any>(null); const [disable, setDisable] = useState<boolean>(true);
const [previewUrl, setPreviewUrl] = useState<any>(null);
function setLoadingTimeOut() {
setTimeout(() => {
setLoadingFile(false);
}, 500);
}
async function getUserStorage() { async function getUserStorage() {
getAllStorage("1").then((res: any) => { getAllStorage("1").then((res: any) => {
if (res && res.success) { if (res && res.success) {
@@ -79,6 +87,7 @@ const File: FunctionComponent = () => {
return getBucketFiles("1", bucket, dirName, type).then((res: any) => { return getBucketFiles("1", bucket, dirName, type).then((res: any) => {
if (res && res.success) { if (res && res.success) {
setFiles(res.data); setFiles(res.data);
setLoadingTimeOut();
} }
}); });
} }
@@ -90,7 +99,6 @@ const File: FunctionComponent = () => {
getStorageBuckets("1", type).then((res: any) => { getStorageBuckets("1", type).then((res: any) => {
if (res && res.success) { if (res && res.success) {
setBuckets(res.data); setBuckets(res.data);
setBucketLoading(false);
} }
}); });
} }
@@ -116,26 +124,24 @@ const File: FunctionComponent = () => {
showSearch={true} showSearch={true}
allowClear={true} allowClear={true}
onSelect={(value: any) => { onSelect={(value: any) => {
setCurrentStorage(value); store.setCurrentStorage(value);
getBuckets(value).then(); getBuckets(value).then();
}} }}
onClear={() => { onClear={() => {
setCurrentStorage(null); store.setCurrentStorage("");
setBuckets([]); setBuckets([]);
}} }}
onChange={(value: any) => { onChange={(value: any) => {
setCurrentStorage(value); store.setCurrentStorage(value);
// getBuckets(value).then();
}} }}
notFoundContent={"未找到,请先配置存储商"} fieldNames={{
placeholder={"请选择存储商"}> label: "name",
{userStorage && value: "ossType",
userStorage.map((item: any, index: number) => { }}
labelRender={(label: any) => {
return ( return (
<> <>
<Select.Option value={item.ossType} key={index}>
<Card <Card
key={item}
bordered={false} bordered={false}
style={{ style={{
height: 35, height: 35,
@@ -145,7 +151,7 @@ const File: FunctionComponent = () => {
}} }}
size={"small"}> size={"small"}>
<Avatar <Avatar
src={StorageIcon[item.ossType]} src={StorageIcon[label.value]}
size={"small"} size={"small"}
/> />
<span <span
@@ -153,21 +159,49 @@ const File: FunctionComponent = () => {
marginLeft: "10px", marginLeft: "10px",
fontWeight: "bolder", fontWeight: "bolder",
}}> }}>
{item.name} {label.label}
</span> </span>
</Card> </Card>
</Select.Option>
</> </>
); );
})} }}
</Select> options={userStorage}
notFoundContent={"未找到,请先配置存储商"}
optionRender={(item: any) => {
return (
<>
<Card
bordered={false}
style={{
height: 35,
display: "flex",
alignItems: "center",
flexDirection: "row",
}}
size={"small"}>
<Avatar
src={StorageIcon[item.value]}
size={"small"}
/>
<span
style={{
marginLeft: "10px",
fontWeight: "bolder",
}}>
{item.label}
</span>
</Card>
</>
);
}}
placeholder={"请选择存储商"}></Select>
<Button <Button
type="default" type="default"
shape={"circle"} shape={"circle"}
style={{ marginLeft: 10 }} style={{ marginLeft: 10 }}
onClick={() => { onClick={() => {
if (currentStorage != null) { if (store.getCurrentStorage() != null) {
getBuckets(currentStorage).then(); getBuckets(store.getCurrentStorage()).then();
} }
}} }}
icon={<ReloadOutlined />}></Button> icon={<ReloadOutlined />}></Button>
@@ -176,14 +210,14 @@ const File: FunctionComponent = () => {
<div> <div>
<CheckCard.Group <CheckCard.Group
size={"small"} size={"small"}
loading={bucketLoading}
onChange={(value: any) => { onChange={(value: any) => {
setFiles([]);
store.clearAllFilePath(); store.clearAllFilePath();
store.setFilePath(`/${value}`); store.setFilePath(`/${value}`);
setCurrentBucket(value); store.setCurrentBucket(value);
console.log(value);
if (value !== undefined) { if (value !== undefined) {
getFiles(value, "", currentStorage).then(); setLoadingFile(true);
getFiles(value, "", store.getCurrentStorage()).then();
} }
}} }}
style={{ width: "100%" }}> style={{ width: "100%" }}>
@@ -196,7 +230,6 @@ const File: FunctionComponent = () => {
{buckets && {buckets &&
Array.from(buckets).map((item: any, index: number) => { Array.from(buckets).map((item: any, index: number) => {
return ( return (
<>
<div key={index}> <div key={index}>
<CheckCard <CheckCard
style={{ width: "100%" }} style={{ width: "100%" }}
@@ -208,22 +241,19 @@ const File: FunctionComponent = () => {
<Dropdown <Dropdown
placement="top" placement="top"
menu={{ menu={{
onClick: ({ onClick: (value: any) => {
domEvent, if (
}) => { value.key === "view"
domEvent.stopPropagation(); ) {
message.info( navigate(
"menu click", "/main/bucket",
); );
}
}, },
items: [ items: [
{
label: "删除",
key: "1",
},
{ {
label: "查看", label: "查看",
key: "2", key: "view",
}, },
], ],
}}> }}>
@@ -240,7 +270,6 @@ const File: FunctionComponent = () => {
} }
/> />
</div> </div>
</>
); );
})} })}
</> </>
@@ -258,18 +287,59 @@ const File: FunctionComponent = () => {
shape="circle" shape="circle"
icon={<LeftOutlined />} icon={<LeftOutlined />}
onClick={() => { onClick={() => {
if (
store.getCurrentBucket() === null ||
store.getCurrentStorage() === null
) {
message.open({
content: "请选择存储商和桶",
type: "warning",
});
return;
}
if (store.getFilePath().length === 1) {
if (files.length !== 0) {
return;
}
setLoadingFile(true);
getFiles( getFiles(
currentBucket, store.getCurrentBucket(),
"",
store.getCurrentStorage(),
).then();
} else {
setLoadingFile(true);
getFiles(
store.getCurrentBucket(),
store.getMiddlePath(), store.getMiddlePath(),
currentStorage, store.getCurrentStorage(),
).then(() => { ).then(() => {
store.clearFilePath(); store.clearFilePath();
}); });
}
}} }}
/> />
<Button <Button
style={{ marginLeft: 20 }} style={{ marginLeft: 20 }}
shape="circle" shape="circle"
onClick={() => {
if (
store.getCurrentBucket() === null ||
store.getCurrentStorage() === null
) {
message.open({
content: "请选择存储商和桶",
type: "warning",
});
return;
}
setLoadingFile(true);
getFiles(
store.getCurrentBucket(),
store.getFilePathExceptFirst(),
store.getCurrentStorage(),
).then();
}}
icon={<RedoOutlined />} icon={<RedoOutlined />}
/> />
</Flex> </Flex>
@@ -283,13 +353,60 @@ const File: FunctionComponent = () => {
align={"center"} align={"center"}
justify={"flex-start"} justify={"flex-start"}
style={{ marginTop: 10 }}> style={{ marginTop: 10 }}>
<Tooltip title="删除" placement={"bottom"}> {/*<Tooltip title="删除" placement={"bottom"}>*/}
<Button shape="circle" icon={<DeleteOutlined />} /> {/* <Button shape="circle" icon={<DeleteOutlined />} />*/}
</Tooltip> {/*</Tooltip>*/}
<Tooltip title="下载" placement={"bottom"}> <Tooltip title="下载" placement={"bottom"}>
<Button <Button
style={{ marginLeft: 20 }} style={{ marginLeft: 0 }}
shape="circle" shape="circle"
onClick={() => {
if (
store.getCurrentBucket() === null ||
store.getCurrentStorage() === null
) {
message.open({
content: "请选择存储商和桶",
type: "warning",
});
return;
}
if (store.getCurrentFile() === null) {
message
.open({
content: "请先选择文件!",
type: "warning",
})
.then();
return;
}
downloadFiles(
1,
store.getCurrentBucket(),
store.getCurrentStorage(),
store.getCurrentFile(),
).then((res: any) => {
const content = res.data;
const blob = new Blob([content]);
const url = window.URL.createObjectURL(blob);
const link = document.createElement("a");
link.style.display = "none";
link.href = url;
// 取后端给前端返的请求头中的文件名称
const temp = res.headers["content-disposition"]
.split(";")[1]
.split("filename=")[1];
const fileName = decodeURIComponent(temp);
console.log("fileName:" + fileName);
link.download = fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link); //下载完成移除元素
window.URL.revokeObjectURL(url); //释放掉blob对象
});
}}
icon={<DownloadOutlined />} icon={<DownloadOutlined />}
/> />
</Tooltip> </Tooltip>
@@ -297,50 +414,324 @@ const File: FunctionComponent = () => {
<Button <Button
style={{ marginLeft: 20 }} style={{ marginLeft: 20 }}
shape="circle" shape="circle"
onClick={() => {
if (
store.getCurrentBucket() === null ||
store.getCurrentStorage() === null
) {
message.open({
content: "请选择存储商和桶",
type: "warning",
});
return;
}
if (store.getCurrentFile() !== null) {
previewFile(
1,
store.getCurrentBucket(),
store.getCurrentStorage(),
store.getCurrentFile(),
).then((res: any) => {
if (res && res.success && res.data) {
window.open(
"http://1.95.0.111:8012/onlinePreview?url=" +
encodeURIComponent(
Base64.encode(res.data) as any,
),
);
}
});
} else {
message
.open({
content: "请先选择文件!",
type: "warning",
})
.then();
}
}}
icon={<EyeOutlined />} icon={<EyeOutlined />}
/> />
</Tooltip> </Tooltip>
<Tooltip title="扫码下载" placement={"bottom"}> <Tooltip title="扫码查看" placement={"bottom"}>
<Popover
placement="bottom"
title={"扫码查看"}
trigger="click"
content={
<>
<QRCode
errorLevel="H"
size={300}
iconSize={300 / 5}
value={previewUrl}
icon={logo as any}
/>
</>
}>
<Button <Button
style={{ marginLeft: 20 }} style={{ marginLeft: 20 }}
shape="circle" shape="circle"
onClick={() => {
if (
store.getCurrentBucket() === null ||
store.getCurrentStorage() === null
) {
message.open({
content: "请选择存储商和桶",
type: "warning",
});
return;
}
if (store.getCurrentFile() === null) {
message
.open({
content: "请先选择文件!",
type: "warning",
})
.then();
return;
}
previewFile(
1,
store.getCurrentBucket(),
store.getCurrentStorage(),
store.getCurrentFile(),
).then((res: any) => {
if (res && res.success && res.data) {
setPreviewUrl(
"http://1.95.0.111:8012/onlinePreview?url=" +
encodeURIComponent(
Base64.encode(res.data) as any,
),
);
}
});
}}
icon={<QrcodeOutlined />} icon={<QrcodeOutlined />}
/> />
</Popover>
</Tooltip> </Tooltip>
<Tooltip title="粘贴" placement={"bottom"}> <Tooltip title="粘贴" placement={"bottom"}>
<Button <Button
style={{ marginLeft: 20 }} style={{ marginLeft: 20 }}
disabled={disable}
shape="circle" shape="circle"
onClick={() => {
if (
store.getCurrentBucket() === null ||
store.getCurrentStorage() === null
) {
message.open({
content: "请选择存储商和桶",
type: "warning",
});
return;
}
if (store.getCopyFile() === null) {
message.open({
content: "请先复制文件",
type: "warning",
});
return;
}
copyFile(
1,
store.getCurrentBucket(),
store.getCurrentStorage(),
store.getCopyFile(),
store.getFilePathExceptFirst() +
"/" +
store.getCopyFileName(),
).then((res: any) => {
if (res && res.success) {
message
.open({
content: "粘贴成功 ",
type: "success",
})
.then();
setDisable(true);
} else {
message
.open({
content: "粘贴失败 ",
type: "error",
})
.then();
}
});
}}
icon={<SnippetsOutlined />} icon={<SnippetsOutlined />}
/> />
</Tooltip> </Tooltip>
<Tooltip title="剪切" placement={"bottom"}> {/*<Tooltip title="剪切" placement={"bottom"}>*/}
<Button {/* <Button*/}
style={{ marginLeft: 20 }} {/* style={{ marginLeft: 20 }}*/}
shape="circle" {/* shape="circle"*/}
icon={<ScissorOutlined />} {/* icon={<ScissorOutlined />}*/}
/> {/* />*/}
</Tooltip> {/*</Tooltip>*/}
<Tooltip title="复制" placement={"bottom"}> <Tooltip title="复制" placement={"bottom"}>
<Button <Button
style={{ marginLeft: 20 }} style={{ marginLeft: 20 }}
shape="circle" shape="circle"
onClick={() => {
if (
store.getCurrentBucket() === null ||
store.getCurrentStorage() === null
) {
message.open({
content: "请选择存储商和桶",
type: "warning",
});
return;
}
if (store.getCurrentFile() === null) {
message.open({
content: "请先选择文件!",
type: "warning",
});
return;
}
if (
store.getCurrentFile() !== "" &&
store.getCurrentFile() !== null
) {
store.setCopyFile(store.getCurrentFile() as string);
message
.open({
content: "复制成功 ",
type: "success",
})
.then();
setDisable(false);
} else {
message
.open({
content: "请先选择文件!",
type: "warning",
})
.then();
}
}}
icon={<CopyOutlined />} icon={<CopyOutlined />}
/> />
</Tooltip> </Tooltip>
<Tooltip title="搜索" placement={"bottom"}> {/*<Tooltip title="搜索" placement={"bottom"}>*/}
{/* <Button*/}
{/* style={{ marginLeft: 20 }}*/}
{/* shape="circle"*/}
{/* icon={<SearchOutlined />}*/}
{/* />*/}
{/*</Tooltip>*/}
<Tooltip title="重命名" placement={"bottom"}>
<Popover
onOpenChange={(value: any) => {
setOpenRename(value);
}}
open={openRename}
content={
<Flex vertical={false} align={"center"}>
<Input
onChange={(e: any) => {
setNewFileName(e.target.value);
}}></Input>
<Button
shape={"circle"}
onClick={() => {
if (
store.getCurrentBucket() === null ||
store.getCurrentStorage() === null
) {
message.open({
content: "请选择存储商和桶",
type: "warning",
});
return;
}
if (newFileName !== null) {
renameFile(
1,
store.getCurrentBucket(),
store.getCurrentStorage(),
store.getCurrentFile(),
store.getFilePathExceptFirst() +
"/" +
newFileName,
).then((res: any) => {
if (res && res.success) {
message
.open({
content: "重命名成功!",
type: "success",
})
.then();
setNewFileName(null);
setOpenRename(false);
} else {
message
.open({
content: "重命名失败!",
type: "error",
})
.then();
}
});
} else {
message
.open({
content: "请输入新文件名!",
type: "warning",
})
.then();
}
}}
icon={<CheckOutlined />}></Button>
</Flex>
}
title={"重命名:" + store.getCurrentFile()}
trigger="click">
<Button <Button
style={{ marginLeft: 20 }} style={{ marginLeft: 20 }}
shape="circle" shape="circle"
icon={<SearchOutlined />} icon={<EditOutlined />}
onClick={() => {
if (
store.getCurrentBucket() === null ||
store.getCurrentStorage() === null
) {
message.open({
content: "请选择存储商和桶",
type: "warning",
});
return;
}
if (
store.getCurrentFile() === null ||
store.getCurrentFile() === ""
) {
message
.open({
content: "请先选择文件!",
type: "warning",
})
.then();
return;
} else {
setOpenRename(true);
}
}}
/> />
</Popover>
</Tooltip> </Tooltip>
</Flex> </Flex>
</div> </div>
<div className={styles.file_right_content}> <div className={styles.file_right_content}>
<Skeleton active loading={loadingFile} paragraph={{ rows: 13 }}>
<CheckCard.Group <CheckCard.Group
multiple={true} multiple={false}
bordered={false} bordered={false}
className={styles.file_right_content}> className={styles.file_right_content}>
{files.map((file: any, index: any) => { {files.map((file: any, index: any) => {
@@ -350,24 +741,31 @@ const File: FunctionComponent = () => {
key={index} key={index}
menu={{ menu={{
items: [ items: [
{ // {
label: "删除", // label: "删除",
key: "1", // key: "delete",
}, // },
{
label: "编辑",
key: "2",
},
], ],
}} }}
trigger={["contextMenu"]}> trigger={["contextMenu"]}>
<div <div
style={{ marginLeft: 10 }} style={{ marginLeft: 10 }}
onDoubleClick={() => { onDoubleClick={() => {
if (
store.getCurrentBucket() === null ||
store.getCurrentStorage() === null
) {
message.open({
content: "请选择存储商和桶",
type: "warning",
});
return;
}
setLoadingTimeOut();
getFiles( getFiles(
currentBucket, store.getCurrentBucket(),
`${file.name}/`, `${store.getFilePathExceptFirst()}/${file.name}`,
currentStorage, store.getCurrentStorage(),
).then(() => { ).then(() => {
store.setFilePath(file.name); store.setFilePath(file.name);
}); });
@@ -418,14 +816,136 @@ const File: FunctionComponent = () => {
<Dropdown <Dropdown
key={index} key={index}
menu={{ menu={{
onClick: ({ key }) => {
if (key === "delete") {
deleteFile(
1,
store.getCurrentBucket(),
store.getCurrentStorage(),
store.getFilePathExceptFirst() +
"/" +
file.name,
).then((res: any) => {
if (res && res.success) {
message.open({
content: "删除成功!",
type: "success",
});
} else {
message.open({
content: "删除失败!",
type: "error",
});
}
});
} else if (key === "view") {
previewFile(
1,
store.getCurrentBucket(),
store.getCurrentStorage(),
store.getFilePathExceptFirst() +
"/" +
file.name,
).then((res: any) => {
if (
res &&
res.success &&
res.data
) {
window.open(
"http://1.95.0.111:8012/onlinePreview?url=" +
encodeURIComponent(
Base64.encode(
res.data,
) as any,
),
);
}
});
} else if (key === "copy") {
if (
store.getCurrentFile() !== "" &&
store.getCurrentFile() !== null
) {
message
.open({
content: "复制成功 ",
type: "success",
})
.then();
store.setCopyFile(
store.getCurrentFile() as string,
);
setDisable(false);
} else {
message
.open({
content:
"请先选择文件!",
type: "warning",
})
.then();
}
} else if (key === "paste") {
if (disable) {
message
.open({
content: "请先复制文件",
type: "warning",
})
.then();
} else {
copyFile(
1,
store.getCurrentBucket(),
store.getCurrentStorage(),
store.getCopyFile(),
store.getFilePathExceptFirst() +
"/" +
store.getCopyFileName(),
).then((res: any) => {
if (res && res.success) {
message
.open({
content:
"粘贴成功 ",
type: "success",
})
.then();
setDisable(true);
} else {
message
.open({
content:
"粘贴失败 ",
type: "error",
})
.then();
}
});
}
}
},
items: [ items: [
{ {
label: "删除", label: "复制",
key: "1", key: "copy",
icon: <CopyOutlined />,
}, },
{ {
label: "编辑", label: "粘贴",
key: "2", key: "paste",
icon: <SnippetsOutlined />,
},
{
label: "删除",
key: "delete",
icon: <DeleteOutlined />,
},
{
label: "预览",
key: "view",
icon: <EyeOutlined />,
}, },
], ],
}} }}
@@ -434,6 +954,24 @@ const File: FunctionComponent = () => {
<CheckCard <CheckCard
size={"small"} size={"small"}
value={file.name} value={file.name}
onChange={(value: any) => {
if (value) {
const pathExceptFirst =
store.getFilePathExceptFirst();
const fileValue =
pathExceptFirst +
"/" +
file.name;
store.setCurrentFile(fileValue);
store.setCopyFileName(
file.name,
);
}
if (value === false) {
store.setCopyFileName("");
store.setCurrentFile("");
}
}}
bodyStyle={{ overflow: "hidden" }} bodyStyle={{ overflow: "hidden" }}
avatar={ avatar={
<div <div
@@ -484,6 +1022,7 @@ const File: FunctionComponent = () => {
} }
})} })}
</CheckCard.Group> </CheckCard.Group>
</Skeleton>
</div> </div>
</ProCard> </ProCard>
</div> </div>
@@ -502,7 +1041,7 @@ const File: FunctionComponent = () => {
open={open} open={open}
width={"50%"} width={"50%"}
onCancel={() => setOpen(false)}> onCancel={() => setOpen(false)}>
<FileUpload></FileUpload> <FileUpload userStorage={userStorage}></FileUpload>
</Modal> </Modal>
</> </>
); );

View File

@@ -1,6 +1,6 @@
/** @format */ /** @format */
import React, { memo, useEffect, useState } from "react"; import React, { memo, useEffect, useState } from "react";
import { Avatar, Card, Flex, message, Skeleton, Space, Tag, Tooltip } from "antd"; import { Avatar, Card, Flex, message, Skeleton, Tooltip } from "antd";
import styles from "./index.module.less"; import styles from "./index.module.less";
import ReactECharts from "echarts-for-react"; import ReactECharts from "echarts-for-react";
import { ProCard, ProList } from "@ant-design/pro-components"; import { ProCard, ProList } from "@ant-design/pro-components";
@@ -17,11 +17,13 @@ import {
getUserDownloadFileDiagramPerMonth, getUserDownloadFileDiagramPerMonth,
getUserFileCount, getUserFileCount,
getUserFileFlow, getUserFileFlow,
getUserFileHeatMap, getUserRecentDownloadFile, getUserRecentPreviewFile, getUserRecentUploadFile, getUserFileHeatMap,
getUserUploadFileDiagramPerMonth getUserRecentPreviewFile,
getUserRecentUploadFile,
getUserUploadFileDiagramPerMonth,
} from "@/api/oss"; } from "@/api/oss";
import { Link, useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { EyeOutlined, InfoCircleOutlined } from "@ant-design/icons"; import { InfoCircleOutlined } from "@ant-design/icons";
import FileIcon from "@/constant/file-icon.ts"; import FileIcon from "@/constant/file-icon.ts";
import file_icon from "@/assets/icons/files/file.svg"; import file_icon from "@/assets/icons/files/file.svg";
@@ -42,10 +44,9 @@ const MainHome: React.FC = () => {
const [monthUpload, setMonthUpload] = useState<any>([]); const [monthUpload, setMonthUpload] = useState<any>([]);
const [monthDownload, setMonthDownload] = useState<any>([]); const [monthDownload, setMonthDownload] = useState<any>([]);
const [recentUploadFile, setRecentUploadFile] = useState<any>([]); const [recentUploadFile, setRecentUploadFile] = useState<any>([]);
const [recentDownloadFile, setRecentDownloadFile] = useState<any>([]); // const [recentDownloadFile, setRecentDownloadFile] = useState<any>([]);
const [recentPreviewFile, setRecentPreviewFile] = useState<any>([]); const [recentPreviewFile, setRecentPreviewFile] = useState<any>([]);
// 获取存储同和存储商的个数 // 获取存储同和存储商的个数
async function getStorageAndBucketsCount() { async function getStorageAndBucketsCount() {
return getStorageAndBuckets("1").then((res: any) => { return getStorageAndBuckets("1").then((res: any) => {
@@ -101,7 +102,7 @@ const MainHome: React.FC = () => {
if (res && res.success && res.data) { if (res && res.success && res.data) {
setMonthUpload(res.data); setMonthUpload(res.data);
} }
}) });
} }
// 获取用户最近上传文件 // 获取用户最近上传文件
async function getRecentUploadFile() { async function getRecentUploadFile() {
@@ -109,23 +110,23 @@ const MainHome: React.FC = () => {
if (res && res.success && res.data) { if (res && res.success && res.data) {
setRecentUploadFile(res.data); setRecentUploadFile(res.data);
} }
}) });
} }
//获取用户最近下载文件 //获取用户最近下载文件
async function getRecentDownloadFile() { // async function getRecentDownloadFile() {
getUserRecentDownloadFile(1).then((res: any)=>{ // getUserRecentDownloadFile(1).then((res: any) => {
if(res && res.success && res.data){ // if (res && res.success && res.data) {
setRecentDownloadFile(res.data); // setRecentDownloadFile(res.data);
} // }
}) // });
} // }
//获取用户最近预览文件 //获取用户最近预览文件
async function getRecentPreviewFile() { async function getRecentPreviewFile() {
getUserRecentPreviewFile(1).then((res:any)=>{ getUserRecentPreviewFile(1).then((res: any) => {
if(res && res.success && res.data){ if (res && res.success && res.data) {
setRecentPreviewFile(res.data); setRecentPreviewFile(res.data);
} }
}) });
} }
const option = { const option = {
tooltip: { tooltip: {
@@ -145,7 +146,8 @@ const MainHome: React.FC = () => {
height: "80%", height: "80%",
containLabel: true, containLabel: true,
}, },
xAxis: [{ xAxis: [
{
type: "time", type: "time",
// data: month, // data: month,
axisLine: { axisLine: {
@@ -153,7 +155,8 @@ const MainHome: React.FC = () => {
color: "#999", color: "#999",
}, },
}, },
}], },
],
yAxis: { yAxis: {
type: "value", type: "value",
@@ -257,9 +260,8 @@ const MainHome: React.FC = () => {
], ],
}; };
useEffect(() => { useEffect(() => {
getStorageAndBucketsCount().then(()=>{ getStorageAndBucketsCount().then(() => {
getUploadDownloadFlux().then(() => { getUploadDownloadFlux().then(() => {
getUploadDownloadCount().then(() => { getUploadDownloadCount().then(() => {
setLoading(false); setLoading(false);
@@ -268,27 +270,25 @@ const MainHome: React.FC = () => {
}); });
}, []); }, []);
useEffect(() => { useEffect(() => {
getFileHeatMap().then(()=>{ getFileHeatMap().then(() => {
setLoadingHeatmap(false); setLoadingHeatmap(false);
}); });
}, []); }, []);
useEffect(() => { useEffect(() => {
getDownloadCountByMonth().then(()=>{ getDownloadCountByMonth().then(() => {
getUploadCountByMonth().then(()=>{ getUploadCountByMonth().then(() => {
setLoadingEChart(false); setLoadingEChart(false);
}); });
}); });
}, []); }, []);
useEffect(() => { useEffect(() => {
getRecentUploadFile().then(()=>{ getRecentUploadFile().then(() => {
setLoadingRecentFile(false); setLoadingRecentFile(false);
}); });
getRecentPreviewFile().then(()=>{ getRecentPreviewFile().then(() => {
setLoadingPreviewFile(false); setLoadingPreviewFile(false);
}); });
}, []); }, []);
@@ -343,11 +343,20 @@ const MainHome: React.FC = () => {
</span> </span>
</Flex> </Flex>
</Flex> </Flex>
<Flex vertical={false} align={"center"} justify={"flex-end"} style={{marginTop: 50}}> <Flex
<Tooltip title={"存储商个数,点击查看详情"} color={"#47D8BE"}> vertical={false}
<InfoCircleOutlined onClick={()=>{ align={"center"}
navigate("/main/setting") justify={"flex-end"}
}} className={styles.home_content_icon}/> style={{ marginTop: 50 }}>
<Tooltip
title={"存储商个数,点击查看详情"}
color={"#47D8BE"}>
<InfoCircleOutlined
onClick={() => {
navigate("/main/setting");
}}
className={styles.home_content_icon}
/>
</Tooltip> </Tooltip>
</Flex> </Flex>
</Flex> </Flex>
@@ -395,11 +404,20 @@ const MainHome: React.FC = () => {
</span> </span>
</Flex> </Flex>
</Flex> </Flex>
<Flex vertical={false} align={"center"} justify={"flex-end"} style={{marginTop: 50}}> <Flex
<Tooltip title={"存储桶个数,点击查看详情"} color={"#47D8BE"}> vertical={false}
<InfoCircleOutlined onClick={()=>{ align={"center"}
navigate("/main/bucket") justify={"flex-end"}
}} className={styles.home_content_icon}/> style={{ marginTop: 50 }}>
<Tooltip
title={"存储桶个数,点击查看详情"}
color={"#47D8BE"}>
<InfoCircleOutlined
onClick={() => {
navigate("/main/bucket");
}}
className={styles.home_content_icon}
/>
</Tooltip> </Tooltip>
</Flex> </Flex>
</Flex> </Flex>
@@ -567,11 +585,12 @@ const MainHome: React.FC = () => {
showMonthLabels={true} showMonthLabels={true}
horizontal={true} horizontal={true}
showWeekdayLabels={false} showWeekdayLabels={false}
onClick={(value:any)=>{ onClick={(value: any) => {
if(value!==null) { if (value !== null) {
message.open({ message
.open({
content: (<> content: (
<>
<Flex vertical={true}> <Flex vertical={true}>
<Flex vertical={false} align={"center"}> <Flex vertical={false} align={"center"}>
{value.date} {value.date}
@@ -580,12 +599,13 @@ const MainHome: React.FC = () => {
{value.count} {value.count}
</Flex> </Flex>
</Flex> </Flex>
</>), </>
type:"success", ),
type: "success",
duration: 2, duration: 2,
}).then(); })
.then();
} }
}} }}
monthLabels={[ monthLabels={[
"一月", "一月",
@@ -632,36 +652,43 @@ const MainHome: React.FC = () => {
dataIndex: "fileName", dataIndex: "fileName",
}, },
avatar: { avatar: {
render: (text: any,record: any) => { render: (_: any, record: any) => {
if(record.fileName) { if (record.fileName) {
return (<> return (
<>
<Avatar <Avatar
src={ src={
FileIcon[getFileExtension(record.fileName)] || file_icon FileIcon[
getFileExtension(
record.fileName,
)
] || file_icon
} }
shape={"square"} shape={"square"}
size={"large"} size={"large"}
/> />
</>) </>
}else { );
return (<> } else {
return (
<>
<Avatar <Avatar
src={file_icon as any} src={file_icon as any}
shape={"square"} shape={"square"}
size={"large"} size={"large"}
/> />
</>) </>
} );
} }
}, },
},
description: { description: {
dataIndex: "size", dataIndex: "size",
valueType: "text" valueType: "text",
}, },
content: { content: {
dataIndex: "time" dataIndex: "time",
} },
}} }}
/> />
</Skeleton> </Skeleton>
@@ -682,36 +709,43 @@ const MainHome: React.FC = () => {
dataIndex: "fileName", dataIndex: "fileName",
}, },
avatar: { avatar: {
render: (text: any,record: any) => { render: (_: any, record: any) => {
if(record.fileName) { if (record.fileName) {
return (<> return (
<>
<Avatar <Avatar
src={ src={
FileIcon[getFileExtension(record.fileName)] || file_icon FileIcon[
getFileExtension(
record.fileName,
)
] || file_icon
} }
shape={"square"} shape={"square"}
size={"large"} size={"large"}
/> />
</>) </>
}else { );
return (<> } else {
return (
<>
<Avatar <Avatar
src={file_icon as any} src={file_icon as any}
shape={"square"} shape={"square"}
size={"large"} size={"large"}
/> />
</>) </>
} );
} }
}, },
},
description: { description: {
dataIndex: "size", dataIndex: "size",
valueType: "text", valueType: "text",
}, },
content: { content: {
dataIndex: "time" dataIndex: "time",
} },
}} }}
/> />
</Skeleton> </Skeleton>

View File

@@ -2,22 +2,38 @@
import { PlusOutlined } from "@ant-design/icons"; import { PlusOutlined } from "@ant-design/icons";
import type { ActionType, ProColumns } from "@ant-design/pro-components"; import type { ActionType, ProColumns } from "@ant-design/pro-components";
import { ProTable, TableDropdown } from "@ant-design/pro-components"; import { ProTable, TableDropdown } from "@ant-design/pro-components";
import { Button, Col, Drawer, Form, Input, Row, Space } from "antd"; import {
Button,
Col,
Drawer,
Flex,
Form,
Input,
message,
Modal,
Row,
Space,
} from "antd";
import React, { useEffect, useRef, useState } from "react"; import React, { useEffect, useRef, useState } from "react";
import { getAllAliOSSConfig } from "@/api/oss/ali"; import {
addAliOSSConfig,
deleteAliConfig,
getAliConfigDetailById,
getAllAliOSSConfig,
initAliOSS,
setAliShutdown,
updateAliConfig,
} from "@/api/oss/ali";
type AliOssConfigItem = { const AliSettings: React.FC = () => {
id: number; const [form] = Form.useForm();
userId: number; const actionRef = useRef<ActionType>();
endpoint: string; const [open, setOpen] = useState(false);
accessKeyId: string; const [openModal, setOpenModal] = useState(false);
accessKeySecret: string; const [configs, setConfigs] = useState<any>([]);
createdTime: string; const [loading, setLoading] = useState<boolean>(true);
updateTime: string; const [configDetail, setConfigDetail] = useState<object>({});
status: string; const columns: ProColumns<any[]>[] = [
};
const columns: ProColumns<AliOssConfigItem>[] = [
{ {
dataIndex: "index", dataIndex: "index",
valueType: "indexBorder", valueType: "indexBorder",
@@ -25,10 +41,12 @@ const columns: ProColumns<AliOssConfigItem>[] = [
}, },
{ {
title: "ID", title: "ID",
valueType: "text",
dataIndex: "id", dataIndex: "id",
copyable: true, copyable: true,
ellipsis: true, ellipsis: true,
tooltip: "Id", tooltip: "Id",
editable: false,
}, },
{ {
disable: true, disable: true,
@@ -59,18 +77,25 @@ const columns: ProColumns<AliOssConfigItem>[] = [
dataIndex: "createdTime", dataIndex: "createdTime",
valueType: "dateTime", valueType: "dateTime",
sorter: true, sorter: true,
editable: false,
}, },
{ {
title: "更新时间", title: "更新时间",
dataIndex: "updateTime", dataIndex: "updateTime",
valueType: "dateTime", valueType: "dateTime",
sorter: true, sorter: true,
editable: false,
}, },
{ {
disable: true, disable: true,
title: "状态", title: "状态",
dataIndex: "status", dataIndex: "status",
search: true, search: true,
valueEnum: {
true: { text: "开启", status: "Success" },
false: { text: "关闭", status: "Error" },
},
editable: false,
}, },
{ {
title: "操作", title: "操作",
@@ -86,34 +111,126 @@ const columns: ProColumns<AliOssConfigItem>[] = [
}}> }}>
</a>, </a>,
<a target="_blank" rel="noopener noreferrer" key="view"> <a
target="_blank"
rel="noopener noreferrer"
key="view"
onClick={() => {
getConfigDetail(record.id).then(() => {
setOpenModal(true);
setLoading(false);
});
}}>
</a>, </a>,
<TableDropdown <TableDropdown
key="actionGroup" key="actionGroup"
onSelect={() => action?.reload()} onSelect={(key: string) => {
if (key === "open") {
init(record.id).then();
} else if (key === "close") {
shutdown(record.id).then();
}
}}
menus={[ menus={[
{ key: "copy", name: "复制" }, { key: "open", name: "开启" },
{ key: "delete", name: "删除" }, { key: "close", name: "关闭" },
]} ]}
/>, />,
], ],
}, },
]; ];
const AliSettings: React.FC = () => {
const actionRef = useRef<ActionType>();
const [open, setOpen] = useState(false);
const [configs, setConfigs] = useState<AliOssConfigItem[]>([]);
async function getAllConfig() { async function getAllConfig() {
getAllAliOSSConfig(1).then((res) => { getAllAliOSSConfig("1").then((res: any) => {
console.log(res);
if (res && res.success && res.data) { if (res && res.success && res.data) {
setConfigs(res.data); setConfigs(res.data);
} }
}); });
} }
async function getConfigDetail(id: any) {
getAliConfigDetailById(id).then((res: any) => {
if (res && res.success && res.data) {
setConfigDetail(res.data);
}
});
}
async function init(id: any) {
initAliOSS("1", id).then((res: any) => {
if (res && res.success) {
message.open({
content: "开启成功",
type: "success",
});
getAllConfig().then();
} else {
message.open({
content: res.data,
type: "error",
});
}
});
}
async function shutdown(id: any) {
setAliShutdown("1", id).then((res: any) => {
if (res && res.success) {
message.open({
content: res.data,
type: "success",
});
getAllConfig().then();
} else {
message.open({
content: res.data,
type: "error",
});
}
});
}
async function deleteConfig(id: any) {
const form: any = {
id: id,
isDeleted: 1,
};
deleteAliConfig(form).then((res: any) => {
if (res && res.success) {
message.open({
content: "删除成功",
type: "success",
});
getAllConfig().then();
} else {
message.open({
content: res.data,
type: "error",
});
}
});
}
async function addConfigs() {
const fieldsValue = form.getFieldsValue();
const AliOssConfig = {
userId: "1",
...fieldsValue,
};
addAliOSSConfig(AliOssConfig).then((res: any) => {
if (res && res.success) {
onClose();
getAllConfig().then(() => {
message
.open({
content: "新增成功",
type: "success",
})
.then();
});
} else {
message.open({
content: res.data,
type: "error",
});
}
});
}
const showDrawer = () => { const showDrawer = () => {
setOpen(true); setOpen(true);
}; };
@@ -121,7 +238,7 @@ const AliSettings: React.FC = () => {
setOpen(false); setOpen(false);
}; };
useEffect(() => { useEffect(() => {
getAllConfig(); getAllConfig().then();
}, []); }, []);
const AddAliOssConfigDrawer = () => { const AddAliOssConfigDrawer = () => {
return ( return (
@@ -139,12 +256,12 @@ const AliSettings: React.FC = () => {
extra={ extra={
<Space> <Space>
<Button onClick={onClose}></Button> <Button onClick={onClose}></Button>
<Button onClick={onClose} type="primary"> <Button onClick={addConfigs} type="primary">
</Button> </Button>
</Space> </Space>
}> }>
<Form layout="vertical"> <Form layout="vertical" form={form}>
<Row gutter={16}> <Row gutter={16}>
<Col span={12}> <Col span={12}>
<Form.Item <Form.Item
@@ -180,14 +297,52 @@ const AliSettings: React.FC = () => {
}; };
return ( return (
<> <>
<div style={{ height: "65vh" }}> <div style={{ minHeight: "65vh" }}>
<ProTable<AliOssConfigItem> <ProTable
columns={columns} columns={columns}
dataSource={configs} dataSource={configs}
actionRef={actionRef} actionRef={actionRef}
cardBordered={true} cardBordered={true}
editable={{ editable={{
type: "multiple", type: "multiple",
onSave: async (__: any, originRow: any, _: any) => {
const updateForm: any = {
id: originRow.id,
endpoint: originRow.endpoint,
accessKeyId: originRow.accessKeyId,
accessKeySecret: originRow.accessKeySecret,
};
updateAliConfig(updateForm).then((res: any) => {
if (res && res.success) {
message.open({
content: "修改成功!",
type: "success",
});
getAllConfig().then();
} else {
message.open({
content: res.data,
type: "error",
});
}
});
},
onDelete: async (row: any) => {
deleteConfig(row).then((res: any) => {
if (res && res.success) {
message.open({
content: "删除成功",
type: "success",
});
getAllConfig().then();
} else {
message.open({
content: res.data,
type: "error",
});
}
});
},
}} }}
columnsState={{ columnsState={{
persistenceKey: "pro-table-singe-demos", persistenceKey: "pro-table-singe-demos",
@@ -224,6 +379,43 @@ const AliSettings: React.FC = () => {
]} ]}
/> />
<AddAliOssConfigDrawer /> <AddAliOssConfigDrawer />
<Modal
title={<p></p>}
loading={loading}
footer={false}
open={openModal}
afterClose={() => {
setConfigDetail({});
}}
onCancel={() => setOpenModal(false)}>
<Flex vertical={false} align={"center"}>
<h4>ID:</h4> <span style={{ marginLeft: 10 }}>{configDetail.id}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>endpoint: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.endpoint}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>accessKeyId: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.accessKeyId} </span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>accessKeySecret: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.accessKeySecret}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>status: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.status}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>createdTime: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.createdTime}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>updateTime: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.updateTime}</span>
</Flex>
</Modal>
</div> </div>
</> </>
); );

View File

@@ -2,9 +2,17 @@
import { PlusOutlined } from "@ant-design/icons"; import { PlusOutlined } from "@ant-design/icons";
import type { ActionType, ProColumns } from "@ant-design/pro-components"; import type { ActionType, ProColumns } from "@ant-design/pro-components";
import { ProTable, TableDropdown } from "@ant-design/pro-components"; import { ProTable, TableDropdown } from "@ant-design/pro-components";
import { Button, Col, Drawer, Form, Input, Row, Space } from "antd"; import { Button, Col, Drawer, Flex, Form, Input, message, Modal, Row, Space } from "antd";
import React, { ReactNode, useEffect, useRef, useState } from "react"; import React, { ReactNode, useEffect, useRef, useState } from "react";
import { getAllMinioConfig } from "@/api/oss/minio"; import {
addMinioOSSConfig,
deleteMinioConfig,
getAllMinioConfig,
getMinioConfigDetailById,
initMinioOSS,
setMinioShutdown,
updateMinioConfig,
} from "@/api/oss/minio";
type MinioOssConfigItem = { type MinioOssConfigItem = {
id: number; id: number;
@@ -17,11 +25,20 @@ type MinioOssConfigItem = {
status: string; status: string;
}; };
const columns: ProColumns<MinioOssConfigItem>[] = [ const MinioSettings: React.FC = () => {
const [form] = Form.useForm();
const actionRef = useRef<ActionType>();
const [open, setOpen] = useState(false);
const [configs, setConfigs] = useState<MinioOssConfigItem[]>([]);
const [openModal, setOpenModal] = useState(false);
const [loading, setLoading] = useState<boolean>(true);
const [configDetail, setConfigDetail] = useState<object>({});
const columns: ProColumns<MinioOssConfigItem>[] = [
{ {
dataIndex: "index", dataIndex: "index",
valueType: "indexBorder", valueType: "indexBorder",
width: 48, width: 48,
editable: false,
}, },
{ {
title: "ID", title: "ID",
@@ -29,6 +46,7 @@ const columns: ProColumns<MinioOssConfigItem>[] = [
copyable: true, copyable: true,
ellipsis: true, ellipsis: true,
tooltip: "Id", tooltip: "Id",
editable: false,
}, },
{ {
disable: true, disable: true,
@@ -54,24 +72,32 @@ const columns: ProColumns<MinioOssConfigItem>[] = [
ellipsis: true, ellipsis: true,
copyable: true, copyable: true,
}, },
{
disable: true,
title: "状态",
dataIndex: "status",
search: true,
},
{ {
title: "创建时间", title: "创建时间",
dataIndex: "createdTime", dataIndex: "createdTime",
valueType: "dateTime", valueType: "dateTime",
sorter: true, sorter: true,
hideInSearch: true, hideInSearch: true,
editable: false,
}, },
{ {
title: "更新时间", title: "更新时间",
dataIndex: "updateTime", dataIndex: "updateTime",
valueType: "dateTime", valueType: "dateTime",
sorter: true, sorter: true,
editable: false,
},
{
disable: true,
title: "状态",
dataIndex: "status",
search: true,
valueEnum: {
true: { text: "开启", status: "Success" },
false: { text: "关闭", status: "Error" },
},
editable: false,
}, },
{ {
title: "操作", title: "操作",
@@ -79,7 +105,7 @@ const columns: ProColumns<MinioOssConfigItem>[] = [
key: "option", key: "option",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error // @ts-expect-error
render: (text: ReactNode, record: MinioOssConfigItem, _: number, action: any) => [ render: (text: ReactNode, record: any, _: number, action: any) => [
<a <a
key="editable" key="editable"
onClick={() => { onClick={() => {
@@ -87,25 +113,35 @@ const columns: ProColumns<MinioOssConfigItem>[] = [
}}> }}>
</a>, </a>,
<a target="_blank" rel="noopener noreferrer" key="view"> <a
target="_blank"
rel="noopener noreferrer"
key="view"
onClick={() => {
getConfigDetail(record.id).then(() => {
setOpenModal(true);
setLoading(false);
});
}}>
</a>, </a>,
<TableDropdown <TableDropdown
key="actionGroup" key="actionGroup"
onSelect={() => action?.reload()} onSelect={(key: string) => {
if (key === "open") {
init(record.id).then();
} else if (key === "close") {
shutdown(record.id).then();
}
}}
menus={[ menus={[
{ key: "copy", name: "复制" }, { key: "open", name: "开启" },
{ key: "delete", name: "删除" }, { key: "close", name: "关闭" },
]} ]}
/>, />,
], ],
}, },
]; ];
const MinioSettings: React.FC = () => {
const actionRef = useRef<ActionType>();
const [open, setOpen] = useState(false);
const [configs, setConfigs] = useState<MinioOssConfigItem[]>([]);
const showDrawer = () => { const showDrawer = () => {
setOpen(true); setOpen(true);
}; };
@@ -114,14 +150,101 @@ const MinioSettings: React.FC = () => {
setOpen(false); setOpen(false);
}; };
async function getConfigDetail(id: any) {
getMinioConfigDetailById(id).then((res: any) => {
if (res && res.success && res.data) {
setConfigDetail(res.data);
}
});
}
async function init(id: any) {
initMinioOSS("1", id).then((res: any) => {
if (res && res.success) {
message.open({
content: "开启成功",
type: "success",
});
getAllConfig().then();
} else {
message.open({
content: res.data,
type: "error",
});
}
});
}
async function shutdown(id: any) {
setMinioShutdown("1", id).then((res: any) => {
if (res && res.success) {
message.open({
content: res.data,
type: "success",
});
getAllConfig().then();
} else {
message.open({
content: res.data,
type: "error",
});
}
});
}
async function deleteConfig(id: any) {
const form: any = {
id: id,
isDeleted: 1,
};
deleteMinioConfig(form).then((res: any) => {
if (res && res.success) {
message.open({
content: "删除成功",
type: "success",
});
getAllConfig().then();
} else {
message.open({
content: res.data,
type: "error",
});
}
});
}
async function getAllConfig() { async function getAllConfig() {
getAllMinioConfig(1).then((res: any) => { getAllMinioConfig("1").then((res: any) => {
if (res && res.success && res.data) { if (res && res.success && res.data) {
setConfigs(res.data); setConfigs(res.data);
} }
}); });
} }
async function addMinioConfig() {
const fieldsValue = form.getFieldsValue();
const MinioOssConfig = {
userId: "1",
...fieldsValue,
};
addMinioOSSConfig(MinioOssConfig).then((res: any) => {
if (res && res.success) {
message.open({
content: "新增成功",
type: "success",
});
getAllConfig().then(() => {
onClose();
});
} else {
message.open({
content: res.data,
type: "error",
});
}
});
}
useEffect(() => { useEffect(() => {
getAllConfig().then(); getAllConfig().then();
}, []); }, []);
@@ -130,7 +253,7 @@ const MinioSettings: React.FC = () => {
<> <>
<Drawer <Drawer
title="创建连接配置" title="创建连接配置"
width={720} width={"45%"}
onClose={onClose} onClose={onClose}
open={open} open={open}
styles={{ styles={{
@@ -141,12 +264,12 @@ const MinioSettings: React.FC = () => {
extra={ extra={
<Space> <Space>
<Button onClick={onClose}></Button> <Button onClick={onClose}></Button>
<Button onClick={onClose} type="primary"> <Button onClick={addMinioConfig} type="primary">
</Button> </Button>
</Space> </Space>
}> }>
<Form layout="vertical"> <Form layout="vertical" form={form}>
<Row gutter={16}> <Row gutter={16}>
<Col span={12}> <Col span={12}>
<Form.Item <Form.Item
@@ -182,7 +305,7 @@ const MinioSettings: React.FC = () => {
}; };
return ( return (
<> <>
<div style={{ height: "65vh" }}> <div style={{ minHeight: "65vh" }}>
<ProTable<MinioOssConfigItem> <ProTable<MinioOssConfigItem>
columns={columns} columns={columns}
dataSource={configs} dataSource={configs}
@@ -190,6 +313,44 @@ const MinioSettings: React.FC = () => {
cardBordered={true} cardBordered={true}
editable={{ editable={{
type: "multiple", type: "multiple",
onSave: async (__: any, originRow: any, _: any) => {
const updateForm: any = {
id: originRow.id,
endpoint: originRow.endpoint,
accessKey: originRow.accessKey,
secretKey: originRow.secretKey,
};
updateMinioConfig(updateForm).then((res: any) => {
if (res && res.success) {
message.open({
content: "修改成功!",
type: "success",
});
getAllConfig().then();
} else {
message.open({
content: res.data,
type: "error",
});
}
});
},
onDelete: async (row: any) => {
deleteConfig(row).then((res: any) => {
if (res && res.success) {
message.open({
content: "删除成功",
type: "success",
});
getAllConfig().then();
} else {
message.open({
content: res.data,
type: "error",
});
}
});
},
}} }}
columnsState={{ columnsState={{
persistenceKey: "pro-table-singe-demos", persistenceKey: "pro-table-singe-demos",
@@ -226,6 +387,43 @@ const MinioSettings: React.FC = () => {
]} ]}
/> />
<AddMinioOssConfigDrawer /> <AddMinioOssConfigDrawer />
<Modal
title={<p></p>}
loading={loading}
footer={false}
afterClose={() => {
setConfigDetail({});
}}
open={openModal}
onCancel={() => setOpenModal(false)}>
<Flex vertical={false} align={"center"}>
<h4>ID:</h4> <span style={{ marginLeft: 10 }}>{configDetail.id}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>endpoint: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.endpoint}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>accessKey: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.accessKey} </span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>secretKey: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.secretKey}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>status: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.status}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>createdTime: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.createdTime}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>updateTime: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.updateTime}</span>
</Flex>
</Modal>
</div> </div>
</> </>
); );

View File

@@ -2,9 +2,17 @@
import { PlusOutlined } from "@ant-design/icons"; import { PlusOutlined } from "@ant-design/icons";
import type { ActionType, ProColumns } from "@ant-design/pro-components"; import type { ActionType, ProColumns } from "@ant-design/pro-components";
import { ProTable, TableDropdown } from "@ant-design/pro-components"; import { ProTable, TableDropdown } from "@ant-design/pro-components";
import { Button, Col, Drawer, Form, Input, Row, Space } from "antd"; import { Button, Col, Drawer, Flex, Form, Input, message, Modal, Row, Space } from "antd";
import React, { ReactNode, useEffect, useRef, useState } from "react"; import React, { ReactNode, useEffect, useRef, useState } from "react";
import { getAllQiniuConfigs } from "@/api/oss/qiniu"; import {
addQiniuOSSConfig,
deleteQiniuConfig,
getAllQiniuConfigs,
getQiniuConfigDetailById,
initQiniuOSS,
setQiniuShutdown,
updateQiniuConfig,
} from "@/api/oss/qiniu";
type QiniuOssConfigItem = { type QiniuOssConfigItem = {
id: number; id: number;
@@ -17,11 +25,20 @@ type QiniuOssConfigItem = {
status: string; status: string;
}; };
const columns: ProColumns<QiniuOssConfigItem>[] = [ const QiniuSettings: React.FC = () => {
const [form] = Form.useForm();
const actionRef = useRef<ActionType>();
const [open, setOpen] = useState(false);
const [config, setConfig] = useState<QiniuOssConfigItem[]>([]);
const [openModal, setOpenModal] = useState(false);
const [loading, setLoading] = useState<boolean>(true);
const [configDetail, setConfigDetail] = useState<object>({});
const columns: ProColumns<QiniuOssConfigItem>[] = [
{ {
dataIndex: "index", dataIndex: "index",
valueType: "indexBorder", valueType: "indexBorder",
width: 48, width: 48,
editable: false,
}, },
{ {
title: "ID", title: "ID",
@@ -29,6 +46,7 @@ const columns: ProColumns<QiniuOssConfigItem>[] = [
copyable: true, copyable: true,
ellipsis: true, ellipsis: true,
tooltip: "Id", tooltip: "Id",
editable: false,
}, },
{ {
disable: true, disable: true,
@@ -54,24 +72,32 @@ const columns: ProColumns<QiniuOssConfigItem>[] = [
copyable: true, copyable: true,
ellipsis: true, ellipsis: true,
}, },
{
disable: true,
title: "状态",
dataIndex: "status",
search: true,
},
{ {
title: "创建时间", title: "创建时间",
dataIndex: "createdTime", dataIndex: "createdTime",
valueType: "dateTime", valueType: "dateTime",
sorter: true, sorter: true,
hideInSearch: true, hideInSearch: true,
editable: false,
}, },
{ {
title: "更新时间", title: "更新时间",
dataIndex: "updateTime", dataIndex: "updateTime",
valueType: "dateTime", valueType: "dateTime",
sorter: true, sorter: true,
editable: false,
},
{
disable: true,
title: "状态",
dataIndex: "status",
search: true,
valueEnum: {
true: { text: "开启", status: "Success" },
false: { text: "关闭", status: "Error" },
},
editable: false,
}, },
{ {
title: "操作", title: "操作",
@@ -87,25 +113,99 @@ const columns: ProColumns<QiniuOssConfigItem>[] = [
}}> }}>
</a>, </a>,
<a target="_blank" rel="noopener noreferrer" key="view"> <a
target="_blank"
rel="noopener noreferrer"
key="view"
onClick={() => {
getConfigDetail(record.id).then(() => {
setOpenModal(true);
setLoading(false);
});
}}>
</a>, </a>,
<TableDropdown <TableDropdown
key="actionGroup" key="actionGroup"
onSelect={() => action?.reload()} onSelect={(key: string) => {
if (key === "open") {
init(record.id).then();
} else if (key === "close") {
shutdown(record.id).then();
}
}}
menus={[ menus={[
{ key: "copy", name: "复制" }, { key: "open", name: "开启" },
{ key: "delete", name: "删除" }, { key: "close", name: "关闭" },
]} ]}
/>, />,
], ],
}, },
]; ];
async function init(id: any) {
initQiniuOSS("1", id).then((res: any) => {
if (res && res.success) {
message.open({
content: "开启成功",
type: "success",
});
getAllConfigs().then();
} else {
message.open({
content: res.data,
type: "error",
});
}
});
}
async function getConfigDetail(id: any) {
getQiniuConfigDetailById(id).then((res: any) => {
if (res && res.success && res.data) {
setConfigDetail(res.data);
}
});
}
async function shutdown(id: any) {
setQiniuShutdown("1", id).then((res: any) => {
if (res && res.success) {
message.open({
content: res.data,
type: "success",
});
getAllConfigs().then();
} else {
message.open({
content: res.data,
type: "error",
});
}
});
}
async function deleteConfig(id: any) {
const form: any = {
id: id,
isDeleted: 1,
};
deleteQiniuConfig(form).then((res: any) => {
if (res && res.success) {
message.open({
content: "删除成功",
type: "success",
});
getAllConfigs().then();
} else {
message.open({
content: res.data,
type: "error",
});
}
});
}
const QiniuSettings: React.FC = () => {
const actionRef = useRef<ActionType>();
const [open, setOpen] = useState(false);
const [config, setConfig] = useState<QiniuOssConfigItem[]>([]);
const showDrawer = () => { const showDrawer = () => {
setOpen(true); setOpen(true);
}; };
@@ -113,7 +213,8 @@ const QiniuSettings: React.FC = () => {
const onClose = () => { const onClose = () => {
setOpen(false); setOpen(false);
}; };
async function getAllCOnfigs() {
async function getAllConfigs() {
getAllQiniuConfigs(1).then((res: any) => { getAllQiniuConfigs(1).then((res: any) => {
if (res && res.success && res.data) { if (res && res.success && res.data) {
setConfig(res.data); setConfig(res.data);
@@ -121,15 +222,39 @@ const QiniuSettings: React.FC = () => {
}); });
} }
async function addQiniuoConfig() {
const fieldsValue = form.getFieldsValue();
const QiniuOssConfig = {
userId: 1,
...fieldsValue,
};
addQiniuOSSConfig(QiniuOssConfig).then((res: any) => {
if (res && res.success) {
message.open({
content: "新增成功",
type: "success",
});
getAllConfigs().then(() => {
onClose();
});
} else {
message.open({
content: res.data,
type: "error",
});
}
});
}
useEffect(() => { useEffect(() => {
getAllCOnfigs().then(); getAllConfigs().then();
}, []); }, []);
const AddQiniuOssConfigDrawer = () => { const AddQiniuOssConfigDrawer = () => {
return ( return (
<> <>
<Drawer <Drawer
title="创建连接配置" title="创建连接配置"
width={720} width={"45%"}
onClose={onClose} onClose={onClose}
open={open} open={open}
styles={{ styles={{
@@ -140,12 +265,12 @@ const QiniuSettings: React.FC = () => {
extra={ extra={
<Space> <Space>
<Button onClick={onClose}></Button> <Button onClick={onClose}></Button>
<Button onClick={onClose} type="primary"> <Button onClick={addQiniuoConfig} type="primary">
</Button> </Button>
</Space> </Space>
}> }>
<Form layout="vertical"> <Form layout="vertical" form={form}>
<Row gutter={16}> <Row gutter={16}>
<Col span={12}> <Col span={12}>
<Form.Item <Form.Item
@@ -155,6 +280,14 @@ const QiniuSettings: React.FC = () => {
<Input placeholder="请输入存储桶!" /> <Input placeholder="请输入存储桶!" />
</Form.Item> </Form.Item>
</Col> </Col>
<Col span={12}>
<Form.Item
name="endpoint"
label="服务地址"
rules={[{ required: true, message: "请输入服务地址!" }]}>
<Input placeholder="请输入服务地址!" />
</Form.Item>
</Col>
</Row> </Row>
<Row gutter={16}> <Row gutter={16}>
<Col span={12}> <Col span={12}>
@@ -189,6 +322,44 @@ const QiniuSettings: React.FC = () => {
cardBordered={true} cardBordered={true}
editable={{ editable={{
type: "multiple", type: "multiple",
onSave: async (__: any, originRow: any, _: any) => {
const updateForm: any = {
id: originRow.id,
endpoint: originRow.endpoint,
accessKey: originRow.accessKey,
secretKey: originRow.secretKey,
};
updateQiniuConfig(updateForm).then((res: any) => {
if (res && res.success) {
message.open({
content: "修改成功!",
type: "success",
});
getAllConfigs().then();
} else {
message.open({
content: res.data,
type: "error",
});
}
});
},
onDelete: async (row: any) => {
deleteConfig(row).then((res: any) => {
if (res && res.success) {
message.open({
content: "删除成功",
type: "success",
});
getAllConfigs().then();
} else {
message.open({
content: res.data,
type: "error",
});
}
});
},
}} }}
columnsState={{ columnsState={{
persistenceKey: "pro-table-singe-demos", persistenceKey: "pro-table-singe-demos",
@@ -225,6 +396,43 @@ const QiniuSettings: React.FC = () => {
]} ]}
/> />
<AddQiniuOssConfigDrawer /> <AddQiniuOssConfigDrawer />
<Modal
title={<p></p>}
loading={loading}
afterClose={() => {
setConfigDetail({});
}}
footer={false}
open={openModal}
onCancel={() => setOpenModal(false)}>
<Flex vertical={false} align={"center"}>
<h4>ID:</h4> <span style={{ marginLeft: 10 }}>{configDetail.id}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>endpoint: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.endpoint}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>accessKey: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.accessKey} </span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>secretKey: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.secretKey}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>status: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.status}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>createdTime: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.createdTime}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>updateTime: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.updateTime}</span>
</Flex>
</Modal>
</div> </div>
</> </>
); );

View File

@@ -2,25 +2,45 @@
import { PlusOutlined } from "@ant-design/icons"; import { PlusOutlined } from "@ant-design/icons";
import type { ActionType, ProColumns } from "@ant-design/pro-components"; import type { ActionType, ProColumns } from "@ant-design/pro-components";
import { ProTable, TableDropdown } from "@ant-design/pro-components"; import { ProTable, TableDropdown } from "@ant-design/pro-components";
import { Button, Col, Drawer, Form, Input, Row, Space } from "antd"; import { Button, Col, Drawer, Flex, Form, Input, message, Modal, Row, Space } from "antd";
import React, { ReactNode, useEffect, useRef, useState } from "react"; import React, { ReactNode, useEffect, useRef, useState } from "react";
import { getAllTencentOSsConfig } from "@/api/oss/tencent"; import {
addTencentOSSConfig,
deleteTencentConfig,
getAllTencentOSsConfig,
getTencentConfigDetailById,
initTencentOSS,
setTencentShutdown,
updateTencentConfig,
} from "@/api/oss/tencent";
type TencentOssConfigItem = { type TencentOssConfigItem = {
id: number; id: number;
userId: number; userId: number;
endpoint: number;
secretId: string; secretId: string;
secretKey: string; secretKey: string;
appId: string;
region: string;
createdTime: string; createdTime: string;
updateTime: string; updateTime: string;
status: string; status: string;
}; };
const columns: ProColumns<TencentOssConfigItem>[] = [ const TencentSettings: React.FC = () => {
const [form] = Form.useForm();
const actionRef = useRef<ActionType>();
const [open, setOpen] = useState(false);
const [configs, setConfigs] = useState<TencentOssConfigItem[]>([]);
const [openModal, setOpenModal] = useState(false);
const [loading, setLoading] = useState<boolean>(true);
const [configDetail, setConfigDetail] = useState<object>({});
const columns: ProColumns<TencentOssConfigItem>[] = [
{ {
dataIndex: "index", dataIndex: "index",
valueType: "indexBorder", valueType: "indexBorder",
width: 48, width: 48,
editable: false,
}, },
{ {
title: "ID", title: "ID",
@@ -28,6 +48,15 @@ const columns: ProColumns<TencentOssConfigItem>[] = [
copyable: true, copyable: true,
ellipsis: true, ellipsis: true,
tooltip: "Id", tooltip: "Id",
editable: false,
},
{
disable: true,
title: "服务地址",
dataIndex: "endpoint",
copyable: true,
tooltip: "endpoint",
ellipsis: true,
}, },
{ {
disable: true, disable: true,
@@ -47,9 +76,19 @@ const columns: ProColumns<TencentOssConfigItem>[] = [
}, },
{ {
disable: true, disable: true,
title: "状态", title: "appId",
dataIndex: "status", dataIndex: "appId",
search: true, tooltip: "appId",
copyable: true,
ellipsis: true,
},
{
disable: true,
title: "地域",
dataIndex: "region",
tooltip: "region",
copyable: true,
ellipsis: true,
}, },
{ {
title: "创建时间", title: "创建时间",
@@ -57,12 +96,25 @@ const columns: ProColumns<TencentOssConfigItem>[] = [
valueType: "dateTime", valueType: "dateTime",
sorter: true, sorter: true,
hideInSearch: true, hideInSearch: true,
editable: false,
}, },
{ {
title: "更新时间", title: "更新时间",
dataIndex: "updateTime", dataIndex: "updateTime",
valueType: "dateTime", valueType: "dateTime",
sorter: true, sorter: true,
editable: false,
},
{
disable: true,
title: "状态",
dataIndex: "status",
search: true,
valueEnum: {
true: { text: "开启", status: "Success" },
false: { text: "关闭", status: "Error" },
},
editable: false,
}, },
{ {
title: "操作", title: "操作",
@@ -78,25 +130,97 @@ const columns: ProColumns<TencentOssConfigItem>[] = [
}}> }}>
</a>, </a>,
<a target="_blank" rel="noopener noreferrer" key="view"> <a
target="_blank"
rel="noopener noreferrer"
key="view"
onClick={() => {
getConfigDetail(record.id).then(() => {
setOpenModal(true);
setLoading(false);
});
}}>
</a>, </a>,
<TableDropdown <TableDropdown
key="actionGroup" key="actionGroup"
onSelect={() => action?.reload()} onSelect={(key: string) => {
if (key === "open") {
init(record.id).then();
} else if (key === "close") {
shutdown(record.id).then();
}
}}
menus={[ menus={[
{ key: "copy", name: "复制" }, { key: "open", name: "开启" },
{ key: "delete", name: "删除" }, { key: "close", name: "关闭" },
]} ]}
/>, />,
], ],
}, },
]; ];
async function init(id: any) {
initTencentOSS("1", id).then((res: any) => {
if (res && res.success) {
message.open({
content: "开启成功",
type: "success",
});
getAllConfigs().then();
} else {
message.open({
content: res.data,
type: "error",
});
}
});
}
async function getConfigDetail(id: any) {
getTencentConfigDetailById(id).then((res: any) => {
if (res && res.success && res.data) {
setConfigDetail(res.data);
}
});
}
async function shutdown(id: any) {
setTencentShutdown("1", id).then((res: any) => {
if (res && res.success) {
message.open({
content: res.data,
type: "success",
});
getAllConfigs().then();
} else {
message.open({
content: res.data,
type: "error",
});
}
});
}
async function deleteConfig(id: any) {
const form: any = {
id: id,
isDeleted: 1,
};
deleteTencentConfig(form).then((res: any) => {
if (res && res.success) {
message.open({
content: "删除成功",
type: "success",
});
getAllConfigs().then();
} else {
message.open({
content: res.data,
type: "error",
});
}
});
}
const TencentSettings: React.FC = () => {
const actionRef = useRef<ActionType>();
const [open, setOpen] = useState(false);
const [configs, setConfigs] = useState<TencentOssConfigItem[]>([]);
const showDrawer = () => { const showDrawer = () => {
setOpen(true); setOpen(true);
}; };
@@ -104,14 +228,39 @@ const TencentSettings: React.FC = () => {
const onClose = () => { const onClose = () => {
setOpen(false); setOpen(false);
}; };
async function getAllConfigs() { async function getAllConfigs() {
getAllTencentOSsConfig(5).then((res: any) => { getAllTencentOSsConfig("1").then((res: any) => {
if (res && res.success && res.data) { if (res && res.success && res.data) {
setConfigs(res.data); setConfigs(res.data);
} }
}); });
} }
async function addTencentConfig() {
const fieldsValue = form.getFieldsValue();
const TencentOssConfig = {
userId: "1",
...fieldsValue,
};
addTencentOSSConfig(TencentOssConfig).then((res: any) => {
if (res && res.success) {
message.open({
content: "新增成功",
type: "success",
});
getAllConfigs().then(() => {
onClose();
});
} else {
message.open({
content: res.data,
type: "error",
});
}
});
}
useEffect(() => { useEffect(() => {
getAllConfigs().then(); getAllConfigs().then();
}, []); }, []);
@@ -131,12 +280,12 @@ const TencentSettings: React.FC = () => {
extra={ extra={
<Space> <Space>
<Button onClick={onClose}></Button> <Button onClick={onClose}></Button>
<Button onClick={onClose} type="primary"> <Button onClick={addTencentConfig} type="primary">
</Button> </Button>
</Space> </Space>
}> }>
<Form layout="vertical"> <Form layout="vertical" form={form}>
<Row gutter={16}> <Row gutter={16}>
<Col span={12}> <Col span={12}>
<Form.Item <Form.Item
@@ -155,6 +304,24 @@ const TencentSettings: React.FC = () => {
</Form.Item> </Form.Item>
</Col> </Col>
</Row> </Row>
<Row gutter={16}>
<Col span={12}>
<Form.Item
name="appId"
label="appId"
rules={[{ required: true, message: "请输入appID!" }]}>
<Input placeholder="请输入appID" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="region"
label="地区"
rules={[{ required: true, message: "请输入地区!" }]}>
<Input placeholder="请输入地区!" />
</Form.Item>
</Col>
</Row>
</Form> </Form>
</Drawer> </Drawer>
</> </>
@@ -170,6 +337,46 @@ const TencentSettings: React.FC = () => {
cardBordered={true} cardBordered={true}
editable={{ editable={{
type: "multiple", type: "multiple",
onSave: async (__: any, originRow: any, _: any) => {
const updateForm: any = {
id: originRow.id,
endpoint: originRow.endpoint,
secretId: originRow.secretId,
secretKey: originRow.secretKey,
appId: originRow.appId,
region: originRow.region,
};
updateTencentConfig(updateForm).then((res: any) => {
if (res && res.success) {
message.open({
content: "修改成功!",
type: "success",
});
getAllConfigs().then();
} else {
message.open({
content: res.data,
type: "error",
});
}
});
},
onDelete: async (row: any) => {
deleteConfig(row).then((res: any) => {
if (res && res.success) {
message.open({
content: "删除成功",
type: "success",
});
getAllConfigs().then();
} else {
message.open({
content: res.data,
type: "error",
});
}
});
},
}} }}
columnsState={{ columnsState={{
persistenceKey: "pro-table-singe-demos", persistenceKey: "pro-table-singe-demos",
@@ -206,6 +413,51 @@ const TencentSettings: React.FC = () => {
]} ]}
/> />
<AddTencentOssConfigDrawer /> <AddTencentOssConfigDrawer />
<Modal
title={<p></p>}
loading={loading}
footer={false}
open={openModal}
afterClose={() => {
setConfigDetail({});
}}
onCancel={() => setOpenModal(false)}>
<Flex vertical={false} align={"center"}>
<h4>ID:</h4> <span style={{ marginLeft: 10 }}>{configDetail.id}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>endpoint: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.endpoint}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>secretId: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.secretId} </span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>secretKey: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.secretKey}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>appId: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.appId}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>region: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.region}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>status: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.status}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>createdTime: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.createdTime}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>updateTime: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.updateTime}</span>
</Flex>
</Modal>
</div> </div>
</> </>
); );

View File

@@ -27,10 +27,13 @@ export default () => {
onSelect={(value: any) => { onSelect={(value: any) => {
navigate("/main/setting/" + value); navigate("/main/setting/" + value);
}} }}
placeholder={"请选择存储商"}> fieldNames={{
{selectOptions.map((storage: any, index: any) => { label: "name",
value: "value",
}}
labelRender={(label: any) => {
return ( return (
<Select.Option value={storage.value} key={index}> <>
<Card <Card
bordered={false} bordered={false}
style={{ style={{
@@ -40,19 +43,44 @@ export default () => {
flexDirection: "row", flexDirection: "row",
}} }}
size={"small"}> size={"small"}>
<Avatar src={StorageIcon[storage.value]} size={"small"} />{" "} <Avatar src={StorageIcon[label.value]} size={"small"} />
<span <span
style={{ style={{
marginLeft: "10px", marginLeft: "10px",
fontWeight: "bolder", fontWeight: "bolder",
}}> }}>
{storage.name} {label.label}
</span> </span>
</Card> </Card>
</Select.Option> </>
); );
})} }}
</Select> options={selectOptions}
optionRender={(item: any) => {
return (
<>
<Card
bordered={false}
style={{
height: 35,
display: "flex",
alignItems: "center",
flexDirection: "row",
}}
size={"small"}>
<Avatar src={StorageIcon[item.value]} size={"small"} />
<span
style={{
marginLeft: "10px",
fontWeight: "bolder",
}}>
{item.label}
</span>
</Card>
</>
);
}}
placeholder={"请选择存储商"}></Select>
</div> </div>
</ProCard> </ProCard>
<ProCard style={{ marginTop: 20, height: "100%" }} bordered boxShadow> <ProCard style={{ marginTop: 20, height: "100%" }} bordered boxShadow>

View File

@@ -6,18 +6,53 @@ import { useNavigate } from "react-router-dom";
import "aieditor/dist/style.css"; import "aieditor/dist/style.css";
import styles from "./index.module.less"; import styles from "./index.module.less";
import { ProCard } from "@ant-design/pro-components"; import { ProCard } from "@ant-design/pro-components";
import { Button, Card, Flex, Form, FormListFieldData, FormProps, Input, Select } from "antd"; import {
Button,
Card,
Flex,
Form,
FormListFieldData,
FormProps,
Input,
message,
Select,
} from "antd";
import { CloseOutlined, LeftOutlined, MinusCircleOutlined, PlusOutlined } from "@ant-design/icons"; import { CloseOutlined, LeftOutlined, MinusCircleOutlined, PlusOutlined } from "@ant-design/icons";
import selectOptions from "@/components/Main/Settings/settings.ts"; import selectOptions from "@/components/Main/Settings/settings.ts";
import { addShareDetail } from "@/api/share";
import useStore from "@/utils/store/useStore.tsx";
import { observer } from "mobx-react";
const ShareAdd: React.FunctionComponent = () => { const ShareAdd: React.FunctionComponent = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const divRef = useRef(null); const divRef = useRef(null);
const fromRef: any = useRef(); const [form] = Form.useForm();
const [isDisabled, setIsDisabled] = React.useState(false); const [isDisabled, setIsDisabled] = React.useState(false);
const store = useStore("share");
const onFinish: FormProps["onFinish"] = (values) => { const onFinish: FormProps["onFinish"] = (values) => {
console.log("Success:", values); const formData: any = {
circleId: store.getCircleId(),
userId: 32,
...values,
};
addShareDetail(formData).then((res: any) => {
if (res && res.success) {
message
.open({
content: "新增成功",
type: "success",
})
.then();
} else {
message
.open({
content: "新增失败",
type: "warning",
})
.then();
}
});
}; };
useEffect(() => { useEffect(() => {
if (divRef.current) { if (divRef.current) {
@@ -39,9 +74,7 @@ const ShareAdd: React.FunctionComponent = () => {
}, },
}, },
onChange: async (value: any) => { onChange: async (value: any) => {
fromRef.current.setFieldsValue({ form.setFieldValue("content" as any, value.getHtml());
content: value.getHtml(),
});
}, },
} as any); } as any);
return () => { return () => {
@@ -58,7 +91,7 @@ const ShareAdd: React.FunctionComponent = () => {
shape="circle" shape="circle"
icon={<LeftOutlined />} icon={<LeftOutlined />}
onClick={() => { onClick={() => {
navigate("/main/share/list/1"); navigate("/main/share/list/" + store.getCircleId());
}} }}
/> />
<Flex <Flex
@@ -71,7 +104,7 @@ const ShareAdd: React.FunctionComponent = () => {
</Flex> </Flex>
</ProCard> </ProCard>
<div className={styles.share_add_content}> <div className={styles.share_add_content}>
<Form onFinish={onFinish} autoComplete="off" ref={fromRef}> <Form onFinish={onFinish} autoComplete="off" form={form}>
<Form.Item <Form.Item
label={ label={
<> <>
@@ -111,6 +144,7 @@ const ShareAdd: React.FunctionComponent = () => {
<h4></h4> <h4></h4>
</> </>
} }
rules={[{ required: true, message: "请输入介绍" }]}
name="content" name="content"
id={"content"}> id={"content"}>
<div <div
@@ -126,7 +160,7 @@ const ShareAdd: React.FunctionComponent = () => {
if (!tags) { if (!tags) {
return Promise.reject(new Error("请至少填写一个标签")); return Promise.reject(new Error("请至少填写一个标签"));
} }
if (tags.length >= 3) { if (tags.length > 3) {
setIsDisabled(true); setIsDisabled(true);
return Promise.reject( return Promise.reject(
new Error("最多只能添加三个标签"), new Error("最多只能添加三个标签"),
@@ -148,7 +182,7 @@ const ShareAdd: React.FunctionComponent = () => {
id={"tags"}> id={"tags"}>
<Form.Item <Form.Item
validateTrigger={["onChange", "onBlur"]} validateTrigger={["onChange", "onBlur"]}
name={[field.name, "tag"]} name={[field.name, "tagName"] as any}
noStyle> noStyle>
<Input <Input
placeholder="请输入标签" placeholder="请输入标签"
@@ -209,7 +243,9 @@ const ShareAdd: React.FunctionComponent = () => {
}} }}
/> />
}> }>
<Form.Item name={[field.name, "type"]} label="分享类型"> <Form.Item
name={[field.name, "type"] as any}
label="分享类型">
<Select <Select
size="middle" size="middle"
style={{ width: "20%" }} style={{ width: "20%" }}
@@ -231,15 +267,17 @@ const ShareAdd: React.FunctionComponent = () => {
</Select> </Select>
</Form.Item> </Form.Item>
<Form.Item <Form.Item
name={[field.name, "description"]} name={[field.name, "description"] as any}
label="资源描述"> label="资源描述">
<Input name={"description"} /> <Input name={"description"} />
</Form.Item> </Form.Item>
<Form.Item name={[field.name, "url"]} label="资源链接"> <Form.Item
name={[field.name, "url"] as any}
label="资源链接">
<Input name={"url"} /> <Input name={"url"} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
name={[field.name, "password"]} name={[field.name, "password"] as any}
label={"提取密码"}> label={"提取密码"}>
<Input name={"password"} /> <Input name={"password"} />
</Form.Item> </Form.Item>
@@ -269,4 +307,4 @@ const ShareAdd: React.FunctionComponent = () => {
</> </>
); );
}; };
export default ShareAdd; export default observer(ShareAdd);

View File

@@ -1,6 +1,6 @@
/** @format */ /** @format */
import React from "react"; import React, { useEffect, useState } from "react";
import { Avatar, Button, Card, Divider, Flex, Tag, Tooltip } from "antd"; import { Avatar, Button, Card, Divider, Flex, Skeleton, Tag, Tooltip } from "antd";
import { import {
CommentOutlined, CommentOutlined,
ExportOutlined, ExportOutlined,
@@ -11,33 +11,54 @@ import {
TagsOutlined, TagsOutlined,
WarningOutlined, WarningOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
import { useNavigate } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
import "aieditor/dist/style.css"; import "aieditor/dist/style.css";
import { ProCard } from "@ant-design/pro-components"; import { ProCard } from "@ant-design/pro-components";
import styles from "./index.module.less"; import styles from "./index.module.less";
import logo from "@/assets/icons/baiduyun.svg";
import { Typography } from "antd"; import { Typography } from "antd";
import Comment from "@/components/Main/Share/components/ShareDetail/components/Comment"; import Comment from "@/components/Main/Share/components/ShareDetail/components/Comment";
import getRandomColor from "@/constant/random-color.ts"; import getRandomColor from "@/constant/random-color.ts";
const { Paragraph } = Typography; const { Paragraph } = Typography;
import like from "@/assets/icons/like.svg"; import like from "@/assets/icons/like.svg";
import favorite from "@/assets/icons/favorite.svg"; import favorite from "@/assets/icons/favorite.svg";
import useStore from "@/utils/store/useStore.tsx";
import { getShareDetail } from "@/api/share";
import StorageIcon from "@/constant/stroage-icon.ts";
import { observer } from "mobx-react";
const ShareDetail: React.FunctionComponent = () => { const ShareDetail: React.FunctionComponent = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const store = useStore("share");
const params = useParams();
const [detail, setDetail] = useState<any>({});
const [loading, setLoading] = useState(true);
async function getDetail() {
getShareDetail(params.id).then((res: any) => {
if (res && res.success && res.data) {
setDetail(res.data);
setLoading(false);
}
});
}
useEffect(() => {
getDetail().then();
}, []);
return ( return (
<> <>
<div> <Skeleton loading={loading} active={true} paragraph={{ rows: 16 }}>
<ProCard bordered={true}> <ProCard bordered={true}>
<Flex vertical={false} align={"center"} justify={"space-between"}> <Flex vertical={false} align={"center"} justify={"space-between"}>
<Button <Button
shape="circle" shape="circle"
icon={<LeftOutlined />} icon={<LeftOutlined />}
onClick={() => { onClick={() => {
navigate("/main/share/list/1"); const circleId = store.getCircleId();
navigate("/main/share/list/" + circleId);
}} }}
/> />
<Flex vertical={false} align={"center"}> <Flex vertical={false} align={"center"}>
<h3></h3> <h3>{detail.title as string}</h3>
</Flex> </Flex>
<Flex <Flex
vertical={false} vertical={false}
@@ -45,14 +66,14 @@ const ShareDetail: React.FunctionComponent = () => {
justify={"space-between"} justify={"space-between"}
style={{ width: "20%" }}> style={{ width: "20%" }}>
<Flex vertical={false} align={"center"}> <Flex vertical={false} align={"center"}>
<Avatar src={logo as any} size={"small"} /> <Avatar src={detail.avatar as any} size={"small"} />
<span <span
style={{ style={{
fontSize: 12, fontSize: 12,
color: "gray", color: "gray",
overflow: "hidden", overflow: "hidden",
}}> }}>
landaiqing {detail.nickname}
</span> </span>
</Flex> </Flex>
<Flex vertical={false} align={"center"}> <Flex vertical={false} align={"center"}>
@@ -62,7 +83,7 @@ const ShareDetail: React.FunctionComponent = () => {
fontSize: 12, fontSize: 12,
color: "gray", color: "gray",
}}> }}>
1024 {detail.likesCount}
</span> </span>
</Flex> </Flex>
<Flex vertical={false} align={"center"}> <Flex vertical={false} align={"center"}>
@@ -72,7 +93,7 @@ const ShareDetail: React.FunctionComponent = () => {
fontSize: 12, fontSize: 12,
color: "gray", color: "gray",
}}> }}>
1024 {detail.commentCount}
</span> </span>
</Flex> </Flex>
<Flex vertical={false} align={"center"}> <Flex vertical={false} align={"center"}>
@@ -82,7 +103,7 @@ const ShareDetail: React.FunctionComponent = () => {
fontSize: 12, fontSize: 12,
color: "gray", color: "gray",
}}> }}>
1024 {detail.views}
</span> </span>
</Flex> </Flex>
</Flex> </Flex>
@@ -90,7 +111,9 @@ const ShareDetail: React.FunctionComponent = () => {
</ProCard> </ProCard>
<div className={styles.share_detail_container}> <div className={styles.share_detail_container}>
<ProCard bordered={true}> <ProCard bordered={true}>
<div style={{ height: 500 }}></div> <div
style={{ height: 500 }}
dangerouslySetInnerHTML={{ __html: detail.content }}></div>
<Card style={{ borderRadius: "10px", borderColor: "#1677FF" }}> <Card style={{ borderRadius: "10px", borderColor: "#1677FF" }}>
<Flex vertical={false} align={"center"} justify={"space-between"}> <Flex vertical={false} align={"center"} justify={"space-between"}>
@@ -111,7 +134,7 @@ const ShareDetail: React.FunctionComponent = () => {
align={"center"} align={"center"}
style={{ marginTop: 10 }} style={{ marginTop: 10 }}
justify={"space-between"}> justify={"space-between"}>
<span style={{ fontSize: 16 }}>Windows DefenderRemover</span> <span style={{ fontSize: 16 }}>{detail.title}</span>
</Flex> </Flex>
<Flex <Flex
vertical={false} vertical={false}
@@ -131,15 +154,28 @@ const ShareDetail: React.FunctionComponent = () => {
vertical={false} vertical={false}
align={"center"} align={"center"}
style={{ marginTop: 10 }} style={{ marginTop: 10 }}
justify={"space-between"}> justify={"flex-start"}>
<Card hoverable={true} style={{ width: 280, height: 140 }}> {detail.urls &&
Array.from(detail.urls).map((url: any, index: number) => {
return (
<div key={index}>
<Card
hoverable={true}
style={{
width: 280,
height: 140,
marginLeft: 10,
}}>
<Flex vertical={true}> <Flex vertical={true}>
<Flex <Flex
vertical={false} vertical={false}
justify={"space-between"} justify={"space-between"}
align={"center"}> align={"center"}>
<div> <div>
<Avatar src={logo as any}></Avatar> <Avatar
src={
StorageIcon[url.type] as any
}></Avatar>
<span <span
style={{ style={{
marginLeft: 10, marginLeft: 10,
@@ -149,15 +185,24 @@ const ShareDetail: React.FunctionComponent = () => {
</span> </span>
</div> </div>
<ExportOutlined className={styles.link_btn} /> <ExportOutlined
className={styles.link_btn}
onClick={() => {
window.open(url.url, "_blank");
}}
/>
</Flex> </Flex>
<Divider></Divider> <Divider></Divider>
<Flex vertical={false}> <Flex vertical={false}>
<Tooltip <Tooltip
title="DefenderRemove111111111" title={url.description}
placement={"bottom"}> placement={"bottom"}>
<span style={{ width: 150, overflowX: "hidden" }}> <span
DefenderRemove111111111 style={{
width: 150,
overflowX: "hidden",
}}>
{url.description}
</span> </span>
</Tooltip> </Tooltip>
<span <span
@@ -167,12 +212,19 @@ const ShareDetail: React.FunctionComponent = () => {
display: "flex", display: "flex",
flexDirection: "row", flexDirection: "row",
}}> }}>
<span style={{ color: "#1677FF" }}></span>{" "} <span style={{ color: "#1677FF" }}>
<Paragraph copyable>12345</Paragraph>
</span>{" "}
<Paragraph copyable>
{url.password}
</Paragraph>
</span> </span>
</Flex> </Flex>
</Flex> </Flex>
</Card> </Card>
</div>
);
})}
</Flex> </Flex>
<Flex vertical={false} style={{ marginTop: 10 }}> <Flex vertical={false} style={{ marginTop: 10 }}>
<span style={{ color: "grey" }}> <span style={{ color: "grey" }}>
@@ -185,19 +237,27 @@ const ShareDetail: React.FunctionComponent = () => {
<Flex vertical={false} align={"center"} style={{ marginTop: 20 }}> <Flex vertical={false} align={"center"} style={{ marginTop: 20 }}>
<TagsOutlined style={{ fontSize: 30, color: "#1677FF" }} /> <TagsOutlined style={{ fontSize: 30, color: "#1677FF" }} />
<Flex vertical={false} align={"center"} style={{ marginLeft: 10 }}> <Flex vertical={false} align={"center"} style={{ marginLeft: 10 }}>
{detail.tags &&
Array.from(detail.tags).map((tag: any, index: number) => {
return (
<div key={index}>
<Tag bordered={false} color={getRandomColor()}> <Tag bordered={false} color={getRandomColor()}>
{tag.tagName}
</Tag>
<Tag bordered={false} color={getRandomColor()}>
</Tag>
<Tag bordered={false} color={getRandomColor()}>
</Tag> </Tag>
</div>
);
})}
</Flex> </Flex>
</Flex> </Flex>
<Flex vertical={false} align={"center"} justify={"center"} style={{height: 50}}> <Flex
<Avatar className={styles.like_icon} src={like as any} size={"large"}></Avatar> vertical={false}
align={"center"}
justify={"center"}
style={{ height: 50 }}>
<Avatar
className={styles.like_icon}
src={like as any}
size={"large"}></Avatar>
<Avatar <Avatar
className={styles.favtorie_icon} className={styles.favtorie_icon}
src={favorite as any} src={favorite as any}
@@ -206,8 +266,8 @@ const ShareDetail: React.FunctionComponent = () => {
</ProCard> </ProCard>
</div> </div>
<Comment /> <Comment />
</div> </Skeleton>
</> </>
); );
}; };
export default ShareDetail; export default observer(ShareDetail);

View File

@@ -1,6 +1,6 @@
/** @format */ /** @format */
import { ProCard } from "@ant-design/pro-components"; import { ProCard } from "@ant-design/pro-components";
import { Avatar, Button, Flex, Input, Segmented, Tag } from "antd"; import { Avatar, Button, Empty, Flex, Input, message, Segmented, Skeleton, Tag } from "antd";
import logo from "@/assets/icons/gitee.svg"; import logo from "@/assets/icons/gitee.svg";
import { import {
BarsOutlined, BarsOutlined,
@@ -13,15 +13,88 @@ import {
SendOutlined, SendOutlined,
SmileOutlined, SmileOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
import { useState } from "react"; import { useEffect, useState } from "react";
import styles from "./index.module.less"; import styles from "./index.module.less";
import { useParams } from "react-router-dom";
import { addComment, addReply, listComment, listReply } from "@/api/share";
const Comment = () => { const Comment = () => {
const [isReply, setIsReply] = useState<boolean>(false); const params = useParams();
const [isReplyComment, setIsReplyComment] = useState<boolean>(false); const [isReply, setIsReply] = useState<any>(null);
const [isReplyReply, setIsReplyReply] = useState<any>(null);
const [loading, setLoading] = useState<boolean>(true);
const [isReplyComment, setIsReplyComment] = useState<any>(null);
const [comment, setComment] = useState<any[]>([]);
const [reply, setReply] = useState<any[]>([]);
const [commentData, setCommentData] = useState<any>("");
const [replyData, setReplyData] = useState<any>("");
const [replyReplyData, setReplyReplyData] = useState<any>("");
async function listComments() {
listComment(params.id).then((res: any) => {
console.log(res);
if (res && res.success && res.data) {
setComment(res.data);
setLoading(false);
}
});
}
async function addComments(data: any) {
addComment(data).then((res: any) => {
if (res && res.success && res.data) {
message.open({
content: "评论成功",
type: "success",
});
listComments().then();
} else {
message.open({
content: "评论失败",
type: "error",
});
}
});
}
async function replyComment(commentId: any) {
listReply(commentId).then((res: any) => {
console.log(res);
if (res && res.success && res.data) {
setReply(res.data);
}
});
}
async function addReplies(data: any) {
addReply(data).then((res: any) => {
if (res && res.success && res.data) {
message.open({
content: "回复成功",
type: "success",
});
listComments().then();
} else {
message.open({
content: "回复失败",
type: "error",
});
}
});
}
const handleExpandClick = (index: any) => {
setIsReply(isReply === index ? null : index);
};
const handleExpandReplyReplyClick = (index: any) => {
setIsReplyReply(isReplyReply === index ? null : index);
};
const handleExpandReplyCommentClick = (index: any) => {
setIsReplyComment(isReplyComment === index ? null : index);
};
useEffect(() => {
listComments().then();
}, []);
return ( return (
<> <>
<div> <div>
<ProCard title={"评论 5"}> <ProCard title={"评论"}>
<Skeleton active={true} loading={loading} paragraph={{ rows: 2 }}>
<Flex vertical={false}> <Flex vertical={false}>
<Flex vertical={true} justify={"flex-start"}> <Flex vertical={true} justify={"flex-start"}>
<Avatar src={logo as any} size={"large"} /> <Avatar src={logo as any} size={"large"} />
@@ -31,6 +104,9 @@ const Comment = () => {
style={{ marginLeft: 10 }} style={{ marginLeft: 10 }}
rows={5} rows={5}
maxLength={500} maxLength={500}
onChange={(e: any) => {
setCommentData(e.target.value);
}}
showCount showCount
placeholder={"平等表达,友善交流"}></Input.TextArea> placeholder={"平等表达,友善交流"}></Input.TextArea>
<Flex <Flex
@@ -43,31 +119,75 @@ const Comment = () => {
/> />
</Flex> </Flex>
<Flex vertical={false} align={"center"} justify={"flex-end"}> <Flex vertical={false} align={"center"} justify={"flex-end"}>
<Button icon={<SendOutlined />} type={"primary"}> <Button
icon={<SendOutlined />}
type={"primary"}
onClick={() => {
const data: any = {
userId: 17,
detailId: params.id,
content: commentData,
};
addComments(data).then(() => {
listComments().then();
});
}}>
</Button> </Button>
</Flex> </Flex>
</Flex> </Flex>
</Flex> </Flex>
<Flex vertical={true}> <Flex vertical={true}>
<Segmented <Segmented
block={false} block={false}
style={{ width: 145 }} style={{ width: 145 }}
size={"middle"} size={"middle"}
options={[ options={[
{ label: "最新", value: "List", icon: <BarsOutlined /> }, {
{ label: "最热", value: "Kanban", icon: <FireOutlined /> }, label: "最新",
value: "List",
icon: <BarsOutlined />,
},
{
label: "最热",
value: "Kanban",
icon: <FireOutlined />,
},
]} ]}
/> />
<Flex vertical={true} style={{ marginTop: 20, width: "100%" }}> {comment.length === 0 ? (
<Empty description={"暂无评论!"} />
) : (
<>
{comment &&
Array.from(comment).map((item: any, index: number) => {
return (
<Flex
key={index}
vertical={true}
style={{ marginTop: 20, width: "100%" }}>
<Flex vertical={false}> <Flex vertical={false}>
<Flex vertical={true} justify={"flex-start"}> <Flex
<Avatar src={logo as any} size={"large"}></Avatar> vertical={true}
justify={"flex-start"}>
<Avatar
src={item.avatar as any}
size={"large"}></Avatar>
</Flex> </Flex>
<Flex vertical={true} style={{ marginLeft: 15, width: "100%" }}> <Flex
vertical={true}
style={{
marginLeft: 15,
width: "100%",
}}>
<Flex vertical={false} align={"center"}> <Flex vertical={false} align={"center"}>
<span style={{ fontSize: 16, fontWeight: 500 }}> <span
landaiqing style={{
fontSize: 16,
fontWeight: 500,
}}>
{item.nick}
</span> </span>
<Tag <Tag
bordered={false} bordered={false}
@@ -82,193 +202,101 @@ const Comment = () => {
boxShadow={false} boxShadow={false}
extra={<></>} extra={<></>}
style={{ width: "100%" }}> style={{ width: "100%" }}>
<span> <span>{item.content}</span>
i回复OIDHJHDCEHIceh车hi是事实上事实是事实hi是是是是是是是是是是
</span>
<Flex
vertical={false}
style={{ marginTop: 10, width: "100%" }}>
<span style={{ fontSize: 13, color: "grey" }}>
1
</span>
<Flex vertical={false} align={"center"}>
<LikeOutlined className={styles.like_icon} />
<span style={{ fontSize: 13, color: "grey" }}>
10
</span>
</Flex>
<Flex vertical={false} align={"center"}>
<CommentOutlined className={styles.comment_icon} />
<span style={{ fontSize: 13, color: "grey" }}>
10
</span>
</Flex>
<Flex vertical={false} align={"center"}>
<Button
type="text"
size={"small"}
onClick={() => setIsReply(true)}
style={{ fontSize: 13, color: "grey" }}>
</Button>
</Flex>
</Flex>
</ProCard>
{isReply && (
<>
<Flex vertical={true}>
<Flex vertical={false} align={"center"}>
<span style={{ fontSize: 13 }}> </span>
<span style={{ fontSize: 13 }}>
{" "}
landaiqing
</span>
<Button
icon={<CloseOutlined />}
style={{ fontSize: 13, marginLeft: 5 }}
type={"dashed"}
onClick={() => setIsReply(false)}
size={"small"}>
{" "}
</Button>
</Flex>
<Flex vertical={false} style={{ marginTop: 5 }}>
<Flex vertical={true} justify={"flex-start"}>
<Avatar
src={logo as any}
size={"default"}
/>
</Flex>
<Flex vertical={true} style={{ width: "100%" }}>
<Input.TextArea
style={{
marginLeft: 10,
width: "100%",
}}
rows={3}
maxLength={500}
showCount
placeholder={
"平等表达,友善交流"
}></Input.TextArea>
<Flex <Flex
vertical={false} vertical={false}
align={"center"} align={"center"}
style={{ style={{
marginTop: 10, marginTop: 10,
marginLeft: 10, width: "100%",
}}> }}>
<SmileOutlined <span
style={{ style={{
fontSize: 20, fontSize: 13,
color: "grey", color: "grey",
}} }}>
/> {item.createdTime}
<PictureOutlined
style={{
marginLeft: 20,
fontSize: 20,
color: "grey",
}}
/>
</Flex>
<Flex
vertical={false}
align={"center"}
justify={"flex-end"}>
<Button
icon={<SendOutlined />}
type={"primary"}>
</Button>
</Flex>
</Flex>
</Flex>
</Flex>
</>
)}
<Flex vertical={false}>
<Flex vertical={true} justify={"flex-start"}>
<Avatar src={logo as any} size={"default"}></Avatar>
</Flex>
<Flex
vertical={true}
style={{ marginLeft: 15, width: "100%" }}>
<Flex vertical={false} align={"center"}>
<span style={{ fontSize: 14, fontWeight: 500 }}>
sjm landaiqing
</span>
{/*<Tag*/}
{/* bordered={false}*/}
{/* icon={<CrownOutlined />}*/}
{/* color={"cyan"}*/}
{/* style={{ marginLeft: 5 }}>*/}
{/* 作者*/}
{/*</Tag>*/}
</Flex>
<ProCard
bordered={true}
boxShadow={false}
extra={<></>}
style={{ width: "100%" }}>
<span>
i回复OIDHJHDCEHIceh车hi是事实上事实是事实hi是是是是是是是是是是
</span> </span>
<Flex <Flex
vertical={false} vertical={false}
style={{ marginTop: 10, width: "100%" }}> align={"center"}>
<span style={{ fontSize: 13, color: "grey" }}>
1
</span>
<Flex vertical={false} align={"center"}>
<LikeOutlined <LikeOutlined
className={
styles.like_icon
}
/>
<span
style={{ style={{
fontSize: 13, fontSize: 13,
color: "grey", color: "grey",
marginLeft: 10, }}>
}} {item.likes}
/>
<span
style={{ fontSize: 13, color: "grey" }}>
10
</span> </span>
</Flex> </Flex>
<Flex vertical={false} align={"center"}> <Flex
vertical={false}
align={"center"}>
<CommentOutlined <CommentOutlined
onClick={() => {
setReply([]);
if (item.id) {
replyComment(
item.id,
).then(() => {
handleExpandReplyCommentClick(
index,
);
});
}
}}
className={
styles.comment_icon
}
/>
<span
style={{ style={{
fontSize: 13, fontSize: 13,
color: "grey", color: "grey",
marginLeft: 10, }}>
}} {item.replyCount}
/>
<span
style={{ fontSize: 13, color: "grey" }}>
10
</span> </span>
</Flex> </Flex>
<Flex vertical={false} align={"center"}> <Flex
vertical={false}
align={"center"}>
<Button <Button
type="text" type="text"
size={"small"} size={"small"}
onClick={() => setIsReplyComment(true)} onClick={() => {
style={{ fontSize: 13, color: "grey" }}> handleExpandClick(
index,
);
}}
style={{
fontSize: 13,
color: "grey",
}}>
</Button> </Button>
</Flex> </Flex>
</Flex> </Flex>
</ProCard> </ProCard>
{isReplyComment && ( {isReply === index && (
<>
<Flex vertical={true}> <Flex vertical={true}>
<Flex vertical={false} align={"center"}> <Flex
<span style={{ fontSize: 13 }}> vertical={false}
{" "} align={"center"}>
{" "} <span
style={{
fontSize: 13,
}}>
</span> </span>
<span style={{ fontSize: 13 }}> <span
{" "} style={{
landaiqing fontSize: 13,
}}>
{item.nick}
</span> </span>
<Button <Button
icon={<CloseOutlined />} icon={<CloseOutlined />}
@@ -277,11 +305,10 @@ const Comment = () => {
marginLeft: 5, marginLeft: 5,
}} }}
type={"dashed"} type={"dashed"}
onClick={() => onClick={() => {
setIsReplyComment(false) setIsReply(null);
} }}
size={"small"}> size={"small"}>
{" "}
</Button> </Button>
</Flex> </Flex>
@@ -298,7 +325,9 @@ const Comment = () => {
</Flex> </Flex>
<Flex <Flex
vertical={true} vertical={true}
style={{ width: "100%" }}> style={{
width: "100%",
}}>
<Input.TextArea <Input.TextArea
style={{ style={{
marginLeft: 10, marginLeft: 10,
@@ -307,6 +336,12 @@ const Comment = () => {
rows={3} rows={3}
maxLength={500} maxLength={500}
showCount showCount
onChange={(e) => {
setReplyData(
e.target
.value,
);
}}
placeholder={ placeholder={
"平等表达,友善交流" "平等表达,友善交流"
}></Input.TextArea> }></Input.TextArea>
@@ -334,30 +369,88 @@ const Comment = () => {
<Flex <Flex
vertical={false} vertical={false}
align={"center"} align={"center"}
justify={"flex-end"}> justify={
"flex-end"
}>
<Button <Button
icon={<SendOutlined />} onClick={() => {
type={"primary"}> const data: any =
{
userId: 32,
detailId:
params.id,
content:
replyData,
toId: item.id,
replyId:
item.id,
};
addReplies(
data,
).then();
}}
icon={
<SendOutlined />
}
type={
"primary"
}>
</Button> </Button>
</Flex> </Flex>
</Flex> </Flex>
</Flex> </Flex>
</Flex> </Flex>
</>
)} )}
</Flex> {isReplyComment === index &&
</Flex> reply &&
<Flex vertical={false}> Array.from(reply).map(
<Flex vertical={true} justify={"flex-start"}> (
<Avatar src={logo as any} size={"default"}></Avatar> replyItem: any,
replyIndex: number,
) => {
return (
<Flex
key={replyIndex}
vertical={false}>
<Flex
vertical={true}
justify={
"flex-start"
}>
<Avatar
src={
replyItem.avatar as any
}
size={
"default"
}></Avatar>
</Flex> </Flex>
<Flex <Flex
vertical={true} vertical={true}
style={{ marginLeft: 15, width: "100%" }}> style={{
<Flex vertical={false} align={"center"}> marginLeft: 15,
<span style={{ fontSize: 14, fontWeight: 500 }}> width: "100%",
sjm landaiqing }}>
<Flex
vertical={
false
}
align={
"center"
}>
<span
style={{
fontSize: 14,
fontWeight: 500,
}}>
{
replyItem.nick
}{" "}
{
replyItem.nickto
}
</span> </span>
{/*<Tag*/} {/*<Tag*/}
{/* bordered={false}*/} {/* bordered={false}*/}
@@ -368,20 +461,47 @@ const Comment = () => {
{/*</Tag>*/} {/*</Tag>*/}
</Flex> </Flex>
<ProCard <ProCard
bordered={true} bordered={
boxShadow={false} true
extra={<></>} }
style={{ width: "100%" }}> boxShadow={
false
}
extra={
<></>
}
style={{
width: "100%",
}}>
<span> <span>
i回复OIDHJHDCEHIceh车hi是事实上事实是事实hi是是是是是是是是是是 {
replyItem.content
}
</span> </span>
<Flex <Flex
vertical={false} vertical={
style={{ marginTop: 10, width: "100%" }}> false
<span style={{ fontSize: 13, color: "grey" }}> }
1 style={{
marginTop: 10,
width: "100%",
}}>
<span
style={{
fontSize: 13,
color: "grey",
}}>
{
replyItem.createdTime
}
</span> </span>
<Flex vertical={false} align={"center"}> <Flex
vertical={
false
}
align={
"center"
}>
<LikeOutlined <LikeOutlined
style={{ style={{
fontSize: 13, fontSize: 13,
@@ -390,89 +510,152 @@ const Comment = () => {
}} }}
/> />
<span <span
style={{ fontSize: 13, color: "grey" }}>
10
</span>
</Flex>
<Flex vertical={false} align={"center"}>
<CommentOutlined
style={{ style={{
fontSize: 13, fontSize: 13,
color: "grey", color: "grey",
marginLeft: 10, }}>
}} {
/> replyItem.likes
<span }
style={{ fontSize: 13, color: "grey" }}>
10
</span> </span>
</Flex> </Flex>
<Flex vertical={false} align={"center"}> <Flex
vertical={
false
}
align={
"center"
}>
<Button <Button
type="text" type="text"
size={"small"} size={
onClick={() => setIsReplyComment(true)} "small"
style={{ fontSize: 13, color: "grey" }}> }
onClick={() => {
handleExpandReplyReplyClick(
replyItem.id,
);
}}
style={{
fontSize: 13,
color: "grey",
}}>
</Button> </Button>
</Flex> </Flex>
</Flex> </Flex>
</ProCard> </ProCard>
{isReplyComment && ( {isReplyReply ===
replyItem.id && (
<> <>
<Flex vertical={true}> <Flex
<Flex vertical={false} align={"center"}> vertical={
<span style={{ fontSize: 13 }}> true
{" "} }>
{" "} <Flex
vertical={
false
}
align={
"center"
}>
<span
style={{
fontSize: 13,
}}>
</span> </span>
<span style={{ fontSize: 13 }}> <span
{" "} style={{
landaiqing fontSize: 13,
}}>
{
replyItem.nick
}
</span> </span>
<Button <Button
icon={<CloseOutlined />} icon={
<CloseOutlined />
}
style={{ style={{
fontSize: 13, fontSize: 13,
marginLeft: 5, marginLeft: 5,
}} }}
type={"dashed"} type={
onClick={() => "dashed"
setIsReplyComment(false)
} }
size={"small"}> onClick={() => {
{" "} setIsReplyReply(
null,
);
}}
size={
"small"
}>
</Button> </Button>
</Flex> </Flex>
<Flex <Flex
vertical={false} vertical={
style={{ marginTop: 5 }}> false
}
style={{
marginTop: 5,
}}>
<Flex <Flex
vertical={true} vertical={
justify={"flex-start"}> true
}
justify={
"flex-start"
}>
<Avatar <Avatar
src={logo as any} src={
size={"default"} logo as any
}
size={
"default"
}
/> />
</Flex> </Flex>
<Flex <Flex
vertical={true} vertical={
style={{ width: "100%" }}> true
}
style={{
width: "100%",
}}>
<Input.TextArea <Input.TextArea
style={{ style={{
marginLeft: 10, marginLeft: 10,
width: "100%", width: "100%",
}} }}
rows={3} rows={
maxLength={500} 3
}
onChange={(
e: any,
) => {
setReplyReplyData(
e
.target
.value,
);
}}
maxLength={
500
}
showCount showCount
placeholder={ placeholder={
"平等表达,友善交流" "平等表达,友善交流"
}></Input.TextArea> }></Input.TextArea>
<Flex <Flex
vertical={false} vertical={
align={"center"} false
}
align={
"center"
}
style={{ style={{
marginTop: 10, marginTop: 10,
marginLeft: 10, marginLeft: 10,
@@ -492,12 +675,38 @@ const Comment = () => {
/> />
</Flex> </Flex>
<Flex <Flex
vertical={false} vertical={
align={"center"} false
justify={"flex-end"}> }
align={
"center"
}
justify={
"flex-end"
}>
<Button <Button
icon={<SendOutlined />} onClick={() => {
type={"primary"}> const data: any =
{
userId: 32,
detailId:
params.id,
content:
replyReplyData,
toId: item.id,
replyId:
replyItem.id,
};
addReplies(
data,
).then();
}}
icon={
<SendOutlined />
}
type={
"primary"
}>
</Button> </Button>
</Flex> </Flex>
@@ -508,10 +717,18 @@ const Comment = () => {
)} )}
</Flex> </Flex>
</Flex> </Flex>
);
},
)}
</Flex> </Flex>
</Flex> </Flex>
</Flex> </Flex>
);
})}
</>
)}
</Flex> </Flex>
</Skeleton>
</ProCard> </ProCard>
</div> </div>
</> </>

View File

@@ -1,11 +1,10 @@
/** @format */ /** @format */
import { ProCard } from "@ant-design/pro-components"; import { ProCard } from "@ant-design/pro-components";
import { Avatar, Button, Divider, Flex, Input, List, Skeleton, Tag } from "antd"; import { Avatar, Button, Flex, Input, List, Skeleton, Tag } from "antd";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import styles from "./index.module.less"; import styles from "./index.module.less";
import InfiniteScroll from "react-infinite-scroll-component"; import { Link, useNavigate, useParams } from "react-router-dom";
import { useNavigate } from "react-router-dom";
import { import {
CommentOutlined, CommentOutlined,
EyeOutlined, EyeOutlined,
@@ -13,7 +12,10 @@ import {
LeftOutlined, LeftOutlined,
ShareAltOutlined, ShareAltOutlined,
} from "@ant-design/icons"; } from "@ant-design/icons";
import logo from "@/assets/icons/aliyun.svg"; import { shareDetailList } from "@/api/share";
import getRandomColor from "@/constant/random-color.ts";
import useStore from "@/utils/store/useStore.tsx";
import { observer } from "mobx-react";
interface DataType { interface DataType {
gender: string; gender: string;
name: { name: {
@@ -30,28 +32,24 @@ interface DataType {
nat: string; nat: string;
} }
export default () => { export default observer(() => {
const navigate = useNavigate(); const navigate = useNavigate();
const [loading, setLoading] = useState(false); const params = useParams();
const [loading, setLoading] = useState(true);
const [data, setData] = useState<DataType[]>([]); const [data, setData] = useState<DataType[]>([]);
const loadMoreData = () => { const store = useStore("share");
if (loading) { async function getShareDetailList() {
return; store.setCircleId(params.id as string);
shareDetailList(params.id).then((res: any) => {
if (res && res.data && res.data) {
setData(res.data);
setLoading(false);
} }
setLoading(true);
fetch("https://randomuser.me/api/?results=10&inc=name,gender,email,nat,picture&noinfo")
.then((res) => res.json())
.then((body) => {
setData([...data, ...body.results]);
setLoading(false);
})
.catch(() => {
setLoading(false);
}); });
}; }
useEffect(() => { useEffect(() => {
loadMoreData(); getShareDetailList().then();
}, []); }, []);
return ( return (
<> <>
@@ -83,13 +81,7 @@ export default () => {
</div> </div>
</ProCard> </ProCard>
<ProCard bordered={false} boxShadow={false}> <ProCard bordered={false} boxShadow={false}>
<InfiniteScroll <Skeleton loading={loading} active={true} paragraph={{ rows: 14 }}>
dataLength={data.length}
next={loadMoreData}
hasMore={data.length < 50}
loader={<Skeleton avatar paragraph={{ rows: 1 }} active />}
endMessage={<Divider plain>It is all, nothing more 🤐</Divider>}
scrollableTarget="scrollableDiv">
<List <List
dataSource={data} dataSource={data}
header={ header={
@@ -97,25 +89,34 @@ export default () => {
<h4></h4> <h4></h4>
</> </>
} }
renderItem={(item) => ( renderItem={(item: any) => (
<List.Item key={item.email}> <List.Item key={item.id}>
<List.Item.Meta <List.Item.Meta
avatar={<Avatar src={item.picture.large} />} avatar={<Avatar src={item.icon} />}
title={ title={
<> <Flex vertical={false} align={"center"}>
<a <Link to={"/main/share/detail/" + item.id}>
onClick={() => { {item.title}
navigate("/main/share/detail/1"); </Link>
}}> {item.tags &&
{item.name.last} Array.from(item.tags).map(
</a> (tag: any, index: number) => {
return (
<Flex
vertical={false}
align={"center"}
key={index}>
<Tag <Tag
bordered={false} bordered={false}
color="processing" color={getRandomColor()}
style={{ marginLeft: 10 }}> style={{ marginLeft: 10 }}>
IDM {tag.tagName}
</Tag> </Tag>
</> </Flex>
);
},
)}
</Flex>
} }
description={ description={
<> <>
@@ -123,7 +124,7 @@ export default () => {
vertical={false} vertical={false}
justify={"space-between"} justify={"space-between"}
align={"center"}> align={"center"}>
{item.email} {item.description}
<Flex <Flex
vertical={false} vertical={false}
align={"center"} align={"center"}
@@ -131,7 +132,7 @@ export default () => {
style={{ width: "300px" }}> style={{ width: "300px" }}>
<Flex vertical={false} align={"center"}> <Flex vertical={false} align={"center"}>
<Avatar <Avatar
src={logo as any} src={item.avatar as any}
size={"small"} size={"small"}
/> />
<span <span
@@ -140,7 +141,7 @@ export default () => {
color: "gray", color: "gray",
overflow: "hidden", overflow: "hidden",
}}> }}>
landaiqing {item.nickname}
</span> </span>
</Flex> </Flex>
<Flex vertical={false} align={"center"}> <Flex vertical={false} align={"center"}>
@@ -150,7 +151,7 @@ export default () => {
fontSize: 12, fontSize: 12,
color: "gray", color: "gray",
}}> }}>
1024 {item.likesCount}
</span> </span>
</Flex> </Flex>
<Flex vertical={false} align={"center"}> <Flex vertical={false} align={"center"}>
@@ -160,7 +161,7 @@ export default () => {
fontSize: 12, fontSize: 12,
color: "gray", color: "gray",
}}> }}>
1024 {item.commentCount}
</span> </span>
</Flex> </Flex>
<Flex vertical={false} align={"center"}> <Flex vertical={false} align={"center"}>
@@ -172,7 +173,7 @@ export default () => {
fontSize: 12, fontSize: 12,
color: "gray", color: "gray",
}}> }}>
1024 {item.views}
</span> </span>
</Flex> </Flex>
</Flex> </Flex>
@@ -180,13 +181,12 @@ export default () => {
</> </>
} }
/> />
{/*<div>Content</div>*/}
</List.Item> </List.Item>
)} )}
/> />
</InfiniteScroll> </Skeleton>
</ProCard> </ProCard>
</div> </div>
</> </>
); );
}; });

View File

@@ -1,22 +1,48 @@
/** @format */ /** @format */
import { FunctionComponent, useState } from "react"; import { FunctionComponent, useEffect, useState } from "react";
import { ProCard } from "@ant-design/pro-components"; import { ProCard } from "@ant-design/pro-components";
import styles from "./index.module.less"; import styles from "./index.module.less";
import { Avatar, Button, Card, Drawer, Flex, FloatButton, Form, Image, Input, Space } from "antd"; import {
Avatar,
Button,
Card,
Drawer,
Flex,
FloatButton,
Form,
Image,
Input,
message,
Skeleton,
Tooltip,
} from "antd";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import TextArea from "antd/es/input/TextArea"; import TextArea from "antd/es/input/TextArea";
import { EyeOutlined, PlusOutlined, UnorderedListOutlined } from "@ant-design/icons"; import { EyeOutlined, PlusOutlined, UnorderedListOutlined } from "@ant-design/icons";
import pic from "@/assets/images/background.png";
import Meta from "antd/es/card/Meta"; import Meta from "antd/es/card/Meta";
import { addShareCircle, getShareCircleList } from "@/api/share";
const MainShare: FunctionComponent = () => { const MainShare: FunctionComponent = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [loading, setLoading] = useState(true);
const [circleList, setCircleList] = useState<any[]>([]);
const [form] = Form.useForm();
const onClose = () => { const onClose = () => {
setOpen(false); setOpen(false);
}; };
async function getShareCircles() {
getShareCircleList().then((res: any) => {
if (res && res.success && res.data) {
setCircleList(res.data);
setLoading(false);
}
});
}
useEffect(() => {
getShareCircles().then();
}, []);
return ( return (
<> <>
<div className={styles.share_main}> <div className={styles.share_main}>
@@ -30,53 +56,68 @@ const MainShare: FunctionComponent = () => {
</div> </div>
</ProCard> </ProCard>
<ProCard title={<h3></h3>} bordered={false} boxShadow={false}> <ProCard title={<h3></h3>} bordered={false} boxShadow={false}>
<Flex vertical={false} align={"center"}> <Flex vertical={false} align={"center"} wrap={true} justify={"flex-start"}>
<Skeleton active={true} loading={loading} paragraph={{ rows: 10 }}>
{circleList &&
circleList.map((item: any, index: number) => {
return (
<div key={index}>
<Card <Card
hoverable hoverable
style={{ style={{
width: "250px", width: "250px",
boxShadow: " 0 0 10px rgba(0, 0, 0, 0.1)", boxShadow: " 0 0 10px rgba(0, 0, 0, 0.1)",
borderRadius: 20, borderRadius: 20,
marginLeft: 30,
marginTop: 20,
// backgroundColor: "rgba(79,68,68,0.11)", // backgroundColor: "rgba(79,68,68,0.11)",
}} }}
onClick={() => { onClick={() => {
navigate("/main/share/list/1"); navigate("/main/share/list/" + item.id);
}} }}
cover={ cover={
<Image <Image
alt="example" alt="example"
src={pic as any} src={item.icon}
style={{ style={{
height: 180, height: 180,
borderTopLeftRadius: 20, borderTopLeftRadius: 20,
borderTopRightRadius: 20, borderTopRightRadius: 20,
backgroundSize: "cover",
}} }}
preview={false} preview={false}
width={"100%"} width={"100%"}
height={"100%"}
fallback="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMIAAADDCAYAAADQvc6UAAABRWlDQ1BJQ0MgUHJvZmlsZQAAKJFjYGASSSwoyGFhYGDIzSspCnJ3UoiIjFJgf8LAwSDCIMogwMCcmFxc4BgQ4ANUwgCjUcG3awyMIPqyLsis7PPOq3QdDFcvjV3jOD1boQVTPQrgSkktTgbSf4A4LbmgqISBgTEFyFYuLykAsTuAbJEioKOA7DkgdjqEvQHEToKwj4DVhAQ5A9k3gGyB5IxEoBmML4BsnSQk8XQkNtReEOBxcfXxUQg1Mjc0dyHgXNJBSWpFCYh2zi+oLMpMzyhRcASGUqqCZ16yno6CkYGRAQMDKMwhqj/fAIcloxgHQqxAjIHBEugw5sUIsSQpBobtQPdLciLEVJYzMPBHMDBsayhILEqEO4DxG0txmrERhM29nYGBddr//5/DGRjYNRkY/l7////39v///y4Dmn+LgeHANwDrkl1AuO+pmgAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAwqADAAQAAAABAAAAwwAAAAD9b/HnAAAHlklEQVR4Ae3dP3PTWBSGcbGzM6GCKqlIBRV0dHRJFarQ0eUT8LH4BnRU0NHR0UEFVdIlFRV7TzRksomPY8uykTk/zewQfKw/9znv4yvJynLv4uLiV2dBoDiBf4qP3/ARuCRABEFAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghgg0Aj8i0JO4OzsrPv69Wv+hi2qPHr0qNvf39+iI97soRIh4f3z58/u7du3SXX7Xt7Z2enevHmzfQe+oSN2apSAPj09TSrb+XKI/f379+08+A0cNRE2ANkupk+ACNPvkSPcAAEibACyXUyfABGm3yNHuAECRNgAZLuYPgEirKlHu7u7XdyytGwHAd8jjNyng4OD7vnz51dbPT8/7z58+NB9+/bt6jU/TI+AGWHEnrx48eJ/EsSmHzx40L18+fLyzxF3ZVMjEyDCiEDjMYZZS5wiPXnyZFbJaxMhQIQRGzHvWR7XCyOCXsOmiDAi1HmPMMQjDpbpEiDCiL358eNHurW/5SnWdIBbXiDCiA38/Pnzrce2YyZ4//59F3ePLNMl4PbpiL2J0L979+7yDtHDhw8vtzzvdGnEXdvUigSIsCLAWavHp/+qM0BcXMd/q25n1vF57TYBp0a3mUzilePj4+7k5KSLb6gt6ydAhPUzXnoPR0dHl79WGTNCfBnn1uvSCJdegQhLI1vvCk+fPu2ePXt2tZOYEV6/fn31dz+shwAR1sP1cqvLntbEN9MxA9xcYjsxS1jWR4AIa2Ibzx0tc44fYX/16lV6NDFLXH+YL32jwiACRBiEbf5KcXoTIsQSpzXx4N28Ja4BQoK7rgXiydbHjx/P25TaQAJEGAguWy0+2Q8PD6/Ki4R8EVl+bzBOnZY95fq9rj9zAkTI2SxdidBHqG9+skdw43borCXO/ZcJdraPWdv22uIEiLA4q7nvvCug8WTqzQveOH26fodo7g6uFe/a17W3+nFBAkRYENRdb1vkkz1CH9cPsVy/jrhr27PqMYvENYNlHAIesRiBYwRy0V+8iXP8+/fvX11Mr7L7ECueb/r48eMqm7FuI2BGWDEG8cm+7G3NEOfmdcTQw4h9/55lhm7DekRYKQPZF2ArbXTAyu4kDYB2YxUzwg0gi/41ztHnfQG26HbGel/crVrm7tNY+/1btkOEAZ2M05r4FB7r9GbAIdxaZYrHdOsgJ/wCEQY0J74TmOKnbxxT9n3FgGGWWsVdowHtjt9Nnvf7yQM2aZU/TIAIAxrw6dOnAWtZZcoEnBpNuTuObWMEiLAx1HY0ZQJEmHJ3HNvGCBBhY6jtaMoEiJB0Z29vL6ls58vxPcO8/zfrdo5qvKO+d3Fx8Wu8zf1dW4p/cPzLly/dtv9Ts/EbcvGAHhHyfBIhZ6NSiIBTo0LNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiEC/wGgKKC4YMA4TAAAAABJRU5ErkJggg==" fallback="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMIAAADDCAYAAADQvc6UAAABRWlDQ1BJQ0MgUHJvZmlsZQAAKJFjYGASSSwoyGFhYGDIzSspCnJ3UoiIjFJgf8LAwSDCIMogwMCcmFxc4BgQ4ANUwgCjUcG3awyMIPqyLsis7PPOq3QdDFcvjV3jOD1boQVTPQrgSkktTgbSf4A4LbmgqISBgTEFyFYuLykAsTuAbJEioKOA7DkgdjqEvQHEToKwj4DVhAQ5A9k3gGyB5IxEoBmML4BsnSQk8XQkNtReEOBxcfXxUQg1Mjc0dyHgXNJBSWpFCYh2zi+oLMpMzyhRcASGUqqCZ16yno6CkYGRAQMDKMwhqj/fAIcloxgHQqxAjIHBEugw5sUIsSQpBobtQPdLciLEVJYzMPBHMDBsayhILEqEO4DxG0txmrERhM29nYGBddr//5/DGRjYNRkY/l7////39v///y4Dmn+LgeHANwDrkl1AuO+pmgAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAwqADAAQAAAABAAAAwwAAAAD9b/HnAAAHlklEQVR4Ae3dP3PTWBSGcbGzM6GCKqlIBRV0dHRJFarQ0eUT8LH4BnRU0NHR0UEFVdIlFRV7TzRksomPY8uykTk/zewQfKw/9znv4yvJynLv4uLiV2dBoDiBf4qP3/ARuCRABEFAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghgg0Aj8i0JO4OzsrPv69Wv+hi2qPHr0qNvf39+iI97soRIh4f3z58/u7du3SXX7Xt7Z2enevHmzfQe+oSN2apSAPj09TSrb+XKI/f379+08+A0cNRE2ANkupk+ACNPvkSPcAAEibACyXUyfABGm3yNHuAECRNgAZLuYPgEirKlHu7u7XdyytGwHAd8jjNyng4OD7vnz51dbPT8/7z58+NB9+/bt6jU/TI+AGWHEnrx48eJ/EsSmHzx40L18+fLyzxF3ZVMjEyDCiEDjMYZZS5wiPXnyZFbJaxMhQIQRGzHvWR7XCyOCXsOmiDAi1HmPMMQjDpbpEiDCiL358eNHurW/5SnWdIBbXiDCiA38/Pnzrce2YyZ4//59F3ePLNMl4PbpiL2J0L979+7yDtHDhw8vtzzvdGnEXdvUigSIsCLAWavHp/+qM0BcXMd/q25n1vF57TYBp0a3mUzilePj4+7k5KSLb6gt6ydAhPUzXnoPR0dHl79WGTNCfBnn1uvSCJdegQhLI1vvCk+fPu2ePXt2tZOYEV6/fn31dz+shwAR1sP1cqvLntbEN9MxA9xcYjsxS1jWR4AIa2Ibzx0tc44fYX/16lV6NDFLXH+YL32jwiACRBiEbf5KcXoTIsQSpzXx4N28Ja4BQoK7rgXiydbHjx/P25TaQAJEGAguWy0+2Q8PD6/Ki4R8EVl+bzBOnZY95fq9rj9zAkTI2SxdidBHqG9+skdw43borCXO/ZcJdraPWdv22uIEiLA4q7nvvCug8WTqzQveOH26fodo7g6uFe/a17W3+nFBAkRYENRdb1vkkz1CH9cPsVy/jrhr27PqMYvENYNlHAIesRiBYwRy0V+8iXP8+/fvX11Mr7L7ECueb/r48eMqm7FuI2BGWDEG8cm+7G3NEOfmdcTQw4h9/55lhm7DekRYKQPZF2ArbXTAyu4kDYB2YxUzwg0gi/41ztHnfQG26HbGel/crVrm7tNY+/1btkOEAZ2M05r4FB7r9GbAIdxaZYrHdOsgJ/wCEQY0J74TmOKnbxxT9n3FgGGWWsVdowHtjt9Nnvf7yQM2aZU/TIAIAxrw6dOnAWtZZcoEnBpNuTuObWMEiLAx1HY0ZQJEmHJ3HNvGCBBhY6jtaMoEiJB0Z29vL6ls58vxPcO8/zfrdo5qvKO+d3Fx8Wu8zf1dW4p/cPzLly/dtv9Ts/EbcvGAHhHyfBIhZ6NSiIBTo0LNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiEC/wGgKKC4YMA4TAAAAABJRU5ErkJggg=="
/> />
}> }>
<Meta title="工具软件分享" description="分享一些常用的办公软件" /> <Meta
{/*<Avatar*/} title={item.name}
{/* src={pic2 as any}*/} description={
{/* shape={"circle"}*/} <>
{/* style={{*/} <Tooltip title={item.description}>
{/* position: "absolute",*/} <span>{item.description}</span>
{/* top: 160,*/} </Tooltip>
{/* left: 10,*/} </>
{/* width: 60,*/} }
{/* height: 60,*/} />
{/* zIndex: 9999,*/}
{/* }}></Avatar>*/}
<Flex <Flex
vertical={false} vertical={false}
style={{ marginTop: 10 }} style={{ marginTop: 10 }}
align={"center"} align={"center"}
justify={"space-between"}> justify={"space-between"}>
<Flex vertical={false} align={"center"}> <Flex vertical={false} align={"center"}>
<Avatar src={pic as any} size={"small"} />{" "} <Avatar
<span style={{ fontSize: 12, color: "gray", marginLeft: 5 }}> src={item.avatar as any}
landaiqing size={"small"}
/>{" "}
<span
style={{
fontSize: 12,
color: "gray",
marginLeft: 5,
}}>
{item.nickName}
</span> </span>
</Flex> </Flex>
<Flex <Flex
@@ -85,22 +126,38 @@ const MainShare: FunctionComponent = () => {
style={{ width: 100 }} style={{ width: 100 }}
justify={"space-between"}> justify={"space-between"}>
<Flex vertical={false} align={"center"}> <Flex vertical={false} align={"center"}>
<EyeOutlined style={{ color: "gray" }} />{" "} <EyeOutlined
style={{ color: "gray" }}
/>{" "}
<span <span
style={{ fontSize: 12, color: "gray", marginLeft: 5 }}> style={{
1024 fontSize: 12,
color: "gray",
marginLeft: 5,
}}>
{item.views}
</span> </span>
</Flex> </Flex>
<Flex vertical={false} align={"center"}> <Flex vertical={false} align={"center"}>
<UnorderedListOutlined style={{ color: "gray" }} />{" "} <UnorderedListOutlined
style={{ color: "gray" }}
/>{" "}
<span <span
style={{ fontSize: 12, color: "gray", marginLeft: 5 }}> style={{
999 fontSize: 12,
color: "gray",
marginLeft: 5,
}}>
{item.count}
</span> </span>
</Flex> </Flex>
</Flex> </Flex>
</Flex> </Flex>
</Card> </Card>
</div>
);
})}
</Skeleton>
</Flex> </Flex>
</ProCard> </ProCard>
</div> </div>
@@ -114,15 +171,44 @@ const MainShare: FunctionComponent = () => {
paddingBottom: 80, paddingBottom: 80,
}, },
}} }}
extra={ // extra={
<Space> // <Space>
<Button onClick={onClose}></Button> // <Button onClick={onClose}>取消</Button>
<Button onClick={onClose} type="primary"> // <Button onClick={onClose} type="primary">
// 提交
</Button> // </Button>
</Space> // </Space>
}> // }
<Form layout="vertical"> >
<Form
layout="vertical"
form={form}
onFinish={(values: any) => {
const formData = {
userId: 17,
...values,
};
addShareCircle(formData).then((res: any) => {
if (res && res.success && res.data) {
message
.open({
content: "创建成功",
type: "success",
})
.then();
setOpen(false);
getShareCircles().then();
form.resetFields();
} else {
message
.open({
content: res.data,
type: "error",
})
.then();
}
});
}}>
<Form.Item <Form.Item
name="name" name="name"
label="名称" label="名称"
@@ -143,6 +229,16 @@ const MainShare: FunctionComponent = () => {
rules={[{ required: true, message: "请输入描述!" }]}> rules={[{ required: true, message: "请输入描述!" }]}>
<TextArea rows={4} maxLength={50} showCount placeholder="请输入描述!" /> <TextArea rows={4} maxLength={50} showCount placeholder="请输入描述!" />
</Form.Item> </Form.Item>
<Form.Item
style={{
display: "flex",
flexDirection: "row",
justifyContent: "flex-end",
}}>
<Button type="primary" htmlType="submit">
</Button>
</Form.Item>
</Form> </Form>
</Drawer> </Drawer>
<FloatButton <FloatButton

View File

@@ -10,17 +10,15 @@ import {
import styles from "./index.module.less"; import styles from "./index.module.less";
import { ProCard } from "@ant-design/pro-components"; import { ProCard } from "@ant-design/pro-components";
import Meta from "antd/es/card/Meta"; import Meta from "antd/es/card/Meta";
import gitee from "@/assets/icons/gitee.svg";
import { Link, useNavigate } from "react-router-dom"; import { Link, useNavigate } from "react-router-dom";
import { getAllStorage } from "@/api/oss"; import { getAllStorage } from "@/api/oss";
import StorageIcon from "@/constant/stroage-icon.ts"; import StorageIcon from "@/constant/stroage-icon.ts";
import { getUserInfoApi } from "@/api/user";
const UserInfo: FunctionComponent = () => { const UserInfo: FunctionComponent = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [userStorage, setUserStorage] = useState([]); const [userStorage, setUserStorage] = useState([]);
const [userInfo, setUserInfo] = useState<any>({} as any); // const [userInfo, setUserInfo] = useState<any>({} as any);
const data = [ const data = [
{ {
title: "Ant Design Title 1", title: "Ant Design Title 1",
@@ -36,20 +34,20 @@ const UserInfo: FunctionComponent = () => {
}, },
]; ];
async function getUserStorage() { async function getUserStorage() {
const res = await getAllStorage("1"); const res: any = await getAllStorage("1");
if (res.success) { if (res && res.success && res.data) {
setUserStorage(res.data); setUserStorage(res.data);
setLoading(false); setLoading(false);
} }
} }
const getUserInfo = async () => { // const getUserInfo = async () => {
const res = await getUserInfoApi("9"); // const res = await getUserInfoApi("9");
if (res.success) { // if (res && res.success && res.data) {
setUserInfo(res.data); // setUserInfo(res.data);
} // }
}; // };
useEffect(() => { useEffect(() => {
getUserInfo().then(); // getUserInfo().then();
getUserStorage().then(); getUserStorage().then();
}, []); }, []);
return ( return (

View File

@@ -2,9 +2,11 @@
import { useUserStore } from "./modules/user.ts"; import { useUserStore } from "./modules/user.ts";
import { useFileStore } from "@/store/modules/file.ts"; import { useFileStore } from "@/store/modules/file.ts";
import { useShareStore } from "@/store/modules/share.ts";
/** 将每个Store实例化 */ /** 将每个Store实例化 */
export const RootStore = { export const RootStore = {
user: new useUserStore(), user: new useUserStore(),
file: new useFileStore(), file: new useFileStore(),
share: new useShareStore(),
}; };

View File

@@ -5,34 +5,78 @@ import { isHydrated, makePersistable } from "mobx-persist-store";
import { handleLocalforage } from "@/utils/localforage"; import { handleLocalforage } from "@/utils/localforage";
export class useFileStore { export class useFileStore {
filePath: [any] = []; filePath: any[] = []; // 文件路径
currentStorage: string = ""; // 当前存储商
currentBucket: string = ""; // 当前存储桶
currentFile: string = ""; // 当前文件
copyFile: string = ""; // 复制的文件地址
pasteFile: string = ""; //粘贴的地址
copyFileName: string = ""; // 复制的文件名
uploadFileStorage: string = ""; // 上传文件的存储商
uploadFileBucket: string = ""; // 上传文件的存储桶
uploadFilePath: string = ""; // 上传文件的路径
constructor() { constructor() {
makeObservable(this, { makeObservable(this, {
filePath: observable, filePath: observable,
currentStorage: observable,
currentFile: observable,
currentBucket: observable,
copyFile: observable,
pasteFile: observable,
copyFileName: observable,
uploadFileStorage: observable,
uploadFileBucket: observable,
uploadFilePath: observable,
setFilePath: action, setFilePath: action,
getFilePath: action, getFilePath: action,
clearFilePath: action, clearFilePath: action,
getFilePathSecondLast: action, getFilePathSecondLast: action,
getMiddlePath: action, getMiddlePath: action,
clearAllFilePath: action, clearAllFilePath: action,
getFilePathExceptFirst: action,
setCurrentBucket: action,
setCurrentStorage: action,
getCurrentBucket: action,
getCurrentStorage: action,
getCurrentFile: action,
setCurrentFile: action,
isHydrated: action, isHydrated: action,
setCopyFile: action,
getCopyFile: action,
setPasteFile: action,
getPasteFile: action,
getCopyFileName: action,
setCopyFileName: action,
setUploadFileStorage: action,
getUploadFileStorage: action,
setUploadFileBucket: action,
getUploadFileBucket: action,
setUploadFilePath: action,
getUploadFilePath: action,
}); });
makePersistable( makePersistable(
this, this,
{ {
// 在构造函数内使用 makePersistable // 在构造函数内使用 makePersistable
name: "file", // 保存的name用于在storage中的名称标识只要不和storage中其他名称重复就可以 name: "file", // 保存的name用于在storage中的名称标识只要不和storage中其他名称重复就可以
properties: ["filePath"], // 要保存的字段这些字段会被保存在name对应的storage中注意不写在这里面的字段将不会被保存刷新页面也将丢失get字段例外。get数据会在数据返回后再自动计算 properties: [
"filePath",
"currentStorage",
"currentBucket",
"currentFile",
"copyFile",
"pasteFile",
"copyFileName",
], // 要保存的字段这些字段会被保存在name对应的storage中注意不写在这里面的字段将不会被保存刷新页面也将丢失get字段例外。get数据会在数据返回后再自动计算
storage: handleLocalforage, // 保存的位置可以是localStoragesessionstorage storage: handleLocalforage, // 保存的位置可以是localStoragesessionstorage
removeOnExpiration: true, //如果 expireIn 具有值且已过期,则在调用 getItem 时将自动删除存储中的数据。默认值为 true。 // removeOnExpiration: true, //如果 expireIn 具有值且已过期,则在调用 getItem 时将自动删除存储中的数据。默认值为 true。
stringify: false, //如果为 true则数据在传递给 setItem 之前将是 JSON.stringify。默认值为 true。 // stringify: false, //如果为 true则数据在传递给 setItem 之前将是 JSON.stringify。默认值为 true。
expireIn: 2592000000, // 一个以毫秒为单位的值,用于确定 getItem 何时不应检索存储中的数据。默认情况下永不过期。 // expireIn: 2592000000, // 一个以毫秒为单位的值,用于确定 getItem 何时不应检索存储中的数据。默认情况下永不过期。
debugMode: false, // 如果为 true将为多个 mobx-persist-store 项调用 console.info。默认值为 false。 // debugMode: false, // 如果为 true将为多个 mobx-persist-store 项调用 console.info。默认值为 false。
}, },
{ {
delay: 0, // 允许您设置一个 delay 选项来限制 write 函数的调用次数。默认情况下没有延迟。 // delay: 0, // 允许您设置一个 delay 选项来限制 write 函数的调用次数。默认情况下没有延迟。
fireImmediately: false, // 确定是应立即保留存储数据,还是等到存储中的属性发生更改。 false 默认情况下。 // fireImmediately: false, // 确定是应立即保留存储数据,还是等到存储中的属性发生更改。 false 默认情况下。
}, },
).then( ).then(
action(() => { action(() => {
@@ -64,6 +108,15 @@ export class useFileStore {
getFilePathSecondLast() { getFilePathSecondLast() {
return this.filePath.slice(0, -1).pop(); return this.filePath.slice(0, -1).pop();
} }
// 获取文件路径除了第一个
getFilePathExceptFirst() {
if (this.filePath.length === 1) {
return "";
} else {
return this.filePath.slice(1).join("/");
}
}
// 获取文件路径中间路径 // 获取文件路径中间路径
getMiddlePath() { getMiddlePath() {
if (this.filePath.length <= 2) { if (this.filePath.length <= 2) {
@@ -75,4 +128,72 @@ export class useFileStore {
isHydrated() { isHydrated() {
return isHydrated(this); return isHydrated(this);
} }
// 设置当前存储桶
setCurrentBucket(currentBucket: string) {
this.currentBucket = currentBucket;
}
// 设置当前存储商
setCurrentStorage(currentStorage: string) {
this.currentStorage = currentStorage;
}
// 获取当前存储商
getCurrentStorage() {
return this.currentStorage ? this.currentStorage : null;
}
// 获取当前存储桶
getCurrentBucket() {
return this.currentBucket ? this.currentBucket : null;
}
// 获取当前文件
getCurrentFile() {
return this.currentFile ? this.currentFile : null;
}
// 设置当前文件
setCurrentFile(currentFile: string) {
return (this.currentFile = currentFile);
}
//设置复制文件
setCopyFile(copyFile: string) {
this.copyFile = copyFile;
}
// 设置粘贴文件
setPasteFile(pasteFile: string) {
this.pasteFile = pasteFile;
}
// 获取复制文件
getCopyFile() {
return this.copyFile ? this.copyFile : null;
}
// 获取粘贴文件
getPasteFile() {
return this.pasteFile ? this.pasteFile : null;
}
// 设置文件名称
setCopyFileName(copyFileName: string) {
this.copyFileName = copyFileName;
}
// 获取文件名称
getCopyFileName() {
return this.copyFileName ? this.copyFileName : null;
}
setUploadFileStorage(uploadFileStorage: string) {
this.uploadFileStorage = uploadFileStorage;
}
getUploadFileStorage() {
return this.uploadFileStorage ? this.uploadFileStorage : null;
}
setUploadFileBucket(uploadFileBucket: string) {
this.uploadFileBucket = uploadFileBucket;
}
getUploadFileBucket() {
return this.uploadFileBucket ? this.uploadFileBucket : null;
}
setUploadFilePath(uploadFilePath: string) {
this.uploadFilePath = uploadFilePath;
}
getUploadFilePath() {
return this.uploadFilePath ? this.uploadFilePath : null;
}
} }

View File

@@ -0,0 +1,52 @@
/** @format */
import { action, makeObservable, observable } from "mobx";
import { isHydrated, makePersistable } from "mobx-persist-store";
import { handleLocalforage } from "@/utils/localforage";
export class useShareStore {
circleId: string = "";
constructor() {
makeObservable(this, {
circleId: observable,
setCircleId: action,
getCircleId: action,
isHydrated: action,
});
makePersistable(
this,
{
// 在构造函数内使用 makePersistable
name: "shareInfo", // 保存的name用于在storage中的名称标识只要不和storage中其他名称重复就可以
properties: ["circleId"], // 要保存的字段这些字段会被保存在name对应的storage中注意不写在这里面的字段将不会被保存刷新页面也将丢失get字段例外。get数据会在数据返回后再自动计算
storage: handleLocalforage, // 保存的位置可以是localStoragesessionstorage
// removeOnExpiration: true, //如果 expireIn 具有值且已过期,则在调用 getItem 时将自动删除存储中的数据。默认值为 true。
// stringify: false, //如果为 true则数据在传递给 setItem 之前将是 JSON.stringify。默认值为 true。
// expireIn: 2592000000, // 一个以毫秒为单位的值,用于确定 getItem 何时不应检索存储中的数据。默认情况下永不过期。
// debugMode: false, // 如果为 true将为多个 mobx-persist-store 项调用 console.info。默认值为 false。
} as any,
{
// delay: 0, // 允许您设置一个 delay 选项来限制 write 函数的调用次数。默认情况下没有延迟。
// fireImmediately: false, // 确定是应立即保留存储数据,还是等到存储中的属性发生更改。 false 默认情况下。
},
).then(
action(() => {
// persist 完成的回调在这里可以执行一些拿到数据后需要执行的操作如果在页面上要判断是否完成persist使用 isHydrated
// console.log(persistStore)
}),
);
}
getCircleId() {
return this.circleId ? this.circleId : null;
}
isHydrated() {
return isHydrated(this);
}
setCircleId(circleId: string) {
this.circleId = circleId;
}
}

View File

@@ -25,14 +25,14 @@ export class useUserStore {
name: "userInfo", // 保存的name用于在storage中的名称标识只要不和storage中其他名称重复就可以 name: "userInfo", // 保存的name用于在storage中的名称标识只要不和storage中其他名称重复就可以
properties: ["token", "userId"], // 要保存的字段这些字段会被保存在name对应的storage中注意不写在这里面的字段将不会被保存刷新页面也将丢失get字段例外。get数据会在数据返回后再自动计算 properties: ["token", "userId"], // 要保存的字段这些字段会被保存在name对应的storage中注意不写在这里面的字段将不会被保存刷新页面也将丢失get字段例外。get数据会在数据返回后再自动计算
storage: handleLocalforage, // 保存的位置可以是localStoragesessionstorage storage: handleLocalforage, // 保存的位置可以是localStoragesessionstorage
removeOnExpiration: true, //如果 expireIn 具有值且已过期,则在调用 getItem 时将自动删除存储中的数据。默认值为 true。 // removeOnExpiration: true, //如果 expireIn 具有值且已过期,则在调用 getItem 时将自动删除存储中的数据。默认值为 true。
stringify: false, //如果为 true则数据在传递给 setItem 之前将是 JSON.stringify。默认值为 true。 // stringify: false, //如果为 true则数据在传递给 setItem 之前将是 JSON.stringify。默认值为 true。
expireIn: 2592000000, // 一个以毫秒为单位的值,用于确定 getItem 何时不应检索存储中的数据。默认情况下永不过期。 // expireIn: 2592000000, // 一个以毫秒为单位的值,用于确定 getItem 何时不应检索存储中的数据。默认情况下永不过期。
debugMode: false, // 如果为 true将为多个 mobx-persist-store 项调用 console.info。默认值为 false。 // debugMode: false, // 如果为 true将为多个 mobx-persist-store 项调用 console.info。默认值为 false。
}, },
{ {
delay: 0, // 允许您设置一个 delay 选项来限制 write 函数的调用次数。默认情况下没有延迟。 // delay: 0, // 允许您设置一个 delay 选项来限制 write 函数的调用次数。默认情况下没有延迟。
fireImmediately: false, // 确定是应立即保留存储数据,还是等到存储中的属性发生更改。 false 默认情况下。 // fireImmediately: false, // 确定是应立即保留存储数据,还是等到存储中的属性发生更改。 false 默认情况下。
}, },
).then( ).then(
action(() => { action(() => {

25
src/types/user.d.ts vendored
View File

@@ -29,4 +29,29 @@ declare namespace API {
token: string; token: string;
deg: number; deg: number;
}; };
type AliOSSConfigRequest = {
userId?:number;
endpoint?:string;
accessKeyId?:string;
accessKeySecret?:string;
}
type MinioOSSConfigRequest = {
userId?:number;
endpoint?:string;
accessKey?:string;
secretKey?:string;
}
type QiniuOSSConfigRequest = {
userId?:number;
endpoint?:string;
accessKey?:string;
secretKey?:string;
region?:string;
}
type TencentOSSConfigRequest = {
userId?:number;
appId?:string;
secretKey?:string;
region?:string;
}
} }

View File

@@ -16,10 +16,6 @@ class Request {
if (token) { if (token) {
config.headers.Authorization = `${import.meta.env.VITE_APP_TOKEN_KEY} ${token}`; config.headers.Authorization = `${import.meta.env.VITE_APP_TOKEN_KEY} ${token}`;
} }
// if (config.method == "post") {
// config.data = EncryptData(JSON.stringify(config.data));
// }
return config; return config;
}, },
(error) => { (error) => {
@@ -30,15 +26,11 @@ class Request {
// 全局响应拦截 // 全局响应拦截
this.instance.interceptors.response.use( this.instance.interceptors.response.use(
(response) => { (response) => {
// 后端返回字符串表示需要解密操作 if (response.data instanceof Blob) {
// if (typeof response.data == "string") { return response;
// response.data = DecryptData(response.data); } else {
// if (response.status !== 200) {
// message.error(response.statusText).then();
// return Promise.reject(response.data);
// }
// }
return response.data; return response.data;
}
}, },
(error) => { (error) => {
const { response } = error; const { response } = error;
@@ -47,9 +39,6 @@ class Request {
} }
if (!window.navigator.onLine) { if (!window.navigator.onLine) {
message.error("网络连接失败"); message.error("网络连接失败");
// return router.push({
// path: '/404',
// })
return Promise.reject(error); return Promise.reject(error);
} }
}, },

View File

@@ -4,7 +4,7 @@ import Request from "./request";
const web: Request = new Request({ const web: Request = new Request({
baseURL: import.meta.env.VITE_APP_BASE_API, baseURL: import.meta.env.VITE_APP_BASE_API,
timeout: 5000, timeout: 10000,
}); });
export default web; export default web;

View File

@@ -11,7 +11,6 @@ import {
PageContainer, PageContainer,
ProCard, ProCard,
ProLayout, ProLayout,
SettingDrawer,
} from "@ant-design/pro-components"; } from "@ant-design/pro-components";
import { Link, Outlet, useLocation } from "react-router-dom"; import { Link, Outlet, useLocation } from "react-router-dom";
import logo from "@/assets/images/logo.png"; import logo from "@/assets/images/logo.png";

1
src/vite-env.d.ts vendored
View File

@@ -36,3 +36,4 @@ declare module "*.jpeg";
declare module "*.gif"; declare module "*.gif";
declare module "*.bmp"; declare module "*.bmp";
declare module "*.tiff"; declare module "*.tiff";
declare module "base-64";