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'
# the upload url
VITE_UPLOAD_URL='http://127.0.0.1:3000'
VITE_UPLOAD_URL='http://127.0.0.1:5050'
# the websocket url
VITE_WEB_SOCKET_URL='ws://127.0.0.1:3010/wx/socket'

View File

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

View File

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

7
pnpm-lock.yaml generated
View File

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

View File

@@ -5,13 +5,15 @@ import web from "@/utils/axios/web.ts";
/**
* 初始化ali oss
* @param data 用户id
* @param Id id
*/
export const initAliOSS = (data: any) => {
export const initAliOSS = (data: any, Id: any) => {
return web.request({
url: "/oss/oss/ali/init",
method: "post",
method: "get",
params: {
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
* @param data 用户id
* @param id
*/
export const initMinioOSS = (data: any) => {
export const initMinioOSS = (data: any, id: any) => {
return web.request({
url: "/oss/oss/minio/init",
method: "post",
params: {
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
* @param data 用户id
* @param id
*/
export const initQiniuOSS = (data: any) => {
export const initQiniuOSS = (data: any, id: any) => {
return web.request({
url: "/oss/oss/qiniu/init",
method: "post",
params: {
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
* @param data 用户id
* @param id
*/
export const initTencentOSS = (data: any) => {
export const initTencentOSS = (data: any, id: any) => {
return web.request({
url: "/oss/oss/ali/tencent",
url: "/oss/oss/tencent/init",
method: "post",
params: {
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 */
import { Avatar, Button, message, Skeleton } from "antd";
import { DrawerForm, ProCard, ProForm, ProFormText } from "@ant-design/pro-components";
import React, { createContext, useContext, useEffect, useState } from "react";
import styles from "./index.module.less";
import { Avatar, Button, Form, Input, message, Modal, Popconfirm, Popover, Skeleton } from "antd";
import { ProCard } from "@ant-design/pro-components";
import { useEffect, useState } from "react";
import { ClusterOutlined, DeleteOutlined, ReloadOutlined } from "@ant-design/icons";
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 { EditOutlined, EllipsisOutlined, ReloadOutlined, SettingOutlined } from "@ant-design/icons";
const DrawerContext = createContext<{
drawerVisit: boolean;
setDrawerVisit: React.Dispatch<React.SetStateAction<boolean>>;
}>({} as any);
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 [loading, setLoading] = useState<boolean>(true);
const [bucketSize, setBucketSize] = useState<string>("");
const [form] = Form.useForm();
const [open, setOpen] = useState<boolean>(false);
async function getAllBucket() {
getAllAliOSsBucket("2").then((res: any) => {
@@ -37,10 +28,11 @@ const ProCardComponent = () => {
useEffect(() => {
getAllBucket().then();
}, []);
const bucketList = () => {
return (
<ProCard
title={"阿里云OSS存储桶列表"}
title={"MinIO存储桶列表"}
headerBordered
extra={
<>
@@ -53,7 +45,7 @@ const ProCardComponent = () => {
getAllBucket().then();
}}
icon={<ReloadOutlined />}></Button>
<Button type="primary" onClick={() => setDrawerVisit(true)}>
<Button type="primary" onClick={() => setOpen(true)}>
</Button>
</>
@@ -63,33 +55,90 @@ const ProCardComponent = () => {
{buckets &&
Array.from(buckets).map((item: any, index: number) => {
return (
<>
<div key={index}>
<ProCard
headStyle={{ backgroundColor: "#f0f2f5" }}
hoverable
bordered
title={<b>{item.name}</b>}
style={{
width: "180px",
height: 150,
borderWidth: "1px",
marginLeft: 10,
}}
actions={[
<SettingOutlined key="setting" />,
<EditOutlined key="edit" />,
<EllipsisOutlined key="ellipsis" />,
]}>
<Avatar
shape={"square"}
src={bucket as any}
size={"large"}
/>
<b style={{ marginLeft: 10 }}>{item.size}</b>
</ProCard>
</div>
</>
<div key={index}>
<ProCard
headStyle={{ backgroundColor: "#f0f2f5" }}
hoverable
bordered
// title={}
style={{
width: "180px",
marginLeft: 10,
}}
actions={[
// eslint-disable-next-line react/jsx-key
<Popover
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 as any}
size={"large"}
/>
<b style={{ marginLeft: 5 }}>{item.name}</b>
</ProCard>
</div>
);
})}
</Skeleton>
@@ -101,32 +150,49 @@ const ProCardComponent = () => {
return (
<>
{bucketList()}
<DrawerForm
<Modal
title="创建存储桶"
open={drawerVisit}
onOpenChange={setDrawerVisit}
onFinish={async (values) => {
console.log(values.name);
message.success("提交成功");
return true;
}}>
<ProForm.Group>
<ProFormText
width="md"
width={"30%"}
open={open}
closable={true}
onOk={() => {
const bucketName = form.getFieldValue("name" as any);
if (bucketName === "" || bucketName === null) {
message.open({
content: " 请输入存储桶名称!",
type: "warning",
});
} 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"
label="存储桶名"
tooltip="最长为 24 位"
placeholder="请输入名称"
/>
<ProFormText
width="md"
name="capacity"
label="我方公司名称"
placeholder="请输入名称"
/>
</ProForm.Group>
</DrawerForm>
label="存储桶名"
rules={[{ required: true, message: "请输入存储桶名称!" }]}>
<Input placeholder="请输入存储桶名称" />
</Form.Item>
</Form>
</Modal>
</>
);
};

View File

@@ -1,29 +1,20 @@
/** @format */
import { Avatar, Button, message, Skeleton } from "antd";
import { DrawerForm, ProCard, ProForm, ProFormText } from "@ant-design/pro-components";
import React, { createContext, useContext, useEffect, useState } from "react";
import { EditOutlined, EllipsisOutlined, ReloadOutlined, SettingOutlined } from "@ant-design/icons";
import { Avatar, Button, Form, Input, message, Modal, Popconfirm, Popover, Skeleton } from "antd";
import { ProCard } from "@ant-design/pro-components";
import { useEffect, useState } from "react";
import { ClusterOutlined, DeleteOutlined, ReloadOutlined } from "@ant-design/icons";
import bucket from "@/assets/icons/bucket.svg";
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 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 [loading, setLoading] = useState<boolean>(true);
const [bucketSize, setBucketSize] = useState<string>("");
const [form] = Form.useForm();
const [open, setOpen] = useState<boolean>(false);
async function getAllBucket() {
getAllMinioBucket("1").then((res: any) => {
@@ -53,7 +44,7 @@ const ProCardComponent = () => {
getAllBucket().then();
}}
icon={<ReloadOutlined />}></Button>
<Button type="primary" onClick={() => setDrawerVisit(true)}>
<Button type="primary" onClick={() => setOpen(true)}>
</Button>
</>
@@ -68,23 +59,83 @@ const ProCardComponent = () => {
headStyle={{ backgroundColor: "#f0f2f5" }}
hoverable
bordered
title={<b>{item.name}</b>}
// title={}
style={{
width: "180px",
height: "150px",
marginLeft: 10,
width: "200px",
marginLeft: 20,
}}
actions={[
<SettingOutlined key="setting" />,
<EditOutlined key="edit" />,
<EllipsisOutlined key="ellipsis" />,
// eslint-disable-next-line react/jsx-key
<Popover
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 as any}
size={"large"}
/>
<b style={{ marginLeft: 10 }}>{item.size}</b>
<b style={{ marginLeft: 5 }}>{item.name}</b>
</ProCard>
</div>
);
@@ -98,32 +149,49 @@ const ProCardComponent = () => {
return (
<>
{bucketList()}
<DrawerForm
<Modal
title="创建存储桶"
open={drawerVisit}
onOpenChange={setDrawerVisit}
onFinish={async (values) => {
console.log(values.name);
message.success("提交成功");
return true;
}}>
<ProForm.Group>
<ProFormText
width="md"
width={"30%"}
open={open}
closable={true}
onOk={() => {
const bucketName = form.getFieldValue("name" as any);
if (bucketName === "" || bucketName === null) {
message.open({
content: " 请输入存储桶名称!",
type: "warning",
});
} 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"
label="存储桶名"
tooltip="最长为 24 位"
placeholder="请输入名称"
/>
<ProFormText
width="md"
name="capacity"
label="我方公司名称"
placeholder="请输入名称"
/>
</ProForm.Group>
</DrawerForm>
label="存储桶名"
rules={[{ required: true, message: "请输入存储桶名称!" }]}>
<Input placeholder="请输入存储桶名称" />
</Form.Item>
</Form>
</Modal>
</>
);
};

View File

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

View File

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

View File

@@ -1,32 +1,23 @@
/** @format */
import { Avatar, Button, message, Skeleton } from "antd";
import { DrawerForm, ProCard, ProForm, ProFormText } from "@ant-design/pro-components";
import React, { createContext, useContext, useEffect, useState } from "react";
import { EditOutlined, EllipsisOutlined, SettingOutlined } from "@ant-design/icons";
import bucket from "../../../../assets/icons/bucket.svg";
import { Avatar, Button, Form, Input, message, Modal, Popconfirm, Popover, Skeleton } from "antd";
import { ProCard } from "@ant-design/pro-components";
import { useEffect, useState } from "react";
import { ClusterOutlined, DeleteOutlined, ReloadOutlined } from "@ant-design/icons";
import bucket from "@/assets/icons/bucket.svg";
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 [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 [loading, setLoading] = useState<boolean>(true);
const [bucketSize, setBucketSize] = useState<string>("");
const [form] = Form.useForm();
const [open, setOpen] = useState<boolean>(false);
async function getAllBucket() {
getAllTencentOSsBucket("1").then((res: any) => {
getAllMinioBucket("1").then((res: any) => {
if (res && res.success) {
setBuckets(res.data);
setLoading(false);
@@ -41,12 +32,23 @@ const ProCardComponent = () => {
const bucketList = () => {
return (
<ProCard
title={"腾讯云COS存储桶列表"}
title={"MinIO存储桶列表"}
headerBordered
extra={
<Button type="primary" onClick={() => setDrawerVisit(true)}>
</Button>
<>
<Button
type="default"
shape={"circle"}
style={{ marginRight: 20 }}
onClick={() => {
setLoading(false);
getAllBucket().then();
}}
icon={<ReloadOutlined />}></Button>
<Button type="primary" onClick={() => setOpen(true)}>
</Button>
</>
}>
<div className={styles.div_proCard}>
<Skeleton loading={loading} paragraph={{ rows: 13 }} active>
@@ -58,23 +60,83 @@ const ProCardComponent = () => {
headStyle={{ backgroundColor: "#f0f2f5" }}
hoverable
bordered
title={<b>{item.name}</b>}
// title={}
style={{
width: "180px",
height: "150px",
marginLeft: 10,
}}
actions={[
<SettingOutlined key="setting" />,
<EditOutlined key="edit" />,
<EllipsisOutlined key="ellipsis" />,
// eslint-disable-next-line react/jsx-key
<Popover
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 as any}
size={"large"}
/>
<b style={{ marginLeft: 10 }}>{item.size}</b>
<b style={{ marginLeft: 5 }}>{item.name}</b>
</ProCard>
</div>
);
@@ -88,32 +150,49 @@ const ProCardComponent = () => {
return (
<>
{bucketList()}
<DrawerForm
<Modal
title="创建存储桶"
open={drawerVisit}
onOpenChange={setDrawerVisit}
onFinish={async (values) => {
console.log(values.name);
message.success("提交成功");
return true;
}}>
<ProForm.Group>
<ProFormText
width="md"
width={"30%"}
open={open}
closable={true}
onOk={() => {
const bucketName = form.getFieldValue("name" as any);
if (bucketName === "" || bucketName === null) {
message.open({
content: " 请输入存储桶名称!",
type: "warning",
});
} 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"
label="存储桶名"
tooltip="最长为 24 位"
placeholder="请输入名称"
/>
<ProFormText
width="md"
name="capacity"
label="我方公司名称"
placeholder="请输入名称"
/>
</ProForm.Group>
</DrawerForm>
label="存储桶名"
rules={[{ required: true, message: "请输入存储桶名称!" }]}>
<Input placeholder="请输入存储桶名称" />
</Form.Item>
</Form>
</Modal>
</>
);
};

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -2,118 +2,235 @@
import { PlusOutlined } from "@ant-design/icons";
import type { ActionType, ProColumns } 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 { getAllAliOSSConfig } from "@/api/oss/ali";
type AliOssConfigItem = {
id: number;
userId: number;
endpoint: string;
accessKeyId: string;
accessKeySecret: string;
createdTime: string;
updateTime: string;
status: string;
};
const columns: ProColumns<AliOssConfigItem>[] = [
{
dataIndex: "index",
valueType: "indexBorder",
width: 48,
},
{
title: "ID",
dataIndex: "id",
copyable: true,
ellipsis: true,
tooltip: "Id",
},
{
disable: true,
title: "服务地址",
dataIndex: "endpoint",
tooltip: "endpoint",
ellipsis: true,
copyable: true,
},
{
disable: true,
title: "密钥ID",
dataIndex: "accessKeyId",
tooltip: "access key id",
ellipsis: true,
copyable: true,
},
{
disable: true,
title: "密钥值",
dataIndex: "accessKeySecret",
tooltip: "access key secret",
ellipsis: true,
copyable: true,
},
{
title: "创建时间",
dataIndex: "createdTime",
valueType: "dateTime",
sorter: true,
},
{
title: "更新时间",
dataIndex: "updateTime",
valueType: "dateTime",
sorter: true,
},
{
disable: true,
title: "状态",
dataIndex: "status",
search: true,
},
{
title: "操作",
valueType: "option",
key: "option",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
render: (text: any, record: any, _, action: any) => [
<a
key="editable"
onClick={() => {
action?.startEditable?.(record.id);
}}>
</a>,
<a target="_blank" rel="noopener noreferrer" key="view">
</a>,
<TableDropdown
key="actionGroup"
onSelect={() => action?.reload()}
menus={[
{ key: "copy", name: "复制" },
{ key: "delete", name: "删除" },
]}
/>,
],
},
];
import {
addAliOSSConfig,
deleteAliConfig,
getAliConfigDetailById,
getAllAliOSSConfig,
initAliOSS,
setAliShutdown,
updateAliConfig,
} from "@/api/oss/ali";
const AliSettings: React.FC = () => {
const [form] = Form.useForm();
const actionRef = useRef<ActionType>();
const [open, setOpen] = useState(false);
const [configs, setConfigs] = useState<AliOssConfigItem[]>([]);
const [openModal, setOpenModal] = useState(false);
const [configs, setConfigs] = useState<any>([]);
const [loading, setLoading] = useState<boolean>(true);
const [configDetail, setConfigDetail] = useState<object>({});
const columns: ProColumns<any[]>[] = [
{
dataIndex: "index",
valueType: "indexBorder",
width: 48,
},
{
title: "ID",
valueType: "text",
dataIndex: "id",
copyable: true,
ellipsis: true,
tooltip: "Id",
editable: false,
},
{
disable: true,
title: "服务地址",
dataIndex: "endpoint",
tooltip: "endpoint",
ellipsis: true,
copyable: true,
},
{
disable: true,
title: "密钥ID",
dataIndex: "accessKeyId",
tooltip: "access key id",
ellipsis: true,
copyable: true,
},
{
disable: true,
title: "密钥值",
dataIndex: "accessKeySecret",
tooltip: "access key secret",
ellipsis: true,
copyable: true,
},
{
title: "创建时间",
dataIndex: "createdTime",
valueType: "dateTime",
sorter: true,
editable: false,
},
{
title: "更新时间",
dataIndex: "updateTime",
valueType: "dateTime",
sorter: true,
editable: false,
},
{
disable: true,
title: "状态",
dataIndex: "status",
search: true,
valueEnum: {
true: { text: "开启", status: "Success" },
false: { text: "关闭", status: "Error" },
},
editable: false,
},
{
title: "操作",
valueType: "option",
key: "option",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
render: (text: any, record: any, _, action: any) => [
<a
key="editable"
onClick={() => {
action?.startEditable?.(record.id);
}}>
</a>,
<a
target="_blank"
rel="noopener noreferrer"
key="view"
onClick={() => {
getConfigDetail(record.id).then(() => {
setOpenModal(true);
setLoading(false);
});
}}>
</a>,
<TableDropdown
key="actionGroup"
onSelect={(key: string) => {
if (key === "open") {
init(record.id).then();
} else if (key === "close") {
shutdown(record.id).then();
}
}}
menus={[
{ key: "open", name: "开启" },
{ key: "close", name: "关闭" },
]}
/>,
],
},
];
async function getAllConfig() {
getAllAliOSSConfig(1).then((res) => {
console.log(res);
getAllAliOSSConfig("1").then((res: any) => {
if (res && res.success && 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 = () => {
setOpen(true);
};
@@ -121,7 +238,7 @@ const AliSettings: React.FC = () => {
setOpen(false);
};
useEffect(() => {
getAllConfig();
getAllConfig().then();
}, []);
const AddAliOssConfigDrawer = () => {
return (
@@ -139,12 +256,12 @@ const AliSettings: React.FC = () => {
extra={
<Space>
<Button onClick={onClose}></Button>
<Button onClick={onClose} type="primary">
<Button onClick={addConfigs} type="primary">
</Button>
</Space>
}>
<Form layout="vertical">
<Form layout="vertical" form={form}>
<Row gutter={16}>
<Col span={12}>
<Form.Item
@@ -180,14 +297,52 @@ const AliSettings: React.FC = () => {
};
return (
<>
<div style={{ height: "65vh" }}>
<ProTable<AliOssConfigItem>
<div style={{ minHeight: "65vh" }}>
<ProTable
columns={columns}
dataSource={configs}
actionRef={actionRef}
cardBordered={true}
editable={{
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={{
persistenceKey: "pro-table-singe-demos",
@@ -224,6 +379,43 @@ const AliSettings: React.FC = () => {
]}
/>
<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>
</>
);

View File

@@ -2,9 +2,17 @@
import { PlusOutlined } from "@ant-design/icons";
import type { ActionType, ProColumns } 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 { getAllMinioConfig } from "@/api/oss/minio";
import {
addMinioOSSConfig,
deleteMinioConfig,
getAllMinioConfig,
getMinioConfigDetailById,
initMinioOSS,
setMinioShutdown,
updateMinioConfig,
} from "@/api/oss/minio";
type MinioOssConfigItem = {
id: number;
@@ -17,95 +25,123 @@ type MinioOssConfigItem = {
status: string;
};
const columns: ProColumns<MinioOssConfigItem>[] = [
{
dataIndex: "index",
valueType: "indexBorder",
width: 48,
},
{
title: "ID",
dataIndex: "id",
copyable: true,
ellipsis: true,
tooltip: "Id",
},
{
disable: true,
title: "服务地址",
dataIndex: "endpoint",
tooltip: "endpoint",
copyable: true,
ellipsis: true,
},
{
disable: true,
title: "密钥ID",
dataIndex: "accessKey",
tooltip: "access key",
ellipsis: true,
copyable: true,
},
{
disable: true,
title: "密钥值",
dataIndex: "secretKey",
tooltip: "secret key",
ellipsis: true,
copyable: true,
},
{
disable: true,
title: "状态",
dataIndex: "status",
search: true,
},
{
title: "创建时间",
dataIndex: "createdTime",
valueType: "dateTime",
sorter: true,
hideInSearch: true,
},
{
title: "更新时间",
dataIndex: "updateTime",
valueType: "dateTime",
sorter: true,
},
{
title: "操作",
valueType: "option",
key: "option",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
render: (text: ReactNode, record: MinioOssConfigItem, _: number, action: any) => [
<a
key="editable"
onClick={() => {
action?.startEditable?.(record.id);
}}>
</a>,
<a target="_blank" rel="noopener noreferrer" key="view">
</a>,
<TableDropdown
key="actionGroup"
onSelect={() => action?.reload()}
menus={[
{ key: "copy", name: "复制" },
{ key: "delete", name: "删除" },
]}
/>,
],
},
];
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",
valueType: "indexBorder",
width: 48,
editable: false,
},
{
title: "ID",
dataIndex: "id",
copyable: true,
ellipsis: true,
tooltip: "Id",
editable: false,
},
{
disable: true,
title: "服务地址",
dataIndex: "endpoint",
tooltip: "endpoint",
copyable: true,
ellipsis: true,
},
{
disable: true,
title: "密钥ID",
dataIndex: "accessKey",
tooltip: "access key",
ellipsis: true,
copyable: true,
},
{
disable: true,
title: "密钥值",
dataIndex: "secretKey",
tooltip: "secret key",
ellipsis: true,
copyable: true,
},
{
title: "创建时间",
dataIndex: "createdTime",
valueType: "dateTime",
sorter: true,
hideInSearch: true,
editable: false,
},
{
title: "更新时间",
dataIndex: "updateTime",
valueType: "dateTime",
sorter: true,
editable: false,
},
{
disable: true,
title: "状态",
dataIndex: "status",
search: true,
valueEnum: {
true: { text: "开启", status: "Success" },
false: { text: "关闭", status: "Error" },
},
editable: false,
},
{
title: "操作",
valueType: "option",
key: "option",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
render: (text: ReactNode, record: any, _: number, action: any) => [
<a
key="editable"
onClick={() => {
action?.startEditable?.(record.id);
}}>
</a>,
<a
target="_blank"
rel="noopener noreferrer"
key="view"
onClick={() => {
getConfigDetail(record.id).then(() => {
setOpenModal(true);
setLoading(false);
});
}}>
</a>,
<TableDropdown
key="actionGroup"
onSelect={(key: string) => {
if (key === "open") {
init(record.id).then();
} else if (key === "close") {
shutdown(record.id).then();
}
}}
menus={[
{ key: "open", name: "开启" },
{ key: "close", name: "关闭" },
]}
/>,
],
},
];
const showDrawer = () => {
setOpen(true);
};
@@ -114,14 +150,101 @@ const MinioSettings: React.FC = () => {
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() {
getAllMinioConfig(1).then((res: any) => {
getAllMinioConfig("1").then((res: any) => {
if (res && res.success && 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(() => {
getAllConfig().then();
}, []);
@@ -130,7 +253,7 @@ const MinioSettings: React.FC = () => {
<>
<Drawer
title="创建连接配置"
width={720}
width={"45%"}
onClose={onClose}
open={open}
styles={{
@@ -141,12 +264,12 @@ const MinioSettings: React.FC = () => {
extra={
<Space>
<Button onClick={onClose}></Button>
<Button onClick={onClose} type="primary">
<Button onClick={addMinioConfig} type="primary">
</Button>
</Space>
}>
<Form layout="vertical">
<Form layout="vertical" form={form}>
<Row gutter={16}>
<Col span={12}>
<Form.Item
@@ -182,7 +305,7 @@ const MinioSettings: React.FC = () => {
};
return (
<>
<div style={{ height: "65vh" }}>
<div style={{ minHeight: "65vh" }}>
<ProTable<MinioOssConfigItem>
columns={columns}
dataSource={configs}
@@ -190,6 +313,44 @@ const MinioSettings: React.FC = () => {
cardBordered={true}
editable={{
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={{
persistenceKey: "pro-table-singe-demos",
@@ -226,6 +387,43 @@ const MinioSettings: React.FC = () => {
]}
/>
<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>
</>
);

View File

@@ -2,9 +2,17 @@
import { PlusOutlined } from "@ant-design/icons";
import type { ActionType, ProColumns } 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 { getAllQiniuConfigs } from "@/api/oss/qiniu";
import {
addQiniuOSSConfig,
deleteQiniuConfig,
getAllQiniuConfigs,
getQiniuConfigDetailById,
initQiniuOSS,
setQiniuShutdown,
updateQiniuConfig,
} from "@/api/oss/qiniu";
type QiniuOssConfigItem = {
id: number;
@@ -17,95 +25,187 @@ type QiniuOssConfigItem = {
status: string;
};
const columns: ProColumns<QiniuOssConfigItem>[] = [
{
dataIndex: "index",
valueType: "indexBorder",
width: 48,
},
{
title: "ID",
dataIndex: "id",
copyable: true,
ellipsis: true,
tooltip: "Id",
},
{
disable: true,
title: "服务地址",
dataIndex: "endpoint",
tooltip: "endpoint",
copyable: true,
ellipsis: true,
},
{
disable: true,
title: "密钥ID",
dataIndex: "accessKey",
tooltip: "access key id",
copyable: true,
ellipsis: true,
},
{
disable: true,
title: "密钥值",
dataIndex: "secretKey",
tooltip: "access key secret",
copyable: true,
ellipsis: true,
},
{
disable: true,
title: "状态",
dataIndex: "status",
search: true,
},
{
title: "创建时间",
dataIndex: "createdTime",
valueType: "dateTime",
sorter: true,
hideInSearch: true,
},
{
title: "更新时间",
dataIndex: "updateTime",
valueType: "dateTime",
sorter: true,
},
{
title: "操作",
valueType: "option",
key: "option",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
render: (text: ReactNode, record: QiniuOssConfigItem, _: number, action: any) => [
<a
key="editable"
onClick={() => {
action?.startEditable?.(record.id);
}}>
</a>,
<a target="_blank" rel="noopener noreferrer" key="view">
</a>,
<TableDropdown
key="actionGroup"
onSelect={() => action?.reload()}
menus={[
{ key: "copy", name: "复制" },
{ key: "delete", name: "删除" },
]}
/>,
],
},
];
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",
valueType: "indexBorder",
width: 48,
editable: false,
},
{
title: "ID",
dataIndex: "id",
copyable: true,
ellipsis: true,
tooltip: "Id",
editable: false,
},
{
disable: true,
title: "服务地址",
dataIndex: "endpoint",
tooltip: "endpoint",
copyable: true,
ellipsis: true,
},
{
disable: true,
title: "密钥ID",
dataIndex: "accessKey",
tooltip: "access key id",
copyable: true,
ellipsis: true,
},
{
disable: true,
title: "密钥值",
dataIndex: "secretKey",
tooltip: "access key secret",
copyable: true,
ellipsis: true,
},
{
title: "创建时间",
dataIndex: "createdTime",
valueType: "dateTime",
sorter: true,
hideInSearch: true,
editable: false,
},
{
title: "更新时间",
dataIndex: "updateTime",
valueType: "dateTime",
sorter: true,
editable: false,
},
{
disable: true,
title: "状态",
dataIndex: "status",
search: true,
valueEnum: {
true: { text: "开启", status: "Success" },
false: { text: "关闭", status: "Error" },
},
editable: false,
},
{
title: "操作",
valueType: "option",
key: "option",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
render: (text: ReactNode, record: QiniuOssConfigItem, _: number, action: any) => [
<a
key="editable"
onClick={() => {
action?.startEditable?.(record.id);
}}>
</a>,
<a
target="_blank"
rel="noopener noreferrer"
key="view"
onClick={() => {
getConfigDetail(record.id).then(() => {
setOpenModal(true);
setLoading(false);
});
}}>
</a>,
<TableDropdown
key="actionGroup"
onSelect={(key: string) => {
if (key === "open") {
init(record.id).then();
} else if (key === "close") {
shutdown(record.id).then();
}
}}
menus={[
{ key: "open", 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 showDrawer = () => {
setOpen(true);
};
@@ -113,7 +213,8 @@ const QiniuSettings: React.FC = () => {
const onClose = () => {
setOpen(false);
};
async function getAllCOnfigs() {
async function getAllConfigs() {
getAllQiniuConfigs(1).then((res: any) => {
if (res && res.success && 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(() => {
getAllCOnfigs().then();
getAllConfigs().then();
}, []);
const AddQiniuOssConfigDrawer = () => {
return (
<>
<Drawer
title="创建连接配置"
width={720}
width={"45%"}
onClose={onClose}
open={open}
styles={{
@@ -140,12 +265,12 @@ const QiniuSettings: React.FC = () => {
extra={
<Space>
<Button onClick={onClose}></Button>
<Button onClick={onClose} type="primary">
<Button onClick={addQiniuoConfig} type="primary">
</Button>
</Space>
}>
<Form layout="vertical">
<Form layout="vertical" form={form}>
<Row gutter={16}>
<Col span={12}>
<Form.Item
@@ -155,6 +280,14 @@ const QiniuSettings: React.FC = () => {
<Input placeholder="请输入存储桶!" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="endpoint"
label="服务地址"
rules={[{ required: true, message: "请输入服务地址!" }]}>
<Input placeholder="请输入服务地址!" />
</Form.Item>
</Col>
</Row>
<Row gutter={16}>
<Col span={12}>
@@ -189,6 +322,44 @@ const QiniuSettings: React.FC = () => {
cardBordered={true}
editable={{
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={{
persistenceKey: "pro-table-singe-demos",
@@ -225,6 +396,43 @@ const QiniuSettings: React.FC = () => {
]}
/>
<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>
</>
);

View File

@@ -2,101 +2,225 @@
import { PlusOutlined } from "@ant-design/icons";
import type { ActionType, ProColumns } 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 { getAllTencentOSsConfig } from "@/api/oss/tencent";
import {
addTencentOSSConfig,
deleteTencentConfig,
getAllTencentOSsConfig,
getTencentConfigDetailById,
initTencentOSS,
setTencentShutdown,
updateTencentConfig,
} from "@/api/oss/tencent";
type TencentOssConfigItem = {
id: number;
userId: number;
endpoint: number;
secretId: string;
secretKey: string;
appId: string;
region: string;
createdTime: string;
updateTime: string;
status: string;
};
const columns: ProColumns<TencentOssConfigItem>[] = [
{
dataIndex: "index",
valueType: "indexBorder",
width: 48,
},
{
title: "ID",
dataIndex: "id",
copyable: true,
ellipsis: true,
tooltip: "Id",
},
{
disable: true,
title: "密钥ID",
dataIndex: "secretId",
copyable: true,
tooltip: "secret Id",
ellipsis: true,
},
{
disable: true,
title: "密钥值",
dataIndex: "secretKey",
tooltip: "secret Key",
copyable: true,
ellipsis: true,
},
{
disable: true,
title: "状态",
dataIndex: "status",
search: true,
},
{
title: "创建时间",
dataIndex: "createdTime",
valueType: "dateTime",
sorter: true,
hideInSearch: true,
},
{
title: "更新时间",
dataIndex: "updateTime",
valueType: "dateTime",
sorter: true,
},
{
title: "操作",
valueType: "option",
key: "option",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
render: (text: ReactNode, record: TencentOssConfigItem, _: number, action: any) => [
<a
key="editable"
onClick={() => {
action?.startEditable?.(record.id);
}}>
</a>,
<a target="_blank" rel="noopener noreferrer" key="view">
</a>,
<TableDropdown
key="actionGroup"
onSelect={() => action?.reload()}
menus={[
{ key: "copy", name: "复制" },
{ key: "delete", name: "删除" },
]}
/>,
],
},
];
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",
valueType: "indexBorder",
width: 48,
editable: false,
},
{
title: "ID",
dataIndex: "id",
copyable: true,
ellipsis: true,
tooltip: "Id",
editable: false,
},
{
disable: true,
title: "服务地址",
dataIndex: "endpoint",
copyable: true,
tooltip: "endpoint",
ellipsis: true,
},
{
disable: true,
title: "密钥ID",
dataIndex: "secretId",
copyable: true,
tooltip: "secret Id",
ellipsis: true,
},
{
disable: true,
title: "密钥值",
dataIndex: "secretKey",
tooltip: "secret Key",
copyable: true,
ellipsis: true,
},
{
disable: true,
title: "appId",
dataIndex: "appId",
tooltip: "appId",
copyable: true,
ellipsis: true,
},
{
disable: true,
title: "地域",
dataIndex: "region",
tooltip: "region",
copyable: true,
ellipsis: true,
},
{
title: "创建时间",
dataIndex: "createdTime",
valueType: "dateTime",
sorter: true,
hideInSearch: true,
editable: false,
},
{
title: "更新时间",
dataIndex: "updateTime",
valueType: "dateTime",
sorter: true,
editable: false,
},
{
disable: true,
title: "状态",
dataIndex: "status",
search: true,
valueEnum: {
true: { text: "开启", status: "Success" },
false: { text: "关闭", status: "Error" },
},
editable: false,
},
{
title: "操作",
valueType: "option",
key: "option",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
render: (text: ReactNode, record: TencentOssConfigItem, _: number, action: any) => [
<a
key="editable"
onClick={() => {
action?.startEditable?.(record.id);
}}>
</a>,
<a
target="_blank"
rel="noopener noreferrer"
key="view"
onClick={() => {
getConfigDetail(record.id).then(() => {
setOpenModal(true);
setLoading(false);
});
}}>
</a>,
<TableDropdown
key="actionGroup"
onSelect={(key: string) => {
if (key === "open") {
init(record.id).then();
} else if (key === "close") {
shutdown(record.id).then();
}
}}
menus={[
{ key: "open", 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 showDrawer = () => {
setOpen(true);
};
@@ -104,14 +228,39 @@ const TencentSettings: React.FC = () => {
const onClose = () => {
setOpen(false);
};
async function getAllConfigs() {
getAllTencentOSsConfig(5).then((res: any) => {
getAllTencentOSsConfig("1").then((res: any) => {
if (res && res.success && 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(() => {
getAllConfigs().then();
}, []);
@@ -131,12 +280,12 @@ const TencentSettings: React.FC = () => {
extra={
<Space>
<Button onClick={onClose}></Button>
<Button onClick={onClose} type="primary">
<Button onClick={addTencentConfig} type="primary">
</Button>
</Space>
}>
<Form layout="vertical">
<Form layout="vertical" form={form}>
<Row gutter={16}>
<Col span={12}>
<Form.Item
@@ -155,6 +304,24 @@ const TencentSettings: React.FC = () => {
</Form.Item>
</Col>
</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>
</Drawer>
</>
@@ -170,6 +337,46 @@ const TencentSettings: React.FC = () => {
cardBordered={true}
editable={{
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={{
persistenceKey: "pro-table-singe-demos",
@@ -206,6 +413,51 @@ const TencentSettings: React.FC = () => {
]}
/>
<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>
</>
);

View File

@@ -27,10 +27,13 @@ export default () => {
onSelect={(value: any) => {
navigate("/main/setting/" + value);
}}
placeholder={"请选择存储商"}>
{selectOptions.map((storage: any, index: any) => {
fieldNames={{
label: "name",
value: "value",
}}
labelRender={(label: any) => {
return (
<Select.Option value={storage.value} key={index}>
<>
<Card
bordered={false}
style={{
@@ -40,19 +43,44 @@ export default () => {
flexDirection: "row",
}}
size={"small"}>
<Avatar src={StorageIcon[storage.value]} size={"small"} />{" "}
<Avatar src={StorageIcon[label.value]} size={"small"} />
<span
style={{
marginLeft: "10px",
fontWeight: "bolder",
}}>
{storage.name}
{label.label}
</span>
</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>
</ProCard>
<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 styles from "./index.module.less";
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 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 navigate = useNavigate();
const divRef = useRef(null);
const fromRef: any = useRef();
const [form] = Form.useForm();
const [isDisabled, setIsDisabled] = React.useState(false);
const store = useStore("share");
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(() => {
if (divRef.current) {
@@ -39,9 +74,7 @@ const ShareAdd: React.FunctionComponent = () => {
},
},
onChange: async (value: any) => {
fromRef.current.setFieldsValue({
content: value.getHtml(),
});
form.setFieldValue("content" as any, value.getHtml());
},
} as any);
return () => {
@@ -58,7 +91,7 @@ const ShareAdd: React.FunctionComponent = () => {
shape="circle"
icon={<LeftOutlined />}
onClick={() => {
navigate("/main/share/list/1");
navigate("/main/share/list/" + store.getCircleId());
}}
/>
<Flex
@@ -71,7 +104,7 @@ const ShareAdd: React.FunctionComponent = () => {
</Flex>
</ProCard>
<div className={styles.share_add_content}>
<Form onFinish={onFinish} autoComplete="off" ref={fromRef}>
<Form onFinish={onFinish} autoComplete="off" form={form}>
<Form.Item
label={
<>
@@ -111,6 +144,7 @@ const ShareAdd: React.FunctionComponent = () => {
<h4></h4>
</>
}
rules={[{ required: true, message: "请输入介绍" }]}
name="content"
id={"content"}>
<div
@@ -126,7 +160,7 @@ const ShareAdd: React.FunctionComponent = () => {
if (!tags) {
return Promise.reject(new Error("请至少填写一个标签"));
}
if (tags.length >= 3) {
if (tags.length > 3) {
setIsDisabled(true);
return Promise.reject(
new Error("最多只能添加三个标签"),
@@ -148,7 +182,7 @@ const ShareAdd: React.FunctionComponent = () => {
id={"tags"}>
<Form.Item
validateTrigger={["onChange", "onBlur"]}
name={[field.name, "tag"]}
name={[field.name, "tagName"] as any}
noStyle>
<Input
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
size="middle"
style={{ width: "20%" }}
@@ -231,15 +267,17 @@ const ShareAdd: React.FunctionComponent = () => {
</Select>
</Form.Item>
<Form.Item
name={[field.name, "description"]}
name={[field.name, "description"] as any}
label="资源描述">
<Input name={"description"} />
</Form.Item>
<Form.Item name={[field.name, "url"]} label="资源链接">
<Form.Item
name={[field.name, "url"] as any}
label="资源链接">
<Input name={"url"} />
</Form.Item>
<Form.Item
name={[field.name, "password"]}
name={[field.name, "password"] as any}
label={"提取密码"}>
<Input name={"password"} />
</Form.Item>
@@ -269,4 +307,4 @@ const ShareAdd: React.FunctionComponent = () => {
</>
);
};
export default ShareAdd;
export default observer(ShareAdd);

View File

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

View File

@@ -1,11 +1,10 @@
/** @format */
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 styles from "./index.module.less";
import InfiniteScroll from "react-infinite-scroll-component";
import { useNavigate } from "react-router-dom";
import { Link, useNavigate, useParams } from "react-router-dom";
import {
CommentOutlined,
EyeOutlined,
@@ -13,7 +12,10 @@ import {
LeftOutlined,
ShareAltOutlined,
} 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 {
gender: string;
name: {
@@ -30,28 +32,24 @@ interface DataType {
nat: string;
}
export default () => {
export default observer(() => {
const navigate = useNavigate();
const [loading, setLoading] = useState(false);
const params = useParams();
const [loading, setLoading] = useState(true);
const [data, setData] = useState<DataType[]>([]);
const loadMoreData = () => {
if (loading) {
return;
}
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]);
const store = useStore("share");
async function getShareDetailList() {
store.setCircleId(params.id as string);
shareDetailList(params.id).then((res: any) => {
if (res && res.data && res.data) {
setData(res.data);
setLoading(false);
})
.catch(() => {
setLoading(false);
});
};
}
});
}
useEffect(() => {
loadMoreData();
getShareDetailList().then();
}, []);
return (
<>
@@ -83,13 +81,7 @@ export default () => {
</div>
</ProCard>
<ProCard bordered={false} boxShadow={false}>
<InfiniteScroll
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">
<Skeleton loading={loading} active={true} paragraph={{ rows: 14 }}>
<List
dataSource={data}
header={
@@ -97,25 +89,34 @@ export default () => {
<h4></h4>
</>
}
renderItem={(item) => (
<List.Item key={item.email}>
renderItem={(item: any) => (
<List.Item key={item.id}>
<List.Item.Meta
avatar={<Avatar src={item.picture.large} />}
avatar={<Avatar src={item.icon} />}
title={
<>
<a
onClick={() => {
navigate("/main/share/detail/1");
}}>
{item.name.last}
</a>
<Tag
bordered={false}
color="processing"
style={{ marginLeft: 10 }}>
IDM
</Tag>
</>
<Flex vertical={false} align={"center"}>
<Link to={"/main/share/detail/" + item.id}>
{item.title}
</Link>
{item.tags &&
Array.from(item.tags).map(
(tag: any, index: number) => {
return (
<Flex
vertical={false}
align={"center"}
key={index}>
<Tag
bordered={false}
color={getRandomColor()}
style={{ marginLeft: 10 }}>
{tag.tagName}
</Tag>
</Flex>
);
},
)}
</Flex>
}
description={
<>
@@ -123,7 +124,7 @@ export default () => {
vertical={false}
justify={"space-between"}
align={"center"}>
{item.email}
{item.description}
<Flex
vertical={false}
align={"center"}
@@ -131,7 +132,7 @@ export default () => {
style={{ width: "300px" }}>
<Flex vertical={false} align={"center"}>
<Avatar
src={logo as any}
src={item.avatar as any}
size={"small"}
/>
<span
@@ -140,7 +141,7 @@ export default () => {
color: "gray",
overflow: "hidden",
}}>
landaiqing
{item.nickname}
</span>
</Flex>
<Flex vertical={false} align={"center"}>
@@ -150,7 +151,7 @@ export default () => {
fontSize: 12,
color: "gray",
}}>
1024
{item.likesCount}
</span>
</Flex>
<Flex vertical={false} align={"center"}>
@@ -160,7 +161,7 @@ export default () => {
fontSize: 12,
color: "gray",
}}>
1024
{item.commentCount}
</span>
</Flex>
<Flex vertical={false} align={"center"}>
@@ -172,7 +173,7 @@ export default () => {
fontSize: 12,
color: "gray",
}}>
1024
{item.views}
</span>
</Flex>
</Flex>
@@ -180,13 +181,12 @@ export default () => {
</>
}
/>
{/*<div>Content</div>*/}
</List.Item>
)}
/>
</InfiniteScroll>
</Skeleton>
</ProCard>
</div>
</>
);
};
});

View File

@@ -1,22 +1,48 @@
/** @format */
import { FunctionComponent, useState } from "react";
import { FunctionComponent, useEffect, useState } from "react";
import { ProCard } from "@ant-design/pro-components";
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 TextArea from "antd/es/input/TextArea";
import { EyeOutlined, PlusOutlined, UnorderedListOutlined } from "@ant-design/icons";
import pic from "@/assets/images/background.png";
import Meta from "antd/es/card/Meta";
import { addShareCircle, getShareCircleList } from "@/api/share";
const MainShare: FunctionComponent = () => {
const navigate = useNavigate();
const [open, setOpen] = useState(false);
const [loading, setLoading] = useState(true);
const [circleList, setCircleList] = useState<any[]>([]);
const [form] = Form.useForm();
const onClose = () => {
setOpen(false);
};
async function getShareCircles() {
getShareCircleList().then((res: any) => {
if (res && res.success && res.data) {
setCircleList(res.data);
setLoading(false);
}
});
}
useEffect(() => {
getShareCircles().then();
}, []);
return (
<>
<div className={styles.share_main}>
@@ -30,77 +56,108 @@ const MainShare: FunctionComponent = () => {
</div>
</ProCard>
<ProCard title={<h3></h3>} bordered={false} boxShadow={false}>
<Flex vertical={false} align={"center"}>
<Card
hoverable
style={{
width: "250px",
boxShadow: " 0 0 10px rgba(0, 0, 0, 0.1)",
borderRadius: 20,
// backgroundColor: "rgba(79,68,68,0.11)",
}}
onClick={() => {
navigate("/main/share/list/1");
}}
cover={
<Image
alt="example"
src={pic as any}
style={{
height: 180,
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
}}
preview={false}
width={"100%"}
fallback="data:image/png;base64,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"
/>
}>
<Meta title="工具软件分享" description="分享一些常用的办公软件" />
{/*<Avatar*/}
{/* src={pic2 as any}*/}
{/* shape={"circle"}*/}
{/* style={{*/}
{/* position: "absolute",*/}
{/* top: 160,*/}
{/* left: 10,*/}
{/* width: 60,*/}
{/* height: 60,*/}
{/* zIndex: 9999,*/}
{/* }}></Avatar>*/}
<Flex
vertical={false}
style={{ marginTop: 10 }}
align={"center"}
justify={"space-between"}>
<Flex vertical={false} align={"center"}>
<Avatar src={pic as any} size={"small"} />{" "}
<span style={{ fontSize: 12, color: "gray", marginLeft: 5 }}>
landaiqing
</span>
</Flex>
<Flex
vertical={false}
align={"center"}
style={{ width: 100 }}
justify={"space-between"}>
<Flex vertical={false} align={"center"}>
<EyeOutlined style={{ color: "gray" }} />{" "}
<span
style={{ fontSize: 12, color: "gray", marginLeft: 5 }}>
1024
</span>
</Flex>
<Flex vertical={false} align={"center"}>
<UnorderedListOutlined style={{ color: "gray" }} />{" "}
<span
style={{ fontSize: 12, color: "gray", marginLeft: 5 }}>
999
</span>
</Flex>
</Flex>
</Flex>
</Card>
<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
hoverable
style={{
width: "250px",
boxShadow: " 0 0 10px rgba(0, 0, 0, 0.1)",
borderRadius: 20,
marginLeft: 30,
marginTop: 20,
// backgroundColor: "rgba(79,68,68,0.11)",
}}
onClick={() => {
navigate("/main/share/list/" + item.id);
}}
cover={
<Image
alt="example"
src={item.icon}
style={{
height: 180,
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
backgroundSize: "cover",
}}
preview={false}
width={"100%"}
height={"100%"}
fallback="data:image/png;base64,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"
/>
}>
<Meta
title={item.name}
description={
<>
<Tooltip title={item.description}>
<span>{item.description}</span>
</Tooltip>
</>
}
/>
<Flex
vertical={false}
style={{ marginTop: 10 }}
align={"center"}
justify={"space-between"}>
<Flex vertical={false} align={"center"}>
<Avatar
src={item.avatar as any}
size={"small"}
/>{" "}
<span
style={{
fontSize: 12,
color: "gray",
marginLeft: 5,
}}>
{item.nickName}
</span>
</Flex>
<Flex
vertical={false}
align={"center"}
style={{ width: 100 }}
justify={"space-between"}>
<Flex vertical={false} align={"center"}>
<EyeOutlined
style={{ color: "gray" }}
/>{" "}
<span
style={{
fontSize: 12,
color: "gray",
marginLeft: 5,
}}>
{item.views}
</span>
</Flex>
<Flex vertical={false} align={"center"}>
<UnorderedListOutlined
style={{ color: "gray" }}
/>{" "}
<span
style={{
fontSize: 12,
color: "gray",
marginLeft: 5,
}}>
{item.count}
</span>
</Flex>
</Flex>
</Flex>
</Card>
</div>
);
})}
</Skeleton>
</Flex>
</ProCard>
</div>
@@ -114,15 +171,44 @@ const MainShare: FunctionComponent = () => {
paddingBottom: 80,
},
}}
extra={
<Space>
<Button onClick={onClose}></Button>
<Button onClick={onClose} type="primary">
</Button>
</Space>
}>
<Form layout="vertical">
// extra={
// <Space>
// <Button onClick={onClose}>取消</Button>
// <Button onClick={onClose} type="primary">
// 提交
// </Button>
// </Space>
// }
>
<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
name="name"
label="名称"
@@ -143,6 +229,16 @@ const MainShare: FunctionComponent = () => {
rules={[{ required: true, message: "请输入描述!" }]}>
<TextArea rows={4} maxLength={50} showCount placeholder="请输入描述!" />
</Form.Item>
<Form.Item
style={{
display: "flex",
flexDirection: "row",
justifyContent: "flex-end",
}}>
<Button type="primary" htmlType="submit">
</Button>
</Form.Item>
</Form>
</Drawer>
<FloatButton

View File

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

View File

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

View File

@@ -5,34 +5,78 @@ import { isHydrated, makePersistable } from "mobx-persist-store";
import { handleLocalforage } from "@/utils/localforage";
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() {
makeObservable(this, {
filePath: observable,
currentStorage: observable,
currentFile: observable,
currentBucket: observable,
copyFile: observable,
pasteFile: observable,
copyFileName: observable,
uploadFileStorage: observable,
uploadFileBucket: observable,
uploadFilePath: observable,
setFilePath: action,
getFilePath: action,
clearFilePath: action,
getFilePathSecondLast: action,
getMiddlePath: action,
clearAllFilePath: action,
getFilePathExceptFirst: action,
setCurrentBucket: action,
setCurrentStorage: action,
getCurrentBucket: action,
getCurrentStorage: action,
getCurrentFile: action,
setCurrentFile: 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(
this,
{
// 在构造函数内使用 makePersistable
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
removeOnExpiration: true, //如果 expireIn 具有值且已过期,则在调用 getItem 时将自动删除存储中的数据。默认值为 true。
stringify: false, //如果为 true则数据在传递给 setItem 之前将是 JSON.stringify。默认值为 true。
expireIn: 2592000000, // 一个以毫秒为单位的值,用于确定 getItem 何时不应检索存储中的数据。默认情况下永不过期。
debugMode: false, // 如果为 true将为多个 mobx-persist-store 项调用 console.info。默认值为 false。
// removeOnExpiration: true, //如果 expireIn 具有值且已过期,则在调用 getItem 时将自动删除存储中的数据。默认值为 true。
// stringify: false, //如果为 true则数据在传递给 setItem 之前将是 JSON.stringify。默认值为 true。
// expireIn: 2592000000, // 一个以毫秒为单位的值,用于确定 getItem 何时不应检索存储中的数据。默认情况下永不过期。
// debugMode: false, // 如果为 true将为多个 mobx-persist-store 项调用 console.info。默认值为 false。
},
{
delay: 0, // 允许您设置一个 delay 选项来限制 write 函数的调用次数。默认情况下没有延迟。
fireImmediately: false, // 确定是应立即保留存储数据,还是等到存储中的属性发生更改。 false 默认情况下。
// delay: 0, // 允许您设置一个 delay 选项来限制 write 函数的调用次数。默认情况下没有延迟。
// fireImmediately: false, // 确定是应立即保留存储数据,还是等到存储中的属性发生更改。 false 默认情况下。
},
).then(
action(() => {
@@ -64,6 +108,15 @@ export class useFileStore {
getFilePathSecondLast() {
return this.filePath.slice(0, -1).pop();
}
// 获取文件路径除了第一个
getFilePathExceptFirst() {
if (this.filePath.length === 1) {
return "";
} else {
return this.filePath.slice(1).join("/");
}
}
// 获取文件路径中间路径
getMiddlePath() {
if (this.filePath.length <= 2) {
@@ -75,4 +128,72 @@ export class useFileStore {
isHydrated() {
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

@@ -1,64 +1,64 @@
/** @format */
import { action, makeObservable, observable } from "mobx";
import { isHydrated, makePersistable } from "mobx-persist-store";
import { handleLocalforage } from "@/utils/localforage";
export class useUserStore {
token: string = "";
userId: string = "";
constructor() {
makeObservable(this, {
token: observable,
userId: observable,
setToken: action,
setUserId: action,
getToken: action,
getUserId: action,
isHydrated: action,
});
makePersistable(
this,
{
// 在构造函数内使用 makePersistable
name: "userInfo", // 保存的name用于在storage中的名称标识只要不和storage中其他名称重复就可以
properties: ["token", "userId"], // 要保存的字段这些字段会被保存在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。
},
{
delay: 0, // 允许您设置一个 delay 选项来限制 write 函数的调用次数。默认情况下没有延迟。
fireImmediately: false, // 确定是应立即保留存储数据,还是等到存储中的属性发生更改。 false 默认情况下。
},
).then(
action(() => {
// persist 完成的回调在这里可以执行一些拿到数据后需要执行的操作如果在页面上要判断是否完成persist使用 isHydrated
// console.log(persistStore)
}),
);
}
getToken() {
return this.token ? this.token : null;
}
getUserId() {
return this.userId ? this.userId : null;
}
isHydrated() {
return isHydrated(this);
}
setToken(token: string) {
this.token = token;
}
setUserId(userId: string) {
this.userId = userId;
}
}
/** @format */
import { action, makeObservable, observable } from "mobx";
import { isHydrated, makePersistable } from "mobx-persist-store";
import { handleLocalforage } from "@/utils/localforage";
export class useUserStore {
token: string = "";
userId: string = "";
constructor() {
makeObservable(this, {
token: observable,
userId: observable,
setToken: action,
setUserId: action,
getToken: action,
getUserId: action,
isHydrated: action,
});
makePersistable(
this,
{
// 在构造函数内使用 makePersistable
name: "userInfo", // 保存的name用于在storage中的名称标识只要不和storage中其他名称重复就可以
properties: ["token", "userId"], // 要保存的字段这些字段会被保存在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。
},
{
// delay: 0, // 允许您设置一个 delay 选项来限制 write 函数的调用次数。默认情况下没有延迟。
// fireImmediately: false, // 确定是应立即保留存储数据,还是等到存储中的属性发生更改。 false 默认情况下。
},
).then(
action(() => {
// persist 完成的回调在这里可以执行一些拿到数据后需要执行的操作如果在页面上要判断是否完成persist使用 isHydrated
// console.log(persistStore)
}),
);
}
getToken() {
return this.token ? this.token : null;
}
getUserId() {
return this.userId ? this.userId : null;
}
isHydrated() {
return isHydrated(this);
}
setToken(token: string) {
this.token = token;
}
setUserId(userId: string) {
this.userId = userId;
}
}

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

@@ -29,4 +29,29 @@ declare namespace API {
token: string;
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

@@ -1,174 +1,163 @@
/** @format */
import axios, { AxiosInstance, AxiosRequestConfig } from "axios";
import { message } from "antd";
import { getStorageFromKey } from "@/utils/localStorage/config.ts";
class Request {
private instance: AxiosInstance | undefined;
constructor(config: AxiosRequestConfig) {
this.instance = axios.create(config);
// 全局请求拦截
this.instance.interceptors.request.use(
(config) => {
const token: string | null = getStorageFromKey("token");
if (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;
},
(error) => {
return Promise.reject(error);
},
);
// 全局响应拦截
this.instance.interceptors.response.use(
(response) => {
// 后端返回字符串表示需要解密操作
// if (typeof response.data == "string") {
// response.data = DecryptData(response.data);
// if (response.status !== 200) {
// message.error(response.statusText).then();
// return Promise.reject(response.data);
// }
// }
return response.data;
},
(error) => {
const { response } = error;
if (response) {
this.handleCode(response.status);
}
if (!window.navigator.onLine) {
message.error("网络连接失败");
// return router.push({
// path: '/404',
// })
return Promise.reject(error);
}
},
);
}
handleCode(code: number): void {
switch (code) {
case 400:
message
.open({
content: "请求错误(400)",
type: "error",
})
.then();
break;
case 401:
message
.open({
content: "未授权,请重新登录(401)",
type: "error",
})
.then();
break;
case 403:
message
.open({
content: "拒绝访问(403)",
type: "error",
})
.then();
break;
case 404:
message
.open({
content: "请求出错(404)",
type: "error",
})
.then();
break;
case 408:
message
.open({
content: "请求超时(408)",
type: "error",
})
.then();
break;
case 500:
message
.open({
content: "服务器错误(500)",
type: "error",
})
.then();
break;
case 501:
message
.open({
content: "服务未实现(501)",
type: "error",
})
.then();
break;
case 502:
message
.open({
content: "网络错误(502)",
type: "error",
})
.then();
break;
case 503:
message
.open({
content: "服务不可用(503)",
type: "error",
})
.then();
break;
case 504:
message
.open({
content: "网络超时(504)",
type: "error",
})
.then();
break;
case 505:
message
.open({
content: "HTTP版本不受支持(505)",
type: "error",
})
.then();
break;
default:
message
.open({
content: `连接出错(${code})!`,
type: "error",
})
.then();
break;
}
}
request<T>(config: AxiosRequestConfig<T>): Promise<T> {
return new Promise<T>((resolve, reject) => {
this.instance
?.request<any, T>(config)
.then((res) => {
resolve(res);
})
.catch((err) => {
reject(err);
});
});
}
}
export default Request;
/** @format */
import axios, { AxiosInstance, AxiosRequestConfig } from "axios";
import { message } from "antd";
import { getStorageFromKey } from "@/utils/localStorage/config.ts";
class Request {
private instance: AxiosInstance | undefined;
constructor(config: AxiosRequestConfig) {
this.instance = axios.create(config);
// 全局请求拦截
this.instance.interceptors.request.use(
(config) => {
const token: string | null = getStorageFromKey("token");
if (token) {
config.headers.Authorization = `${import.meta.env.VITE_APP_TOKEN_KEY} ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
},
);
// 全局响应拦截
this.instance.interceptors.response.use(
(response) => {
if (response.data instanceof Blob) {
return response;
} else {
return response.data;
}
},
(error) => {
const { response } = error;
if (response) {
this.handleCode(response.status);
}
if (!window.navigator.onLine) {
message.error("网络连接失败");
return Promise.reject(error);
}
},
);
}
handleCode(code: number): void {
switch (code) {
case 400:
message
.open({
content: "请求错误(400)",
type: "error",
})
.then();
break;
case 401:
message
.open({
content: "未授权,请重新登录(401)",
type: "error",
})
.then();
break;
case 403:
message
.open({
content: "拒绝访问(403)",
type: "error",
})
.then();
break;
case 404:
message
.open({
content: "请求出错(404)",
type: "error",
})
.then();
break;
case 408:
message
.open({
content: "请求超时(408)",
type: "error",
})
.then();
break;
case 500:
message
.open({
content: "服务器错误(500)",
type: "error",
})
.then();
break;
case 501:
message
.open({
content: "服务未实现(501)",
type: "error",
})
.then();
break;
case 502:
message
.open({
content: "网络错误(502)",
type: "error",
})
.then();
break;
case 503:
message
.open({
content: "服务不可用(503)",
type: "error",
})
.then();
break;
case 504:
message
.open({
content: "网络超时(504)",
type: "error",
})
.then();
break;
case 505:
message
.open({
content: "HTTP版本不受支持(505)",
type: "error",
})
.then();
break;
default:
message
.open({
content: `连接出错(${code})!`,
type: "error",
})
.then();
break;
}
}
request<T>(config: AxiosRequestConfig<T>): Promise<T> {
return new Promise<T>((resolve, reject) => {
this.instance
?.request<any, T>(config)
.then((res) => {
resolve(res);
})
.catch((err) => {
reject(err);
});
});
}
}
export default Request;

View File

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

View File

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

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

@@ -1,38 +1,39 @@
/** @format */
/// <reference types="vite/client" />
declare interface ImportMetaEnv {
readonly VITE_APP_BASE_API: string;
readonly VITE_APP_TITLE: string;
readonly VITE_API_BASE_URL: string;
readonly VITE_NODE_ENV: string;
readonly VITE_TITLE_NAME: string;
readonly VITE_APP_TOKEN_KEY?: string;
readonly VITE_UPLOAD_URL?: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
declare module "*.svg" {
const content: any;
export default content;
}
declare module "*.js" {
const content: any;
export default content;
}
declare module ".*" {
const value: any;
export default value;
}
declare module "*.tsx";
declare module "*.svg";
declare module "*.png";
declare module "*.jpg";
declare module "*.jpeg";
declare module "*.gif";
declare module "*.bmp";
declare module "*.tiff";
/** @format */
/// <reference types="vite/client" />
declare interface ImportMetaEnv {
readonly VITE_APP_BASE_API: string;
readonly VITE_APP_TITLE: string;
readonly VITE_API_BASE_URL: string;
readonly VITE_NODE_ENV: string;
readonly VITE_TITLE_NAME: string;
readonly VITE_APP_TOKEN_KEY?: string;
readonly VITE_UPLOAD_URL?: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
declare module "*.svg" {
const content: any;
export default content;
}
declare module "*.js" {
const content: any;
export default content;
}
declare module ".*" {
const value: any;
export default value;
}
declare module "*.tsx";
declare module "*.svg";
declare module "*.png";
declare module "*.jpg";
declare module "*.jpeg";
declare module "*.gif";
declare module "*.bmp";
declare module "*.tiff";
declare module "base-64";