19 Commits
dev ... master

Author SHA1 Message Date
landaiqing
79f951bac8 feat: 去掉多余import 2024-07-24 01:01:38 +08:00
landaiqing
190fda426c fix: bug修复 2024-07-24 00:59:28 +08:00
landaiqing
8fa1c50fb3 feat: update 2024-07-23 11:00:17 +08:00
landaiqing
757f494f1f feat: update 2024-07-23 10:17:28 +08:00
landaiqing
c218006dd9 fix: bug修复 2024-07-23 09:44:11 +08:00
landaiqing
8e10d4a253 fix: bug修复 2024-07-22 22:44:09 +08:00
landaiqing
ac41e00105 feat: update 2024-07-22 09:04:46 +08:00
landaiqing
3dacedf7fd feat: 更新我的分享页面 2024-07-20 16:54:08 +08:00
landaiqing
6e8b2ae643 feat: 评论接口对接 2024-07-19 18:58:47 +08:00
landaiqing
359ef60128 feat: 首页仪表盘接口对接完成 2024-07-17 00:12:32 +08:00
landaiqing
d63b76b639 feat: 评论界面 2024-07-13 10:44:31 +08:00
landaiqing
4a3809e959 feat: 修复打包报错问题 2024-07-13 00:06:43 +08:00
landaiqing
6ec4a92853 feat: 界面调整 2024-07-12 23:46:40 +08:00
landaiqing
8677b975db feat: update 2024-07-12 11:33:41 +08:00
landaiqing
59cd675028 feat: 首页仪表盘调整 2024-07-12 10:48:43 +08:00
landaiqing
e79b193b27 feat: 更新依赖包版本 2024-07-11 17:34:27 +08:00
landaiqing
36625c10c6 feat: 添加我的分享页面 2024-07-11 16:43:56 +08:00
landaiqing
b7a16c3c59 feat: 文件分享update 2024-07-11 14:56:57 +08:00
landaiqing
32dff4adbb feat: update 2024-07-10 15:54:41 +08:00
104 changed files with 17956 additions and 9584 deletions

View File

@@ -8,7 +8,7 @@ VITE_APP_BASE_API='/api'
VITE_APP_TITLE=开发环境 VITE_APP_TITLE=开发环境
# 网络请求公用地址 # 网络请求公用地址
VITE_API_BASE_URL='http://127.0.0.1:3000' VITE_API_BASE_URL='http://127.0.0.1:5050'
#VITE_API_BASE_URL='http://1.95.0.111:4000' #VITE_API_BASE_URL='http://1.95.0.111:4000'
VITE_TITLE_NAME='五味子云存储' VITE_TITLE_NAME='五味子云存储'
@@ -17,7 +17,7 @@ VITE_TITLE_NAME='五味子云存储'
VITE_APP_TOKEN_KEY='schisandra' VITE_APP_TOKEN_KEY='schisandra'
# the upload url # the upload url
VITE_UPLOAD_URL='http://127.0.0.1:3000' VITE_UPLOAD_URL='http://127.0.0.1:5050'
# the websocket url # the websocket url
VITE_WEB_SOCKET_URL='ws://127.0.0.1:3010/wx/socket' VITE_WEB_SOCKET_URL='ws://127.0.0.1:3010/wx/socket'

View File

@@ -7,7 +7,7 @@ VITE_APP_BASE_API='/api'
VITE_APP_TITLE=生产环境 VITE_APP_TITLE=生产环境
# 网络请求公用地址 # 网络请求公用地址
VITE_API_BASE_URL='http://1.95.0.111:3000' VITE_API_BASE_URL='http://1.95.0.111:5050'
VITE_TITLE_NAME='五味子云存储' VITE_TITLE_NAME='五味子云存储'
@@ -15,4 +15,4 @@ VITE_TITLE_NAME='五味子云存储'
VITE_APP_TOKEN_KEY='schisandra' VITE_APP_TOKEN_KEY='schisandra'
# the upload url # the upload url
VITE_UPLOAD_URL='http://1.95.0.111:3000' VITE_UPLOAD_URL='http://1.95.0.111:5050'

View File

@@ -10,29 +10,34 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@ant-design/charts": "^2.1.1",
"@ant-design/icons": "^5.3.7", "@ant-design/icons": "^5.3.7",
"@ant-design/pro-components": "^2.7.9", "@ant-design/pro-components": "^2.7.10",
"@ant-design/pro-provider": "^2.14.7",
"@ant-design/use-emotion-css": "^1.0.4", "@ant-design/use-emotion-css": "^1.0.4",
"@babel/preset-env": "^7.24.6", "@babel/preset-env": "^7.24.7",
"@types/crypto-js": "^4.2.2", "@types/crypto-js": "^4.2.2",
"@types/react-calendar-heatmap": "^1.6.7", "@types/react-calendar-heatmap": "^1.6.7",
"@vitejs/plugin-legacy": "^5.4.0", "@vitejs/plugin-legacy": "^5.4.1",
"antd": "^5.17.4", "aieditor": "^1.0.12",
"antd": "^5.19.1",
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.19",
"axios": "^1.7.2", "axios": "^1.7.2",
"base-64": "^1.0.0",
"core-js": "^3.37.1", "core-js": "^3.37.1",
"crypto-js": "^4.2.0", "crypto-js": "^4.2.0",
"echarts-for-react": "^3.0.2", "echarts-for-react": "^3.0.2",
"jsencrypt": "^3.3.2", "jsencrypt": "^3.3.2",
"localforage": "^1.10.0", "localforage": "^1.10.0",
"mobx": "^6.12.3", "mobx": "^6.13.0",
"mobx-persist-store": "^1.1.5", "mobx-persist-store": "^1.1.5",
"mobx-react": "^9.1.1", "mobx-react": "^9.1.1",
"react": "^18.3.1", "react": "^18.3.1",
"react-calendar-heatmap": "^1.9.0", "react-calendar-heatmap": "^1.9.0",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-infinite-scroll-component": "^6.1.0",
"react-rotate-captcha": "^1.0.26", "react-rotate-captcha": "^1.0.26",
"react-router-dom": "^6.23.1", "react-router-dom": "^6.24.1",
"regenerator-runtime": "^0.14.1", "regenerator-runtime": "^0.14.1",
"timer-manager-lib": "^1.0.2", "timer-manager-lib": "^1.0.2",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
@@ -40,28 +45,28 @@
}, },
"devDependencies": { "devDependencies": {
"@rollup/plugin-babel": "^6.0.4", "@rollup/plugin-babel": "^6.0.4",
"@types/node": "^20.12.12", "@types/node": "^20.14.10",
"@types/react": "^18.3.3", "@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0", "@types/react-dom": "^18.3.0",
"@typescript-eslint/eslint-plugin": "^7.11.0", "@typescript-eslint/eslint-plugin": "^7.16.0",
"@typescript-eslint/parser": "^7.11.0", "@typescript-eslint/parser": "^7.16.0",
"@vitejs/plugin-react": "^4.3.0", "@vitejs/plugin-react": "^4.3.1",
"eslint": "^8.57.0", "eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3", "eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-react": "^7.34.2", "eslint-plugin-react": "^7.34.3",
"eslint-plugin-react-hooks": "^4.6.2", "eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-react-refresh": "^0.4.7", "eslint-plugin-react-refresh": "^0.4.8",
"less": "^4.2.0", "less": "^4.2.0",
"postcss-less": "^6.0.0", "postcss-less": "^6.0.0",
"postcss-preset-env": "^9.5.14", "postcss-preset-env": "^9.6.0",
"prettier": "^3.2.5", "prettier": "^3.3.2",
"stylelint": "^16.6.1", "stylelint": "^16.6.1",
"stylelint-config-recess-order": "^5.0.1", "stylelint-config-recess-order": "^5.0.1",
"stylelint-config-standard-less": "^3.0.1", "stylelint-config-standard-less": "^3.0.1",
"stylelint-order": "^6.0.4", "stylelint-order": "^6.0.4",
"typescript": "^5.4.5", "typescript": "^5.5.3",
"vite": "^5.2.12", "vite": "^5.3.3",
"vite-plugin-node-polyfills": "^0.22.0", "vite-plugin-node-polyfills": "^0.22.0",
"vite-plugin-svgr": "^4.2.0" "vite-plugin-svgr": "^4.2.0"
} }

4214
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

134
src/api/oss/ali/index.ts Normal file
View File

@@ -0,0 +1,134 @@
/** @format */
import web from "@/utils/axios/web.ts";
/**
* 初始化ali oss
* @param data 用户id
* @param Id id
*/
export const initAliOSS = (data: any, Id: any) => {
return web.request({
url: "/oss/oss/ali/init",
method: "get",
params: {
userId: data,
Id: Id,
},
});
};
/**
* 获取文件目录信息
* @param userId 用户id
* @param dirName 目录名称
* @param bucket 桶名称
*/
export const getDirAndFileList = (userId: any, dirName: any, bucket: any) => {
return web.request({
url: "/oss/oss/ali/listDir",
method: "get",
params: {
userId: userId,
dirName: dirName,
bucket: bucket,
},
});
};
/**
* 查询Ali OSS所有桶
* @param userId
*/
export const getAllAliOSsBucket = (userId: any) => {
return web.request({
url: `/oss/oss/ali/seleteBucket`,
method: "post",
params: {
userId: userId,
},
});
};
/**
* 获取所有阿里云oss配置
* @param userId
*/
export const getAllAliOSSConfig = (userId: any) => {
return web.request({
url: `/oss/oss/ali/return_online`,
method: "get",
params: {
userId: userId,
},
});
};
/**
* 新增阿里云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,
},
});
};

357
src/api/oss/index.ts Normal file
View File

@@ -0,0 +1,357 @@
/** @format */
import web from "@/utils/axios/web.ts";
/**
* 获取用户存储商
* @param data 用户id
*/
export const getAllStorage = (data: any) => {
return web.request({
url: "/oss/oss/user/selectUserOSSType",
method: "get",
params: {
userId: data,
},
});
};
/**
* 获取存储商所有存储桶
* @param data 用户id
* @param type
*/
export const getStorageBuckets = (data: any, type: any) => {
return web.request({
url: "/oss/oss/" + type + "/seleteBucket",
method: "post",
params: {
userId: data,
},
});
};
/**
* 获取存储桶所有文件
* @param userId
* @param type
* @param bucket
* @param dirName
*/
export const getBucketFiles = (userId: any, bucket: any, dirName: any, type: any) => {
return web.request({
url: "/oss/oss/" + type + "/listDir",
method: "get",
params: {
userId: userId,
bucket: bucket,
dirName: dirName,
},
});
};
/**
* 获取存储商所有存储桶个数
* @param userId
*/
export const getStorageAndBuckets = (userId: any) => {
return web.request({
url: "/oss/oss/user/selectUserOSSType",
method: "get",
params: {
userId: userId,
},
});
};
/**
* 获取用户文件热力图
* @param userId
*/
export const getUserFileHeatMap = (userId: any) => {
return web.request({
url: "/oss/oss/user/selectUserFileHeat",
method: "get",
params: {
userId: userId,
},
});
};
/**
* 获取用户文件流量
* @param userId
*/
export const getUserFileFlow = (userId: any) => {
return web.request({
url: "/oss/oss/user/selectFileFlow",
method: "get",
params: {
userId: userId,
},
});
};
/**
* 获取用户文件个数
* @param userId
*/
export const getUserFileCount = (userId: any) => {
return web.request({
url: "/oss/oss/user/selectFileCount",
method: "get",
params: {
userId: userId,
},
});
};
/**
* 获取用户每月上传文件个数
* @param userId
*/
export const getUserUploadFileDiagramPerMonth = (userId: any) => {
return web.request({
url: "/oss/oss/user/selectUploadFileDiagramPerMonth",
method: "get",
params: {
userId: userId,
},
});
};
/**
* 获取用户每月下载文件个数
* @param userId
*/
export const getUserDownloadFileDiagramPerMonth = (userId: any) => {
return web.request({
url: "/oss/oss/user/selectDownloadFileDiagramPerMonth",
method: "get",
params: {
userId: userId,
},
});
};
/**
* 获取用户最近下载文件
* @param userId
*/
export const getUserRecentDownloadFile = (userId: any) => {
return web.request({
url: "/oss/oss/user/selectRecentDownloadFile",
method: "get",
params: {
userId: userId,
},
});
};
/**
* 获取用户最近上传文件
* @param userId
*/
export const getUserRecentUploadFile = (userId: any) => {
return web.request({
url: "/oss/oss/user/selectRecentUploadFile",
method: "get",
params: {
userId: userId,
},
});
};
/**
* 获取用户最近预览文件
* @param userId
*/
export const getUserRecentPreviewFile = (userId: any) => {
return web.request({
url: "/oss/oss/user/selectPreviewFile",
method: "get",
params: {
userId: userId,
},
});
};
/**
* 获取存储桶
* @param userId
* @param bucket
* @param type
*/
export const deleteBucket = (userId: any, bucket: any, type: any) => {
return web.request({
url: `/oss/oss/` + type + `/deleteBucket`,
method: "post",
params: {
userId: userId,
bucket: bucket,
},
});
};
/**
* 获取存储桶大小
* @param userId
* @param bucket
* @param type
*/
export const getBucketSize = (userId: any, bucket: any, type: any) => {
return web.request({
url: `/oss/oss/` + type + `/getBucketSize`,
method: "post",
params: {
userId: userId,
bucket: bucket,
},
});
};
/**
* 创建存储桶
* @param userId
* @param bucket
* @param type
*/
export const creatBucket = (userId: any, bucket: any, type: any) => {
return web.request({
url: `/oss/oss/` + type + `/createBucket`,
method: "post",
params: {
userId: userId,
bucket: bucket,
},
});
};
/**
* 删除文件
* @param userId
* @param bucket
* @param type
* @param filePath
*/
export const deleteFile = (userId: any, bucket: any, type: any, filePath: any) => {
return web.request({
url: `/oss/oss/` + type + `/deleteFile`,
method: "post",
params: {
userId: userId,
bucket: bucket,
filePath: filePath,
},
});
};
/**
* 下载文件
* @param userId
* @param bucket
* @param type
* @param listObjectsArgs
*/
export const downloadFiles = (userId: any, bucket: any, type: any, listObjectsArgs: any) => {
return web.request({
url: `/oss/oss/` + type + `/downloadFile`,
method: "get",
responseType: "blob",
params: {
userId: userId,
bucket: bucket,
listObjectsArgs: listObjectsArgs,
},
});
};
/**
* 预览文件
* @param userId
* @param bucket
* @param type
* @param filePath
*/
export const previewFile = (userId: any, bucket: any, type: any, filePath: any) => {
return web.request({
url: `/oss/oss/` + type + `/previewFile`,
method: "post",
params: {
userId: userId,
bucket: bucket,
filePath: filePath,
},
});
};
/**
* 重命名文件
* @param userId
* @param bucket
* @param type
* @param oldFileName
* @param newFileName
*/
export const renameFile = (
userId: any,
bucket: any,
type: any,
oldFileName: any,
newFileName: any,
) => {
return web.request({
url: `/oss/oss/` + type + `/renameFile`,
method: "post",
params: {
userId: userId,
bucket: bucket,
oldFileName: oldFileName,
newFileName: newFileName,
},
});
};
/**
* 拷贝文件
* @param userId
* @param bucket
* @param type
* @param oldFilePath
* @param newFilePath
*/
export const copyFile = (
userId: any,
bucket: any,
type: any,
oldFilePath: any,
newFilePath: any,
) => {
return web.request({
url: `/oss/oss/` + type + `/copyFile`,
method: "post",
params: {
userId: userId,
bucket: bucket,
oldFilePath: oldFilePath,
newFilePath: newFilePath,
},
});
};
/**
* 创建存储桶
* @param userId
* @param bucket
* @param type
*/
export const createBucket = (userId: any, bucket: any, type: any) => {
return web.request({
url: `/oss/oss/` + type + `/createBucket`,
method: "post",
params: {
userId: userId,
bucket: bucket,
},
});
};
/**
* 上传文件
* @param type
* @param data
* @param peram
*/
export const uploadFiles = (type: any, data: any, peram: any) => {
return web.request({
url: `/oss/oss/` + type + `/uploadFile`,
method: "post",
headers: {
"Content-Type": "multipart/form-data",
},
data: data,
params: peram,
});
};

View File

@@ -5,13 +5,15 @@ import web from "@/utils/axios/web.ts";
/** /**
* 初始化minio * 初始化minio
* @param data 用户id * @param data 用户id
* @param id
*/ */
export const initMinio = (data: any) => { export const initMinioOSS = (data: any, id: any) => {
return web.request({ return web.request({
url: "/oss/oss/minio/init", url: "/oss/oss/minio/init",
method: "post", method: "post",
params: { params: {
userId: data, userId: data,
Id: id,
}, },
}); });
}; };
@@ -33,15 +35,100 @@ export const getDirAndFileList = (userId: any, dirName: any, bucket: any) => {
}); });
}; };
/** /**
* 获取用户拥有的厂商 * 查询MinIO所有桶
* @param userId * @param userId
*/ */
export const getBrandsList = (userId: any) => { export const getAllMinioBucket = (userId: any) => {
return web.request({ return web.request({
url: "/oss/oss/minio/userId", url: `/oss/oss/minio/seleteBucket`,
method: "post",
params: {
userId: userId,
},
});
};
/**
* 获取MinIO配置信息
* @param userId
*/
export const getAllMinioConfig = (userId: any) => {
return web.request({
url: `/oss/oss/minio/return_online`,
method: "get", method: "get",
params: { params: {
userId: userId, userId: userId,
}, },
}); });
}; };
/**
* 新增阿里云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,
},
});
};

133
src/api/oss/qiniu/index.ts Normal file
View File

@@ -0,0 +1,133 @@
/** @format */
import web from "@/utils/axios/web.ts";
/**
* 初始化qiniu
* @param data 用户id
* @param id
*/
export const initQiniuOSS = (data: any, id: any) => {
return web.request({
url: "/oss/oss/qiniu/init",
method: "post",
params: {
userId: data,
Id: id,
},
});
};
/**
* 获取文件目录信息
* @param userId 用户id
* @param dirName 目录名称
* @param bucket 桶名称
*/
export const getDirAndFileList = (userId: any, dirName: any, bucket: any) => {
return web.request({
url: "/oss/oss/qiniu/listMinioDir",
method: "get",
params: {
userId: userId,
dirName: dirName,
bucket: bucket,
},
});
};
/**
* 查询qiniu所有桶
* @param userId
*/
export const getAllQiniuBucket = (userId: any) => {
return web.request({
url: `/oss/oss/qiniu/seleteBucket`,
method: "post",
params: {
userId: userId,
},
});
};
export const getAllQiniuConfigs = (userId: any) => {
return web.request({
url: `/oss/oss/qiniu/return_online`,
method: "get",
params: {
userId: userId,
},
});
};
/**
* 新增七牛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

@@ -0,0 +1,135 @@
/** @format */
import web from "@/utils/axios/web.ts";
/**
* 初始化Tencent oss
* @param data 用户id
* @param id
*/
export const initTencentOSS = (data: any, id: any) => {
return web.request({
url: "/oss/oss/tencent/init",
method: "post",
params: {
userId: data,
Id: id,
},
});
};
/**
* 获取文件目录信息
* @param userId 用户id
* @param dirName 目录名称
* @param bucket 桶名称
*/
export const getDirAndFileList = (userId: any, dirName: any, bucket: any) => {
return web.request({
url: "/oss/oss/tencent/listDir",
method: "get",
params: {
userId: userId,
dirName: dirName,
bucket: bucket,
},
});
};
/**
* 查询Tencent OSS所有桶
* @param userId
*/
export const getAllTencentOSsBucket = (userId: any) => {
return web.request({
url: `/oss/oss/tencent/seleteBucket`,
method: "post",
params: {
userId: userId,
},
});
};
/**
* 查询Tencent OSS所有配置
* @param userId
*/
export const getAllTencentOSsConfig = (userId: any) => {
return web.request({
url: `/oss/oss/tencent/return_online`,
method: "get",
params: {
userId: userId,
},
});
};
/**
* 新增腾讯云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,
},
});
};

47
src/api/oss/up/index.ts Normal file
View File

@@ -0,0 +1,47 @@
/** @format */
import web from "@/utils/axios/web.ts";
/**
* 初始化Up oss
* @param data 用户id
*/
export const initUpOSS = (data: any) => {
return web.request({
url: "/oss/oss/up/tencent",
method: "post",
params: {
userId: data,
},
});
};
/**
* 获取文件目录信息
* @param userId 用户id
* @param dirName 目录名称
* @param bucket 桶名称
*/
export const getDirAndFileList = (userId: any, dirName: any, bucket: any) => {
return web.request({
url: "/oss/oss/up/listDir",
method: "get",
params: {
userId: userId,
dirName: dirName,
bucket: bucket,
},
});
};
/**
* 查询Tencent OSS所有桶
* @param userId
*/
export const getAllUpOSsBucket = (userId: any) => {
return web.request({
url: `/oss/oss/up/seleteBucket`,
method: "post",
params: {
userId: userId,
},
});
};

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

@@ -0,0 +1,212 @@
/** @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: "get",
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
* @param userId
*/
export const listComment = (detailId: any, userId: any) => {
return web.request({
url: `/share/share/comment/reply/listcomment`,
method: "get",
params: {
detailId: detailId,
userId: userId,
},
});
};
/**
* 分享圈详情热门评论列表
* @param detailId
* @param userId
*/
export const listCommentHot = (detailId: any, userId: any) => {
return web.request({
url: `/share/share/comment/reply/listcommenthot`,
method: "get",
params: {
detailId: detailId,
userId: userId,
},
});
};
/**
* 分享圈详情回复列表
* @param commentId
* @param userId
*/
export const listReply = (commentId: any, userId: any) => {
return web.request({
url: `/share/share/comment/reply/listreply`,
method: "get",
params: {
commentId: commentId,
userId: userId,
},
});
};
/**
* 新增评论
* @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/detail/returncount`,
method: "get",
params: {
detailId: detailId,
},
});
};
/**
* 获取我的分享圈列表
* @param userId
*/
export const getMyShareList = (userId: any) => {
return web.request({
url: `/share/share/detail/mydetail`,
method: "get",
params: {
userId: userId,
},
});
};
/**
* 获取我的分享圈收藏
* @param userId
*/
export const getMyFavor = (userId: any) => {
return web.request({
url: `/share/share/user/favorites/myfavor`,
method: "get",
params: {
userId: userId,
},
});
};
/**
* 新增点赞
* @param data
*/
export const addLikeComment = (data: any) => {
return web.request({
url: `/share/share/comment/reply/addlike`,
method: "post",
data: data,
});
};
/**
* 删除点赞
* @param data
*/
export const deletedLikeComment = (data: any) => {
return web.request({
url: `/share/share/comment/reply/dellike`,
method: "post",
data: data,
});
};
/**
* 新增点赞
* @param data
*/
export const addLikeDetail = (data: any) => {
return web.request({
url: `/share/share/detail/addlike`,
method: "post",
data: data,
});
};
/**
* 删除点赞
* @param data
*/
export const deletedLikeDetail = (data: any) => {
return web.request({
url: `/share/share/detail/dellike`,
method: "post",
data: data,
});
};

View File

@@ -123,12 +123,101 @@ export const generateQRCode = (clientId: string) => {
}); });
}; };
/** /**
* 获取用户操蛋权限 * 获取用户菜单权限
* @param userId * @param userId
*/ */
export const getUserMenuPermission = (userId: string): any => { export const getUserMenuPermission = (userId: string): any => {
return web.request({ return web.request({
url: "/auth/permission/selectUserPermission", url: "/auth/auth/permission/selectUserPermission",
method: "get",
params: {
userId: userId,
},
});
};
/**
* 获取用户信息
* @param userId
*/
export const getUserInfoApi = (userId: string): any => {
return web.request({
url: "/auth/auth/user/getUserInfo",
method: "get",
params: {
userId: userId,
},
});
};
/**
* 更新用户信息
* @param data
*/
export const updateUserInfo = (data: any): any => {
return web.request({
url: "/auth/auth/user/update",
method: "post",
headers: {
"Content-Type": "application/json;charset=UTF-8",
},
data: data,
});
};
/**
* 新增用户收藏
* @param data
*/
export const addFavorites = (data: any): any => {
return web.request({
url: "/share/share/user/favorites/add",
method: "post",
data: data,
});
};
/**
* 取消用户收藏
* @param data
*/
export const deleteFavorites = (data: any): any => {
return web.request({
url: "/share/share/user/favorites/delete",
method: "post",
data: data,
});
};
/**
* 退出登录
* @param userId
*/
export const logout = (userId: any): any => {
return web.request({
url: "/auth/auth/user/logout",
method: "post",
params: {
userId: userId,
},
});
};
/**
* 检查用户名是否已存在
* @param userName
*/
export const checkUserName = (userName: any): any => {
return web.request({
url: "/auth/auth/user/checkUserName",
method: "post",
params: {
userName: userName,
},
});
};
/**
* 获取最近分享
* @param userId
*/
export const getRecentShare = (userId: any): any => {
return web.request({
url: "/share/share/detail/getRecentShare",
method: "get", method: "get",
params: { params: {
userId: userId, userId: userId,

View File

@@ -0,0 +1 @@
<svg t="1721106120251" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11406" width="200" height="200"><path d="M860 64H164c-55.2 0-100 44.8-100 100v696c0 55.2 44.8 100 100 100h696c55.2 0 100-44.8 100-100V164c0-55.2-44.8-100-100-100zM709.5 566.4L536.9 764.3c-13.2 15.1-36.6 15.1-49.8 0L314.5 566.4c-15.5-17.8-2.9-45.5 20.7-45.5h119.7V284.5c0-19.9 16.1-36.1 36.1-36.1h42c19.9 0 36.1 16.1 36.1 36.1v236.4h119.7c23.6-0.1 36.2 27.7 20.7 45.5z" fill="#40D786" p-id="11407"></path></svg>

After

Width:  |  Height:  |  Size: 527 B

View File

@@ -0,0 +1 @@
<svg t="1720834001446" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4550" width="200" height="200"><path d="M512 512.8704m-432.3328 0a432.3328 432.3328 0 1 0 864.6656 0 432.3328 432.3328 0 1 0-864.6656 0Z" fill="#F8A10F" p-id="4551"></path><path d="M287.0272 418.7136c-85.0432 0-160.4096 41.3696-207.104 105.0624 4.5568 182.7328 122.368 337.3056 285.952 396.032 103.2192-33.28 177.92-130.048 177.92-244.3776 0-141.7216-114.944-256.7168-256.768-256.7168z" fill="#FFBD48" p-id="4552"></path><path d="M573.184 251.4944L624.64 355.7888a45.8752 45.8752 0 0 0 34.5088 25.088l115.0976 16.7424c37.5808 5.4784 52.5824 51.6608 25.3952 78.1824l-83.3024 81.2032a45.78816 45.78816 0 0 0-13.1584 40.5504l19.6608 114.6368c6.4 37.4272-32.8704 65.9968-66.5088 48.3328l-102.9632-54.1184a45.8496 45.8496 0 0 0-42.6496 0l-102.9632 54.1184c-33.6384 17.664-72.9088-10.8544-66.5088-48.3328l19.6608-114.6368c2.56-14.848-2.3552-30.0544-13.1584-40.5504L264.4992 475.7504c-27.1872-26.5216-12.1856-72.704 25.3952-78.1824l115.0976-16.7424a45.63968 45.63968 0 0 0 34.5088-25.088L491.008 251.4944c16.7936-34.048 65.3824-34.048 82.176 0z" fill="#FFFFFF" p-id="4553"></path><path d="M510.7712 706.3552a45.8496 45.8496 0 0 1 42.6496 0l102.9632 54.1184c33.6384 17.664 72.9088-10.8544 66.5088-48.3328l-19.6608-114.6368c-2.56-14.848 2.3552-30.0544 13.1584-40.5504l-205.6192 149.4016z" fill="#FFEACC" p-id="4554"></path></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1 @@
<svg t="1720833921345" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1614" width="200" height="200"><path d="M511.4368 512m-432.3328 0a432.3328 432.3328 0 1 0 864.6656 0 432.3328 432.3328 0 1 0-864.6656 0Z" fill="#FF6161" p-id="1615"></path><path d="M286.464 417.8432c-85.0432 0-160.4096 41.3696-207.104 105.0624 4.5568 182.7328 122.368 337.3056 285.952 396.032 103.2192-33.28 177.92-130.048 177.92-244.3776 0-141.7728-114.944-256.7168-256.768-256.7168z" fill="#FF7D7D" p-id="1616"></path><path d="M342.6304 721.6128h6.7584V427.9808h-6.7584c-36.096 0-65.3824 29.2864-65.3824 65.3824v162.9184c0.0512 36.0448 29.2864 65.3312 65.3824 65.3312z" fill="#FFCAC7" p-id="1617"></path><path d="M710.656 390.9632h-136.8064c10.6496-34.9696 24.9344-106.8032-25.088-137.1648-23.5008-14.1824-78.8992-24.1664-77.568 37.9904 1.28 62.1568-2.2528 130.4064-81.152 136.192v293.632h280.2688c31.744 0 59.136-22.3232 65.4848-53.4528l40.2944-196.9152c8.5504-41.472-23.0912-80.2816-65.4336-80.2816z" fill="#FFFFFF" p-id="1618"></path></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1 @@
<svg t="1721105828851" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7265" width="200" height="200"><path d="M758.9376 951.4496H272.6912c-107.264 0-194.2016-86.9376-194.2016-194.2016V271.0016C78.4896 163.7376 165.4272 76.8 272.6912 76.8h486.2464c107.264 0 194.2016 86.9376 194.2016 194.2016v486.2464c0 107.264-86.9376 194.2016-194.2016 194.2016z" fill="#F78748" p-id="7266"></path><path d="M745.1136 444.0576c-14.5408-113.9712-111.6672-200.9088-228.4544-200.9088S302.6944 330.0352 288.2048 444.0576c-75.264 13.1584-131.84 78.9504-131.84 157.0304 0 87.9104 71.5264 159.4368 159.4368 159.4368h58.2656c13.6704 0 24.7296-11.0592 24.7296-24.7296s-11.0592-24.7296-24.7296-24.7296H315.8016c-60.6208 0-109.9776-49.3056-109.9776-109.9776 0-58.0608 45.3632-106.2912 103.2704-109.7216a28.62592 28.62592 0 0 0 26.9312-27.136c4.864-96.2048 84.224-171.5712 180.5824-171.5712s175.7184 75.3664 180.5824 171.6224c0.768 14.592 12.3392 26.2656 26.88 27.0848 57.9584 3.4304 103.3216 51.6608 103.3216 109.7216 0 60.6208-49.3056 109.9776-109.9776 109.9776h-54.9376c-13.6704 0-24.7296 11.0592-24.7296 24.7296s11.0592 24.7296 24.7296 24.7296h54.9376c87.9104 0 159.4368-71.5264 159.4368-159.4368 0.1536-78.1312-56.4736-143.872-131.7376-157.0304z" fill="#F7F8F8" p-id="7267"></path><path d="M532.3776 513.792a24.7296 24.7296 0 0 0-34.4576 0.512l-82.0224 81.8176a24.80128 24.80128 0 0 0-0.0512 35.0208c9.6256 9.6768 25.344 9.6768 35.0208 0.0512l42.24-42.1376v167.0656c0 13.6704 11.0592 24.7296 24.7296 24.7296s24.7296-11.0592 24.7296-24.7296v-164.6592l42.752 40.2432c4.7616 4.5056 10.8544 6.7072 16.9472 6.7072 6.6048 0 13.1584-2.6112 18.0224-7.7824 9.3696-9.9328 8.9088-25.6-1.0752-34.9696l-86.8352-81.8688z" fill="#F7F8F8" p-id="7268"></path></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 376 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -31,3 +31,10 @@ body {
background-image: url("@/assets/images/background.png"); background-image: url("@/assets/images/background.png");
background-size: cover; background-size: cover;
} }
.ant-pro-page-container-children-container-no-header {
padding-block-start: 10px !important;
}
.ant-pro-page-container-children-container {
padding-inline: 10px !important;
}

View File

@@ -1,24 +0,0 @@
/** @format */
import { Tree, TreeDataNode } from "antd";
import React from "react";
const { DirectoryTree } = Tree;
interface FileDirectoryTreeProps {
treeData: TreeDataNode[];
}
const FileDirectoryTree: React.FC<FileDirectoryTreeProps> = (props: any) => {
return (
<DirectoryTree
style={{ width: "20%", fontSize: "20px" }}
multiple={false}
defaultExpandAll
treeData={props.treeData}
blockNode={true}
showIcon={true}
{...props}
/>
);
};
export default FileDirectoryTree;

View File

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

View File

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

View File

@@ -1,16 +1,12 @@
/** @format */ /** @format */
import { Avatar, Button, message } from "antd"; import { Avatar, Button, message, Skeleton } from "antd";
import { import { DrawerForm, ProCard, ProForm, ProFormText } from "@ant-design/pro-components";
DrawerForm, import React, { createContext, useContext, useEffect, useState } from "react";
ProCard, import { EditOutlined, EllipsisOutlined, ReloadOutlined, SettingOutlined } from "@ant-design/icons";
ProForm, import bucket from "@/assets/icons/bucket.svg";
ProFormText,
} from "@ant-design/pro-components";
import React, { createContext, useContext, useState } from "react";
import { EditOutlined, EllipsisOutlined, SettingOutlined } from "@ant-design/icons";
import bucket from "../../../../assets/icons/bucket.svg";
import styles from "./index.module.less"; import styles from "./index.module.less";
import { getAllQiniuBucket } from "@/api/oss/qiniu";
const DrawerContext = createContext<{ const DrawerContext = createContext<{
drawerVisit: boolean; drawerVisit: boolean;
@@ -26,24 +22,20 @@ const QingyunDrawer = () => {
}; };
const ProCardComponent = () => { const ProCardComponent = () => {
const { drawerVisit, setDrawerVisit } = useContext(DrawerContext); const { drawerVisit, setDrawerVisit } = useContext(DrawerContext);
const list = [ const [buckets, setBuckets] = useState<any>([]);
"test1", const [loading, setLoading] = useState<boolean>(true);
"test2",
"test3", async function getAllBucket() {
"test1", getAllQiniuBucket("1").then((res: any) => {
"test2", if (res && res.success) {
"test3", setBuckets(res.data);
"test1", setLoading(false);
"test2", }
"test3", });
"test1", }
"test2", useEffect(() => {
"test3", getAllBucket().then();
"test3", }, []);
"test1",
"test2",
"test3",
];
const bucketList = () => { const bucketList = () => {
return ( return (
@@ -51,33 +43,53 @@ const ProCardComponent = () => {
title={"存储桶列表"} title={"存储桶列表"}
headerBordered headerBordered
extra={ extra={
<>
<Button
type="default"
shape={"circle"}
style={{ marginRight: 20 }}
onClick={() => {
setLoading(false);
getAllBucket().then();
}}
icon={<ReloadOutlined />}></Button>
<Button type="primary" onClick={() => setDrawerVisit(true)}> <Button type="primary" onClick={() => setDrawerVisit(true)}>
</Button> </Button>
</>
}> }>
<div className={styles.div_proCard}> <div className={styles.div_proCard}>
{list.map((item, index) => { <Skeleton loading={loading} paragraph={{ rows: 13 }} active>
{buckets &&
Array.from(buckets).map((item: any, index: number) => {
return ( return (
<div key={index}>
<ProCard <ProCard
headStyle={{ backgroundColor: "#f0f2f5" }} headStyle={{ backgroundColor: "#f0f2f5" }}
hoverable hoverable
bordered bordered
key={index} title={<b>{item.name}</b>}
title={<b>{item}</b>}
style={{ style={{
width: "180px", width: "180px",
borderWidth: "1px", height: "150px",
marginLeft: 10,
}} }}
actions={[ actions={[
<SettingOutlined key="setting" />, <SettingOutlined key="setting" />,
<EditOutlined key="edit" />, <EditOutlined key="edit" />,
<EllipsisOutlined key="ellipsis" />, <EllipsisOutlined key="ellipsis" />,
]}> ]}>
<Avatar shape={"square"} src={bucket} size={"large"} /> <Avatar
<b style={{ marginLeft: 10 }}> size</b> shape={"square"}
src={bucket as any}
size={"large"}
/>
<b style={{ marginLeft: 10 }}>{item.size}</b>
</ProCard> </ProCard>
</div>
); );
})} })}
</Skeleton>
</div> </div>
</ProCard> </ProCard>
); );

View File

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

View File

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

View File

@@ -1,16 +1,12 @@
/** @format */ /** @format */
import { Avatar, Button, message } from "antd"; import { Avatar, Button, message, Skeleton } from "antd";
import { import { DrawerForm, ProCard, ProForm, ProFormText } from "@ant-design/pro-components";
DrawerForm, import React, { createContext, useContext, useEffect, useState } from "react";
ProCard, import { EditOutlined, EllipsisOutlined, ReloadOutlined, SettingOutlined } from "@ant-design/icons";
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 bucket from "../../../../assets/icons/bucket.svg";
import styles from "./index.module.less"; import styles from "./index.module.less";
import { getAllQiniuBucket } from "@/api/oss/qiniu";
const DrawerContext = createContext<{ const DrawerContext = createContext<{
drawerVisit: boolean; drawerVisit: boolean;
@@ -26,24 +22,20 @@ const UpDrawer = () => {
}; };
const ProCardComponent = () => { const ProCardComponent = () => {
const { drawerVisit, setDrawerVisit } = useContext(DrawerContext); const { drawerVisit, setDrawerVisit } = useContext(DrawerContext);
const list = [ const [buckets, setBuckets] = useState<any>([]);
"test1", const [loading, setLoading] = useState<boolean>(true);
"test2",
"test3", async function getAllBucket() {
"test1", getAllQiniuBucket("1").then((res: any) => {
"test2", if (res && res.success) {
"test3", setBuckets(res.data);
"test1", setLoading(false);
"test2", }
"test3", });
"test1", }
"test2", useEffect(() => {
"test3", getAllBucket().then();
"test3", }, []);
"test1",
"test2",
"test3",
];
const bucketList = () => { const bucketList = () => {
return ( return (
@@ -51,33 +43,53 @@ const ProCardComponent = () => {
title={"存储桶列表"} title={"存储桶列表"}
headerBordered headerBordered
extra={ extra={
<>
<Button
type="default"
shape={"circle"}
style={{ marginRight: 20 }}
onClick={() => {
setLoading(false);
getAllBucket().then();
}}
icon={<ReloadOutlined />}></Button>
<Button type="primary" onClick={() => setDrawerVisit(true)}> <Button type="primary" onClick={() => setDrawerVisit(true)}>
</Button> </Button>
</>
}> }>
<div className={styles.div_proCard}> <div className={styles.div_proCard}>
{list.map((item, index) => { <Skeleton loading={loading} paragraph={{ rows: 13 }} active>
{buckets &&
Array.from(buckets).map((item: any, index: number) => {
return ( return (
<div key={index}>
<ProCard <ProCard
headStyle={{ backgroundColor: "#f0f2f5" }} headStyle={{ backgroundColor: "#f0f2f5" }}
hoverable hoverable
bordered bordered
key={index} title={<b>{item.name}</b>}
title={<b>{item}</b>}
style={{ style={{
width: "180px", width: "180px",
borderWidth: "1px", height: "150px",
marginLeft: 10,
}} }}
actions={[ actions={[
<SettingOutlined key="setting" />, <SettingOutlined key="setting" />,
<EditOutlined key="edit" />, <EditOutlined key="edit" />,
<EllipsisOutlined key="ellipsis" />, <EllipsisOutlined key="ellipsis" />,
]}> ]}>
<Avatar shape={"square"} src={bucket} size={"large"} /> <Avatar
<b style={{ marginLeft: 10 }}> size</b> shape={"square"}
src={bucket as any}
size={"large"}
/>
<b style={{ marginLeft: 10 }}>{item.size}</b>
</ProCard> </ProCard>
</div>
); );
})} })}
</Skeleton>
</div> </div>
</ProCard> </ProCard>
); );

View File

@@ -1,19 +1,12 @@
.div_proCard{ .div_proCard{
height: 62vh; width: 100%;
max-height: 62vh; min-height: 62vh;
gap: 20px;
width: 105%;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
flex-direction: row; flex-direction: row;
overflow-y:scroll; overflow-y:scroll;
overflow-x: hidden; overflow-x: hidden;
justify-content: space-around; justify-content: flex-start;
} }
.proCard{
height: 75vh;
max-height: 75vh;
width: 105%;
}

View File

@@ -1,13 +1,9 @@
.proCard{ .proCard{
max-width: 30%; max-width: 30%;
height: 75vh; min-height: 84vh;
max-height: 75vh;
} }
.proCardBucket{ .proCardBucket{
height: 75vh;
max-height: 75vh;
width: 100%; width: 100%;
} }
@@ -18,11 +14,6 @@
} }
.div_checkCardArea{ .div_checkCardArea{
height: 65vh;
max-height: 65vh;
overflow-y:scroll; overflow-y:scroll;
overflow-x: hidden; overflow-x: hidden;
} }
//.div_checkCardArea::-webkit-scrollbar {
//
//}

View File

@@ -1,16 +1,33 @@
/** @format */ /** @format */
import { FunctionComponent, Suspense } from "react"; import { FunctionComponent, Suspense, useEffect, useState } from "react";
import { CheckCard, ProCard } from "@ant-design/pro-components"; import { CheckCard, ProCard } from "@ant-design/pro-components";
import styles from "./index.module.less"; import styles from "./index.module.less";
import { Outlet, useNavigate } from "react-router-dom"; import { Outlet, useNavigate } from "react-router-dom";
import { Button, Empty } from "antd"; import { Button, Empty, Skeleton } from "antd";
import StorageIcon from "@/context/stroage-icon.ts"; import StorageIcon from "@/constant/stroage-icon.ts";
import { ReloadOutlined } from "@ant-design/icons"; import { ReloadOutlined } from "@ant-design/icons";
import { getAllStorage } from "@/api/oss";
import useStore from "@/utils/store/useStore.tsx";
import { observer } from "mobx-react";
const Bucket: FunctionComponent = () => { const Bucket: FunctionComponent = observer(() => {
const navigate = useNavigate(); const navigate = useNavigate();
const checkList = ["minio", "ali", "tencent", "huawei", "baidu", "jd"]; const [userStorage, setUserStorage] = useState<any>([]);
const [loading, setLoading] = useState<boolean>(true);
const userStore = useStore("user");
const userId: any = userStore.getUserId();
async function getUserStorage() {
getAllStorage(userId).then((res: any) => {
if (res && res.success) {
setUserStorage(res.data);
setLoading(false);
}
});
}
useEffect(() => {
getUserStorage().then();
}, []);
return ( return (
<div className={styles.div_proCard}> <div className={styles.div_proCard}>
<ProCard <ProCard
@@ -18,8 +35,10 @@ const Bucket: FunctionComponent = () => {
<Button <Button
type="primary" type="primary"
shape={"circle"} shape={"circle"}
icon={<ReloadOutlined />} onClick={() => {
onClick={() => navigate("/main/bucket/add")}></Button> getUserStorage().then();
}}
icon={<ReloadOutlined />}></Button>
} }
title="存储商" title="存储商"
headerBordered headerBordered
@@ -27,29 +46,25 @@ const Bucket: FunctionComponent = () => {
boxShadow={true} boxShadow={true}
colSpan={"100%"} colSpan={"100%"}
bordered> bordered>
<CheckCard.Group <Skeleton loading={loading} paragraph={{ rows: 14 }} active>
onChange={(value) => { <CheckCard.Group style={{ width: "100%" }}>
console.log("value", value); {userStorage.map((item: any, index: number) => {
}}>
<div className={styles.div_checkCardArea}>
{checkList.map((item) => {
return ( return (
<CheckCard <CheckCard
key={item} key={index}
avatar={StorageIcon[item]} avatar={StorageIcon[item.ossType]}
style={{ width: "100%" }} style={{ width: "100%" }}
title={item} title={item.name}
description="点击查看" description="点击查看"
value={item} value={item.ossType}
onChange={() => { onChange={() => {
console.log(`/main/bucket/${item}`); navigate(`/main/bucket/${item.ossType}`);
navigate(`/main/bucket/${item}`);
}} }}
/> />
); );
})} })}
</div>
</CheckCard.Group> </CheckCard.Group>
</Skeleton>
</ProCard> </ProCard>
<ProCard className={styles.proCardBucket} bordered boxShadow> <ProCard className={styles.proCardBucket} bordered boxShadow>
{location.pathname === "/main/bucket" || location.pathname === "/main/bucket/" ? ( {location.pathname === "/main/bucket" || location.pathname === "/main/bucket/" ? (
@@ -67,5 +82,5 @@ const Bucket: FunctionComponent = () => {
</ProCard> </ProCard>
</div> </div>
); );
}; });
export default Bucket; export default Bucket;

View File

@@ -0,0 +1,265 @@
/** @format */
import { useState } from "react";
import { Avatar, Card, Flex, Input, message, Select, Upload } from "antd";
import { CloudUploadOutlined } from "@ant-design/icons";
import { ProCard } from "@ant-design/pro-components";
import StorageIcon from "@/constant/stroage-icon.ts";
import { getStorageBuckets, uploadFiles } from "@/api/oss";
import useStore from "@/utils/store/useStore.tsx";
const { Dragger } = Upload;
const FileUpload = (props: any) => {
const [buckets, setBuckets] = useState<any[]>([]);
const store = useStore("file");
const [defaultFileList, setDefaultFileList] = useState([]);
const userStore = useStore("user");
const userId: any = userStore.getUserId();
async function getBuckets(type: any) {
getStorageBuckets(userId, type).then((res: any) => {
if (res && res.success) {
setBuckets(res.data);
}
});
}
const handleOnChange = ({ fileList }: { fileList: any }) => {
setDefaultFileList(fileList);
};
const uploadFile = async (options: any) => {
const { onSuccess, onError, file }: { onSuccess: any; onError: any; file: any } = options;
if (store.getUploadFileBucket() === null || store.getUploadFileStorage() === null) {
message.open({
content: "请选择存储桶和存储路径",
type: "error",
});
return;
}
const fileData = new FormData();
fileData.append("file", file);
const formData: any = {
userId: userId,
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"}>
<ProCard bordered={false} boxShadow={false}>
<Flex vertical={false}>
<Select
size="large"
status="warning"
style={{
width: "30%",
display: "flex",
flexDirection: "row",
alignItems: "center",
}}
showSearch={true}
allowClear={true}
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 (
<>
<Card
bordered={false}
style={{
height: 35,
display: "flex",
alignItems: "center",
flexDirection: "row",
}}
size={"small"}>
<Avatar src={StorageIcon[label.value]} size={"small"} />
<span
style={{
marginLeft: "10px",
fontWeight: "bolder",
}}>
{label.label}
</span>
</Card>
</>
);
}}
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"
style={{
width: "30%",
display: "flex",
flexDirection: "row",
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={"未找到,请先配置存储商"}
fieldNames={{
label: "name",
value: "name",
}}
labelRender={(label: any) => {
return (
<>
<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={{
height: 35,
display: "flex",
alignItems: "center",
flexDirection: "row",
}}
size={"small"}>
<Avatar
src={StorageIcon["bucket"]}
size={"small"}
shape={"square"}
/>{" "}
<span
style={{
marginLeft: "10px",
fontWeight: "bolder",
}}>
{item.label}
</span>
</Card>
</>
);
}}
placeholder={"请选择存储桶"}></Select>
<Input
placeholder={"请输入路径/默认当前打开的路径"}
onChange={(e: any) => {
store.setUploadFilePath(e.target.value);
}}
style={{ marginLeft: 10, width: "40%" }}></Input>
</Flex>
</ProCard>
</Flex>
<ProCard>
<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>
</Dragger>
</ProCard>
</>
);
};
export default FileUpload;

View File

@@ -17,7 +17,7 @@
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
flex-direction: row; flex-direction: row;
margin-top: 20px; margin-top: 10px;
.file_card:hover { .file_card:hover {
background-color: rgba(16, 15, 15, 0.08); background-color: rgba(16, 15, 15, 0.08);
@@ -29,8 +29,7 @@
.file_right_header { .file_right_header {
display: flex; display: flex;
flex-direction: row; flex-direction: column;
align-items: center;
} }
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -4,6 +4,7 @@
} }
.home_content_head { .home_content_head {
width: 100%;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
@@ -13,7 +14,7 @@
} }
.home_content_head_left { .home_content_head_left {
height: 51vh; height: 50vh;
width: 43%; width: 43%;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@@ -30,3 +31,12 @@
margin-top: 30px; margin-top: 30px;
} }
.home_content_icon{
font-size: 20px;
color: lightgrey;
}
.home_content_icon:hover{
color: #ff6700;
}

View File

@@ -1,7 +1,6 @@
/** @format */ /** @format */
import { FunctionComponent, useEffect, useState } from "react"; import React, { memo, useEffect, useState } from "react";
import { Avatar, Card, Skeleton, Space, Tag } from "antd"; import { Avatar, Card, Flex, message, Skeleton, Tooltip } from "antd";
import Meta from "antd/es/card/Meta";
import styles from "./index.module.less"; import styles from "./index.module.less";
import ReactECharts from "echarts-for-react"; import ReactECharts from "echarts-for-react";
import { ProCard, ProList } from "@ant-design/pro-components"; import { ProCard, ProList } from "@ant-design/pro-components";
@@ -9,48 +8,136 @@ import storage from "@/assets/icons/storage.svg";
import bucket from "@/assets/icons/bucket.svg"; import bucket from "@/assets/icons/bucket.svg";
import file from "@/assets/icons/file.svg"; import file from "@/assets/icons/file.svg";
import flux from "@/assets/icons/flux.svg"; import flux from "@/assets/icons/flux.svg";
import aliyun from "@/assets/icons/aliyun.svg";
// import * as echarts from "echarts/core";
import CalendarHeatmap from "react-calendar-heatmap"; import CalendarHeatmap from "react-calendar-heatmap";
import "react-calendar-heatmap/dist/styles.css"; import "react-calendar-heatmap/dist/styles.css";
const defaultData = [ import upload from "@/assets/icons/upload.svg";
{ import download from "@/assets/icons/download.svg";
id: "1", import {
name: "语雀的天空", getStorageAndBuckets,
image: aliyun, getUserDownloadFileDiagramPerMonth,
desc: "我是一条测试的描述", getUserFileCount,
}, getUserFileFlow,
{ getUserFileHeatMap,
id: "2", getUserRecentPreviewFile,
name: "Ant Design", getUserRecentUploadFile,
image: "https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg", getUserUploadFileDiagramPerMonth,
desc: "我是一条测试的描述", } from "@/api/oss";
}, import { useNavigate } from "react-router-dom";
{ import { InfoCircleOutlined } from "@ant-design/icons";
id: "3", import FileIcon from "@/constant/file-icon.ts";
name: "蚂蚁金服体验科技", import file_icon from "@/assets/icons/files/file.svg";
image: "https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg", import { observer } from "mobx-react";
desc: "我是一条测试的描述", import useStore from "@/utils/store/useStore.tsx";
},
{
id: "4",
name: "TechUI",
image: "https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg",
desc: "我是一条测试的描述",
},
];
type DataItem = (typeof defaultData)[number]; const MainHome: React.FC = observer(() => {
const MainHome: FunctionComponent = () => { const navigate = useNavigate();
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [dataSource, setDataSource] = useState<DataItem[]>(defaultData); const [loadingEChart, setLoadingEChart] = useState(true);
const [loadingHeatmap, setLoadingHeatmap] = useState(true);
const [loadingRecentFile, setLoadingRecentFile] = useState(true);
const [loadingPreviewFile, setLoadingPreviewFile] = useState(true);
const [storageCount, setStorageCount] = useState<any>(null);
const [bucketCount, setBucketCount] = useState<any>(null);
const [fileHeatmap, setFileHeatmap] = useState<any>([]);
const [downloadFlow, setDownloadFlow] = useState<any>(null);
const [uploadFlow, setUploadFlow] = useState<any>(null);
const [uploadFile, setUploadFile] = useState<any>(null);
const [downloadFile, setDownloadFile] = useState<any>(null);
const [monthUpload, setMonthUpload] = useState<any>([]);
const [monthDownload, setMonthDownload] = useState<any>([]);
const [recentUploadFile, setRecentUploadFile] = useState<any>([]);
// const [recentDownloadFile, setRecentDownloadFile] = useState<any>([]);
const [recentPreviewFile, setRecentPreviewFile] = useState<any>([]);
const userStore = useStore("user");
const userId: any = userStore.getUserId();
// 获取存储同和存储商的个数
async function getStorageAndBucketsCount() {
return getStorageAndBuckets(userId).then((res: any) => {
if (res && res.success && res.data) {
const from: any = Array.from(res.data);
setStorageCount(from.length);
const count = from.reduce(
(count: any, item: any) => (count += item.bucketCount),
0,
);
setBucketCount(count);
}
});
}
// 获取文件上传热力图
async function getFileHeatMap() {
return getUserFileHeatMap(userId).then((res: any) => {
if (res && res.success && res.data) {
setFileHeatmap(res.data.upload);
}
});
}
// 获取文件上传下载流量
async function getUploadDownloadFlux() {
return getUserFileFlow(userId).then((res: any) => {
if (res && res.success && res.data) {
setUploadFlow(res.data.uploadflow);
setDownloadFlow(res.data.downloadflow);
}
});
}
// 获取文件上传下载个数
async function getUploadDownloadCount() {
return getUserFileCount(userId).then((res: any) => {
if (res && res.success && res.data) {
setUploadFile(res.data.upload);
setDownloadFile(res.data.download);
}
});
}
// 获取每月用户下载数量
async function getDownloadCountByMonth() {
getUserDownloadFileDiagramPerMonth(userId).then((res: any) => {
if (res && res.success && res.data) {
setMonthDownload(res.data);
}
});
}
// 获取每月用户上传数量
async function getUploadCountByMonth() {
getUserUploadFileDiagramPerMonth(userId).then((res: any) => {
if (res && res.success && res.data) {
setMonthUpload(res.data);
}
});
}
// 获取用户最近上传文件
async function getRecentUploadFile() {
getUserRecentUploadFile(userId).then((res: any) => {
if (res && res.success && res.data) {
setRecentUploadFile(res.data);
}
});
}
//获取用户最近下载文件
// async function getRecentDownloadFile() {
// getUserRecentDownloadFile(userId).then((res: any) => {
// if (res && res.success && res.data) {
// setRecentDownloadFile(res.data);
// }
// });
// }
//获取用户最近预览文件
async function getRecentPreviewFile() {
getUserRecentPreviewFile(userId).then((res: any) => {
if (res && res.success && res.data) {
setRecentPreviewFile(res.data);
}
});
}
const option = { const option = {
tooltip: { tooltip: {
trigger: "axis", trigger: "axis",
}, },
legend: { legend: {
color: ["#F58080", "#47D8BE", "#F9A589"], color: ["#F58080", "#47D8BE", "#F9A589"],
data: ["文件", "流量", "存储桶"], data: ["上传", "下载"],
left: "center", left: "center",
bottom: "bottom", bottom: "bottom",
}, },
@@ -62,15 +149,17 @@ const MainHome: FunctionComponent = () => {
height: "80%", height: "80%",
containLabel: true, containLabel: true,
}, },
xAxis: { xAxis: [
type: "category", {
data: [100, 200, 20, 30, 60, 89], type: "time",
// data: month,
axisLine: { axisLine: {
lineStyle: { lineStyle: {
color: "#999", color: "#999",
}, },
}, },
}, },
],
yAxis: { yAxis: {
type: "value", type: "value",
@@ -95,9 +184,9 @@ const MainHome: FunctionComponent = () => {
}, },
series: [ series: [
{ {
name: "文件", name: "下载",
type: "line", type: "line",
data: [800, 900, 220, 130, 660, 289], data: monthDownload,
color: "#F58080", color: "#F58080",
lineStyle: { lineStyle: {
width: 5, width: 5,
@@ -134,9 +223,9 @@ const MainHome: FunctionComponent = () => {
smooth: true, smooth: true,
}, },
{ {
name: "流量", name: "上传",
type: "line", type: "line",
data: [123, 568, 111, 222, 123, 56], data: monthUpload,
lineStyle: { lineStyle: {
width: 5, width: 5,
color: { color: {
@@ -171,113 +260,309 @@ const MainHome: FunctionComponent = () => {
}, },
smooth: true, smooth: true,
}, },
{
name: "存储桶",
type: "line",
data: [125, 568, 25, 36, 784, 56],
lineStyle: {
width: 5,
color: {
type: "linear",
colorStops: [
{
offset: 0,
color: "#F6D06F", // 0% 处的颜色
},
{
offset: 0.4,
color: "#F9A589", // 100% 处的颜色
},
{
offset: 1,
color: "#F9A589", // 100% 处的颜色
},
],
globalCoord: false, // 缺省为 false
},
shadowColor: "rgba(249,165,137, 0.5)",
shadowBlur: 10,
shadowOffsetY: 7,
},
itemStyle: {
color: "#F6D06F",
borderWidth: 10,
/*shadowColor: 'rgba(72,216,191, 0.3)',
shadowBlur: 100,*/
borderColor: "#F6D06F",
},
smooth: true,
},
], ],
}; };
useEffect(() => { useEffect(() => {
setTimeout(() => { getStorageAndBucketsCount().then(() => {
getUploadDownloadFlux().then(() => {
getUploadDownloadCount().then(() => {
setLoading(false); setLoading(false);
}, 2000); });
});
});
}, []); }, []);
useEffect(() => {
getFileHeatMap().then(() => {
setLoadingHeatmap(false);
});
}, []);
useEffect(() => {
getDownloadCountByMonth().then(() => {
getUploadCountByMonth().then(() => {
setLoadingEChart(false);
});
});
}, []);
useEffect(() => {
getRecentUploadFile().then(() => {
setLoadingRecentFile(false);
});
getRecentPreviewFile().then(() => {
setLoadingPreviewFile(false);
});
}, []);
function getFileExtension(filename: any): string {
const dotIndex = filename.lastIndexOf(".");
return dotIndex !== -1 ? filename.slice(dotIndex + 1) : "";
}
return ( return (
<> <>
<div className={styles.home_content_main}> <div className={styles.home_content_main}>
<div className={styles.home_content_head}> <div className={styles.home_content_head}>
<div className={styles.home_content_head_left}> <div className={styles.home_content_head_left}>
<Card <Card
style={{ width: "13.5vw", height: "21vh" }} style={{ width: "200px", height: "150px" }}
loading={false} loading={false}
bordered bordered
hoverable> hoverable>
<Skeleton loading={loading} active avatar> <Skeleton loading={loading} active avatar>
<Meta <Flex vertical={true}>
avatar={<Avatar shape="square" size="large" src={storage} />} <Flex
title="存储" vertical={false}
description="This is the description" align={"center"}
justify={"space-between"}>
<Flex vertical={false} align={"center"}>
<Avatar
src={storage as any}
shape={"square"}
size={"large"}></Avatar>
<span
style={{
marginLeft: 10,
fontSize: 18,
fontWeight: "bolder",
}}>
{" "}
</span>
</Flex>
<Flex vertical={false} align={"center"}>
<span style={{ fontSize: 15 }}>
{
<span
style={{
fontSize: 30,
color: "coral",
fontWeight: "bolder",
}}>
{storageCount || 0}
</span>
}
/
</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}
/> />
</Tooltip>
</Flex>
</Flex>
</Skeleton> </Skeleton>
</Card> </Card>
<Card <Card
style={{ width: "13.5vw", height: "21vh" }} style={{ width: "200px", height: "150px" }}
loading={false} loading={false}
bordered bordered
hoverable> hoverable>
<Skeleton loading={loading} active avatar> <Skeleton loading={loading} active avatar>
<Meta <Flex vertical={true}>
avatar={<Avatar shape="square" size="large" src={bucket} />} <Flex
title="存储桶" vertical={false}
description="This is the description" align={"center"}
justify={"space-between"}>
<Flex vertical={false} align={"center"}>
<Avatar
src={bucket as any}
shape={"square"}
size={"large"}></Avatar>
<span
style={{
marginLeft: 10,
fontSize: 18,
fontWeight: "bolder",
}}>
{" "}
</span>
</Flex>
<Flex vertical={false} align={"center"}>
<span style={{ fontSize: 15 }}>
{
<span
style={{
fontSize: 30,
color: "lightblue",
fontWeight: "bolder",
}}>
{bucketCount || 0}
</span>
}
/
</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}
/> />
</Tooltip>
</Flex>
</Flex>
</Skeleton> </Skeleton>
</Card> </Card>
<Card <Card
style={{ width: "13.5vw", height: "21vh" }} style={{ width: "200px", height: "150px" }}
loading={false} loading={false}
bordered bordered
hoverable> hoverable>
<Skeleton loading={loading} active avatar> <Skeleton loading={loading} active avatar>
<Meta <Flex vertical={true}>
avatar={<Avatar shape="square" size="large" src={file} />} <Flex
title="文件" vertical={false}
description="This is the description" align={"center"}
/> justify={"space-between"}>
<Flex vertical={false} align={"center"}>
<Avatar
src={file as any}
shape={"square"}
size={"large"}></Avatar>
<span
style={{
marginLeft: 10,
fontSize: 18,
fontWeight: "bolder",
}}>
{" "}
</span>
</Flex>
</Flex>
<Flex vertical={true} style={{ marginLeft: 8, marginTop: 10 }}>
<Flex vertical={false} align={"center"}>
<Avatar
src={upload as any}
shape={"square"}
size={"small"}>
{" "}
</Avatar>
<span
style={{
fontSize: 18,
color: "orange",
marginLeft: 5,
}}>
{uploadFile || 0}
</span>
</Flex>
<Flex vertical={false} align={"center"}>
<Avatar
src={download as any}
shape={"square"}
size={"small"}>
{" "}
</Avatar>
<span
style={{
fontSize: 18,
color: "green",
marginLeft: 5,
}}>
{downloadFile || 0}
</span>
</Flex>
</Flex>
</Flex>
</Skeleton> </Skeleton>
</Card> </Card>
<Card <Card
style={{ width: "13.5vw", height: "21vh" }} style={{ width: "200px", height: "150px" }}
loading={false} loading={false}
bordered bordered
hoverable> hoverable>
<Skeleton loading={loading} active avatar> <Skeleton loading={loading} active avatar>
<Meta <Flex vertical={true}>
avatar={<Avatar shape="square" size="large" src={flux} />} <Flex
title="总量" vertical={false}
description="This is the description" align={"center"}
/> justify={"space-between"}>
<Flex vertical={false} align={"center"}>
<Avatar
src={flux as any}
shape={"square"}
size={"large"}></Avatar>
<span
style={{
marginLeft: 10,
fontSize: 18,
fontWeight: "bolder",
}}>
{" "}
</span>
</Flex>
</Flex>
<Flex vertical={true} style={{ marginLeft: 8, marginTop: 10 }}>
<Flex vertical={false} align={"center"}>
<Avatar
src={upload as any}
shape={"square"}
size={"small"}>
{" "}
</Avatar>
<span
style={{
fontSize: 18,
color: "orange",
marginLeft: 5,
}}>
{uploadFlow || 0}
</span>
</Flex>
<Flex vertical={false} align={"center"}>
<Avatar
src={download as any}
shape={"square"}
size={"small"}>
{" "}
</Avatar>
<span
style={{
fontSize: 18,
color: "green",
marginLeft: 5,
}}>
{downloadFlow || 0}
</span>
</Flex>
</Flex>
</Flex>
</Skeleton> </Skeleton>
</Card> </Card>
</div> </div>
<div style={{ width: "55%" }}> <div style={{ width: "55%" }}>
<ProCard bordered boxShadow> <ProCard bordered boxShadow>
<Skeleton loading={loading} active> <Skeleton
loading={loadingEChart}
active
paragraph={{
rows: 7,
}}>
<ReactECharts option={option} /> <ReactECharts option={option} />
</Skeleton> </Skeleton>
</ProCard> </ProCard>
@@ -291,12 +576,40 @@ const MainHome: FunctionComponent = () => {
marginTop: "30px", marginTop: "30px",
}} }}
title={"文件上传热力图"}> title={"文件上传热力图"}>
<Skeleton
loading={loadingHeatmap}
active
paragraph={{
rows: 3,
}}>
<CalendarHeatmap <CalendarHeatmap
startDate={new Date("2024-01-01")} startDate={new Date(`${new Date().getFullYear()}-01-01`)}
endDate={new Date("2024-12-31")} endDate={new Date(`${new Date().getFullYear()}-12-31`)}
showMonthLabels={true} showMonthLabels={true}
horizontal={true} horizontal={true}
showWeekdayLabels={false} 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();
}
}}
monthLabels={[ monthLabels={[
"一月", "一月",
"二月", "二月",
@@ -311,93 +624,138 @@ const MainHome: FunctionComponent = () => {
"十一月", "十一月",
"十二月", "十二月",
]} ]}
weekdayLabels={["周日", "周一", "周二", "周三", "周四", "周五", "周六"]} weekdayLabels={[
"周日",
"周一",
"周二",
"周三",
"周四",
"周五",
"周六",
]}
gutterSize={3} gutterSize={3}
values={[ values={fileHeatmap}></CalendarHeatmap>
{ date: "2024-01-02", count: 12 }, </Skeleton>
{ date: "2024-05-3", count: 122 },
{ date: "2024-06-30", count: 38 },
]}></CalendarHeatmap>
</ProCard> </ProCard>
</div> </div>
<div className={styles.home_content_list}> <div className={styles.home_content_list}>
<ProCard bordered boxShadow style={{ width: "49%" }}> <ProCard bordered boxShadow style={{ width: "49%" }}>
<ProList<DataItem> <Skeleton
loading={loadingRecentFile}
active
paragraph={{
rows: 8,
}}>
<ProList
rowKey="id" rowKey="id"
headerTitle="最近上传文件" headerTitle="最近上传文件"
dataSource={dataSource} dataSource={recentUploadFile}
showActions="hover"
editable={{
onSave: async (key, record, originRow) => {
console.log(key, record, originRow);
return true;
},
}}
onDataSourceChange={setDataSource}
metas={{ metas={{
title: { title: {
dataIndex: "name", dataIndex: "fileName",
}, },
avatar: { avatar: {
dataIndex: "image", render: (_: any, record: any) => {
editable: false, 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: { description: {
dataIndex: "desc", dataIndex: "size",
}, valueType: "text",
subTitle: {
render: () => {
return (
<Space size={0}>
<Tag color="blue">Ant Design</Tag>
<Tag color="#5BD8A6">TechUI</Tag>
</Space>
);
}, },
content: {
dataIndex: "time",
}, },
}} }}
/> />
</Skeleton>
</ProCard> </ProCard>
<ProCard bordered boxShadow style={{ width: "49%" }}> <ProCard bordered boxShadow style={{ width: "49%" }}>
<ProList<DataItem> <Skeleton
loading={loadingPreviewFile}
active
paragraph={{
rows: 8,
}}>
<ProList
rowKey="id" rowKey="id"
headerTitle="最近浏览文件" headerTitle="最近浏览文件"
dataSource={dataSource} dataSource={recentPreviewFile}
showActions="hover"
editable={{
onSave: async (key, record, originRow) => {
console.log(key, record, originRow);
return true;
},
}}
onDataSourceChange={setDataSource}
metas={{ metas={{
title: { title: {
dataIndex: "name", dataIndex: "fileName",
}, },
avatar: { avatar: {
dataIndex: "image", render: (_: any, record: any) => {
editable: false, 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: { description: {
dataIndex: "desc", dataIndex: "size",
}, valueType: "text",
subTitle: {
render: () => {
return (
<Space size={0}>
<Tag color="blue">Ant Design</Tag>
<Tag color="#5BD8A6">TechUI</Tag>
</Space>
);
}, },
content: {
dataIndex: "time",
}, },
}} }}
/> />
</Skeleton>
</ProCard> </ProCard>
</div> </div>
</div> </div>
</> </>
); );
}; });
export default MainHome; export default memo(MainHome);

View File

@@ -2,33 +2,43 @@
import { PlusOutlined } from "@ant-design/icons"; import { PlusOutlined } from "@ant-design/icons";
import type { ActionType, ProColumns } from "@ant-design/pro-components"; import type { ActionType, ProColumns } from "@ant-design/pro-components";
import { ProTable, TableDropdown } from "@ant-design/pro-components"; import { ProTable, TableDropdown } from "@ant-design/pro-components";
import { Button, Col, Drawer, Form, Input, Row, Space } from "antd"; import {
import React, { useRef, useState } from "react"; Button,
Col,
Drawer,
Flex,
Form,
Input,
message,
Modal,
Row,
Space,
} from "antd";
import React, { useEffect, useRef, useState } from "react";
import {
addAliOSSConfig,
deleteAliConfig,
getAliConfigDetailById,
getAllAliOSSConfig,
initAliOSS,
setAliShutdown,
updateAliConfig,
} from "@/api/oss/ali";
import useStore from "@/utils/store/useStore.tsx";
import { observer } from "mobx-react";
type AliOssConfigItem = { const AliSettings: React.FC = observer(() => {
id: number; const [form] = Form.useForm();
userId: number; const actionRef = useRef<ActionType>();
endpoint: string; const [open, setOpen] = useState(false);
accessKeyId: string; const [openModal, setOpenModal] = useState(false);
accessKeySecret: string; const [configs, setConfigs] = useState<any>([]);
createdTime: string; const [loading, setLoading] = useState<boolean>(true);
updatedTime: string; const [configDetail, setConfigDetail] = useState<any>({} as any);
status: string; const userStore = useStore("user");
}; const userId: any = userStore.getUserId();
const data: AliOssConfigItem[] = [
{
id: 1,
userId: 1,
endpoint: "https://oss.aliyuncs.com",
accessKeyId: "LTAI5tG3",
accessKeySecret: "G3",
createdTime: "2024-06-28 10:51:59",
updatedTime: "2024-06-29 10:51:59",
status: "正常",
},
];
const columns: ProColumns<AliOssConfigItem>[] = [ const columns: ProColumns<any[]>[] = [
{ {
dataIndex: "index", dataIndex: "index",
valueType: "indexBorder", valueType: "indexBorder",
@@ -36,10 +46,12 @@ const columns: ProColumns<AliOssConfigItem>[] = [
}, },
{ {
title: "ID", title: "ID",
valueType: "text",
dataIndex: "id", dataIndex: "id",
copyable: true, copyable: true,
ellipsis: true, ellipsis: true,
tooltip: "Id", tooltip: "Id",
editable: false,
}, },
{ {
disable: true, disable: true,
@@ -47,6 +59,7 @@ const columns: ProColumns<AliOssConfigItem>[] = [
dataIndex: "endpoint", dataIndex: "endpoint",
tooltip: "endpoint", tooltip: "endpoint",
ellipsis: true, ellipsis: true,
copyable: true,
}, },
{ {
disable: true, disable: true,
@@ -54,6 +67,7 @@ const columns: ProColumns<AliOssConfigItem>[] = [
dataIndex: "accessKeyId", dataIndex: "accessKeyId",
tooltip: "access key id", tooltip: "access key id",
ellipsis: true, ellipsis: true,
copyable: true,
}, },
{ {
disable: true, disable: true,
@@ -61,24 +75,32 @@ const columns: ProColumns<AliOssConfigItem>[] = [
dataIndex: "accessKeySecret", dataIndex: "accessKeySecret",
tooltip: "access key secret", tooltip: "access key secret",
ellipsis: true, ellipsis: true,
}, copyable: true,
{
disable: true,
title: "状态",
dataIndex: "status",
search: true,
}, },
{ {
title: "创建时间", title: "创建时间",
dataIndex: "createdTime", dataIndex: "createdTime",
valueType: "dateTime", valueType: "dateTime",
sorter: true, sorter: true,
editable: false,
}, },
{ {
title: "更新时间", title: "更新时间",
dataIndex: "updatedTime", dataIndex: "updateTime",
valueType: "dateTime", valueType: "dateTime",
sorter: true, sorter: true,
editable: false,
},
{
disable: true,
title: "状态",
dataIndex: "status",
search: true,
valueEnum: {
true: { text: "开启", status: "Success" },
false: { text: "关闭", status: "Error" },
},
editable: false,
}, },
{ {
title: "操作", title: "操作",
@@ -86,7 +108,7 @@ const columns: ProColumns<AliOssConfigItem>[] = [
key: "option", key: "option",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error // @ts-expect-error
render: (text, record, _, action) => [ render: (text: any, record: any, _, action: any) => [
<a <a
key="editable" key="editable"
onClick={() => { onClick={() => {
@@ -94,31 +116,135 @@ const columns: ProColumns<AliOssConfigItem>[] = [
}}> }}>
</a>, </a>,
<a target="_blank" rel="noopener noreferrer" key="view"> <a
target="_blank"
rel="noopener noreferrer"
key="view"
onClick={() => {
getConfigDetail(record.id).then(() => {
setOpenModal(true);
setLoading(false);
});
}}>
</a>, </a>,
<TableDropdown <TableDropdown
key="actionGroup" key="actionGroup"
onSelect={() => action?.reload()} onSelect={(key: string) => {
if (key === "open") {
init(record.id).then();
} else if (key === "close") {
shutdown(record.id).then();
}
}}
menus={[ menus={[
{ key: "copy", name: "复制" }, { key: "open", name: "开启" },
{ key: "delete", name: "删除" }, { key: "close", name: "关闭" },
]} ]}
/>, />,
], ],
}, },
]; ];
async function getAllConfig() {
const AliSettings: React.FC = () => { getAllAliOSSConfig(userId).then((res: any) => {
const actionRef = useRef<ActionType>(); if (res && res.success && res.data) {
const [open, setOpen] = useState(false); 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(userId, 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(userId, 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: userId,
...fieldsValue,
};
addAliOSSConfig(AliOssConfig).then((res: any) => {
if (res && res.success) {
onClose();
getAllConfig().then(() => {
message
.open({
content: "新增成功",
type: "success",
})
.then();
});
} else {
message.open({
content: res.data,
type: "error",
});
}
});
}
const showDrawer = () => { const showDrawer = () => {
setOpen(true); setOpen(true);
}; };
const onClose = () => { const onClose = () => {
setOpen(false); setOpen(false);
}; };
useEffect(() => {
getAllConfig().then();
}, []);
const AddAliOssConfigDrawer = () => { const AddAliOssConfigDrawer = () => {
return ( return (
<> <>
@@ -135,12 +261,12 @@ const AliSettings: React.FC = () => {
extra={ extra={
<Space> <Space>
<Button onClick={onClose}></Button> <Button onClick={onClose}></Button>
<Button onClick={onClose} type="primary"> <Button onClick={addConfigs} type="primary">
</Button> </Button>
</Space> </Space>
}> }>
<Form layout="vertical"> <Form layout="vertical" form={form}>
<Row gutter={16}> <Row gutter={16}>
<Col span={12}> <Col span={12}>
<Form.Item <Form.Item
@@ -176,13 +302,52 @@ const AliSettings: React.FC = () => {
}; };
return ( return (
<> <>
<ProTable<AliOssConfigItem> <div style={{ minHeight: "65vh" }}>
<ProTable
columns={columns} columns={columns}
dataSource={data} dataSource={configs}
actionRef={actionRef} actionRef={actionRef}
cardBordered={true} cardBordered={true}
editable={{ editable={{
type: "multiple", type: "multiple",
onSave: async (__: any, originRow: any, _: any) => {
const updateForm: any = {
id: originRow.id,
endpoint: originRow.endpoint,
accessKeyId: originRow.accessKeyId,
accessKeySecret: originRow.accessKeySecret,
};
updateAliConfig(updateForm).then((res: any) => {
if (res && res.success) {
message.open({
content: "修改成功!",
type: "success",
});
getAllConfig().then();
} else {
message.open({
content: res.data,
type: "error",
});
}
});
},
onDelete: async (row: any) => {
deleteConfig(row).then((res: any) => {
if (res && res.success) {
message.open({
content: "删除成功",
type: "success",
});
getAllConfig().then();
} else {
message.open({
content: res.data,
type: "error",
});
}
});
},
}} }}
columnsState={{ columnsState={{
persistenceKey: "pro-table-singe-demos", persistenceKey: "pro-table-singe-demos",
@@ -198,9 +363,11 @@ const AliSettings: React.FC = () => {
listsHeight: 400, listsHeight: 400,
}, },
}} }}
pagination={{ pagination={
{
pageSize: 5, pageSize: 5,
}} } as any
}
dateFormatter="string" dateFormatter="string"
headerTitle="阿里云OSS配置" headerTitle="阿里云OSS配置"
bordered={false} bordered={false}
@@ -217,8 +384,46 @@ const AliSettings: React.FC = () => {
]} ]}
/> />
<AddAliOssConfigDrawer /> <AddAliOssConfigDrawer />
<Modal
title={<p></p>}
loading={loading}
footer={false}
open={openModal}
afterClose={() => {
setConfigDetail({});
}}
onCancel={() => setOpenModal(false)}>
<Flex vertical={false} align={"center"}>
<h4>ID:</h4> <span style={{ marginLeft: 10 }}>{configDetail.id}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>endpoint: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.endpoint}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>accessKeyId: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.accessKeyId} </span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>accessKeySecret: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.accessKeySecret}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>status: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.status}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>createdTime: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.createdTime}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>updateTime: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.updateTime}</span>
</Flex>
</Modal>
</div>
</> </>
); );
}; });
export default AliSettings; export default AliSettings;

View File

@@ -3,9 +3,9 @@ import { PlusOutlined } from "@ant-design/icons";
import type { ActionType, ProColumns } from "@ant-design/pro-components"; import type { ActionType, ProColumns } from "@ant-design/pro-components";
import { ProTable, TableDropdown } from "@ant-design/pro-components"; import { ProTable, TableDropdown } from "@ant-design/pro-components";
import { Button, Col, Drawer, Form, Input, Row, Space } from "antd"; import { Button, Col, Drawer, Form, Input, Row, Space } from "antd";
import React, { useRef, useState } from "react"; import React, { ReactNode, useRef, useState } from "react";
type AliOssConfigItem = { type AwsOssConfigItem = {
id: number; id: number;
userId: number; userId: number;
bucketName: string; bucketName: string;
@@ -17,7 +17,7 @@ type AliOssConfigItem = {
updatedTime: string; updatedTime: string;
status: string; status: string;
}; };
const data: AliOssConfigItem[] = [ const data: AwsOssConfigItem[] = [
{ {
id: 1, id: 1,
userId: 1, userId: 1,
@@ -32,7 +32,7 @@ const data: AliOssConfigItem[] = [
}, },
]; ];
const columns: ProColumns<AliOssConfigItem>[] = [ const columns: ProColumns<AwsOssConfigItem>[] = [
{ {
dataIndex: "index", dataIndex: "index",
valueType: "indexBorder", valueType: "indexBorder",
@@ -105,7 +105,7 @@ const columns: ProColumns<AliOssConfigItem>[] = [
key: "option", key: "option",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error // @ts-expect-error
render: (text, record, _, action) => [ render: (text: ReactNode, record: AwsOssConfigItem, _, action: any) => [
<a <a
key="editable" key="editable"
onClick={() => { onClick={() => {
@@ -213,7 +213,8 @@ const AwsSettings: React.FC = () => {
}; };
return ( return (
<> <>
<ProTable<AliOssConfigItem> <div style={{ height: "65vh" }}>
<ProTable<AwsOssConfigItem>
columns={columns} columns={columns}
dataSource={data} dataSource={data}
actionRef={actionRef} actionRef={actionRef}
@@ -235,9 +236,11 @@ const AwsSettings: React.FC = () => {
listsHeight: 400, listsHeight: 400,
}, },
}} }}
pagination={{ pagination={
{
pageSize: 5, pageSize: 5,
}} } as any
}
dateFormatter="string" dateFormatter="string"
headerTitle="亚马逊S3配置" headerTitle="亚马逊S3配置"
bordered={false} bordered={false}
@@ -254,6 +257,7 @@ const AwsSettings: React.FC = () => {
]} ]}
/> />
<AddAwsOssConfigDrawer /> <AddAwsOssConfigDrawer />
</div>
</> </>
); );
}; };

View File

@@ -3,9 +3,9 @@ import { PlusOutlined } from "@ant-design/icons";
import type { ActionType, ProColumns } from "@ant-design/pro-components"; import type { ActionType, ProColumns } from "@ant-design/pro-components";
import { ProTable, TableDropdown } from "@ant-design/pro-components"; import { ProTable, TableDropdown } from "@ant-design/pro-components";
import { Button, Col, DatePicker, Drawer, Form, Input, Row, Select, Space } from "antd"; import { Button, Col, DatePicker, Drawer, Form, Input, Row, Select, Space } from "antd";
import React, { useRef, useState } from "react"; import React, { ReactNode, useRef, useState } from "react";
type AliOssConfigItem = { type BaiduOssConfigItem = {
id: number; id: number;
userId: number; userId: number;
endpoint: string; endpoint: string;
@@ -15,7 +15,7 @@ type AliOssConfigItem = {
updated_time: string; updated_time: string;
status: string; status: string;
}; };
const data: AliOssConfigItem[] = [ const data: BaiduOssConfigItem[] = [
{ {
id: 1, id: 1,
userId: 1, userId: 1,
@@ -28,7 +28,7 @@ const data: AliOssConfigItem[] = [
}, },
]; ];
const columns: ProColumns<AliOssConfigItem>[] = [ const columns: ProColumns<BaiduOssConfigItem>[] = [
{ {
dataIndex: "index", dataIndex: "index",
valueType: "indexBorder", valueType: "indexBorder",
@@ -87,7 +87,7 @@ const columns: ProColumns<AliOssConfigItem>[] = [
key: "option", key: "option",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error // @ts-expect-error
render: (text, record, _, action) => [ render: (text: ReactNode, record: BaiduOssConfigItem, _, action: any) => [
<a <a
key="editable" key="editable"
onClick={() => { onClick={() => {
@@ -242,7 +242,8 @@ const BaiduSettings: React.FC = () => {
}; };
return ( return (
<> <>
<ProTable<AliOssConfigItem> <div style={{ height: "65vh" }}>
<ProTable<BaiduOssConfigItem>
columns={columns} columns={columns}
dataSource={data} dataSource={data}
actionRef={actionRef} actionRef={actionRef}
@@ -264,9 +265,11 @@ const BaiduSettings: React.FC = () => {
listsHeight: 400, listsHeight: 400,
}, },
}} }}
pagination={{ pagination={
{
pageSize: 5, pageSize: 5,
}} } as any
}
dateFormatter="string" dateFormatter="string"
headerTitle="百度云BOS配置" headerTitle="百度云BOS配置"
bordered={false} bordered={false}
@@ -283,6 +286,7 @@ const BaiduSettings: React.FC = () => {
]} ]}
/> />
<AddBaiduOssConfigDrawer /> <AddBaiduOssConfigDrawer />
</div>
</> </>
); );
}; };

View File

@@ -3,9 +3,9 @@ import { PlusOutlined } from "@ant-design/icons";
import type { ActionType, ProColumns } from "@ant-design/pro-components"; import type { ActionType, ProColumns } from "@ant-design/pro-components";
import { ProTable, TableDropdown } from "@ant-design/pro-components"; import { ProTable, TableDropdown } from "@ant-design/pro-components";
import { Button, Col, Drawer, Form, Input, Row, Space } from "antd"; import { Button, Col, Drawer, Form, Input, Row, Space } from "antd";
import React, { useRef, useState } from "react"; import React, { ReactNode, useRef, useState } from "react";
type AliOssConfigItem = { type HuaweiOssConfigItem = {
id: number; id: number;
userId: number; userId: number;
bucketName: string; bucketName: string;
@@ -16,7 +16,7 @@ type AliOssConfigItem = {
updatedTime: string; updatedTime: string;
status: string; status: string;
}; };
const data: AliOssConfigItem[] = [ const data: HuaweiOssConfigItem[] = [
{ {
id: 1, id: 1,
userId: 1, userId: 1,
@@ -30,7 +30,7 @@ const data: AliOssConfigItem[] = [
}, },
]; ];
const columns: ProColumns<AliOssConfigItem>[] = [ const columns: ProColumns<HuaweiOssConfigItem>[] = [
{ {
dataIndex: "index", dataIndex: "index",
valueType: "indexBorder", valueType: "indexBorder",
@@ -96,7 +96,7 @@ const columns: ProColumns<AliOssConfigItem>[] = [
key: "option", key: "option",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error // @ts-expect-error
render: (text, record, _, action) => [ render: (text: ReactNode, record: HuaweiOssConfigItem, _: number, action: any) => [
<a <a
key="editable" key="editable"
onClick={() => { onClick={() => {
@@ -194,7 +194,8 @@ const HuawaiSettings: React.FC = () => {
}; };
return ( return (
<> <>
<ProTable<AliOssConfigItem> <div style={{ height: "65vh" }}>
<ProTable<HuaweiOssConfigItem>
columns={columns} columns={columns}
dataSource={data} dataSource={data}
actionRef={actionRef} actionRef={actionRef}
@@ -216,9 +217,11 @@ const HuawaiSettings: React.FC = () => {
listsHeight: 400, listsHeight: 400,
}, },
}} }}
pagination={{ pagination={
{
pageSize: 5, pageSize: 5,
}} } as any
}
dateFormatter="string" dateFormatter="string"
headerTitle="华为云OBS配置" headerTitle="华为云OBS配置"
bordered={false} bordered={false}
@@ -235,6 +238,7 @@ const HuawaiSettings: React.FC = () => {
]} ]}
/> />
<AddHuaweiOssConfigDrawer /> <AddHuaweiOssConfigDrawer />
</div>
</> </>
); );
}; };

View File

@@ -3,9 +3,9 @@ import { PlusOutlined } from "@ant-design/icons";
import type { ActionType, ProColumns } from "@ant-design/pro-components"; import type { ActionType, ProColumns } from "@ant-design/pro-components";
import { ProTable, TableDropdown } from "@ant-design/pro-components"; import { ProTable, TableDropdown } from "@ant-design/pro-components";
import { Button, Col, Drawer, Form, Input, Row, Space } from "antd"; import { Button, Col, Drawer, Form, Input, Row, Space } from "antd";
import React, { useRef, useState } from "react"; import React, { ReactNode, useRef, useState } from "react";
type AliOssConfigItem = { type JdOssConfigItem = {
id: number; id: number;
userId: number; userId: number;
bucketName: number; bucketName: number;
@@ -17,7 +17,7 @@ type AliOssConfigItem = {
updatedTime: string; updatedTime: string;
status: string; status: string;
}; };
const data: AliOssConfigItem[] = [ const data: JdOssConfigItem[] = [
{ {
id: 1, id: 1,
userId: 1, userId: 1,
@@ -32,7 +32,7 @@ const data: AliOssConfigItem[] = [
}, },
]; ];
const columns: ProColumns<AliOssConfigItem>[] = [ const columns: ProColumns<JdOssConfigItem>[] = [
{ {
dataIndex: "index", dataIndex: "index",
valueType: "indexBorder", valueType: "indexBorder",
@@ -98,7 +98,7 @@ const columns: ProColumns<AliOssConfigItem>[] = [
key: "option", key: "option",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error // @ts-expect-error
render: (text, record, _, action) => [ render: (text: ReactNode, record: JdOssConfigItem, _: number, action: any) => [
<a <a
key="editable" key="editable"
onClick={() => { onClick={() => {
@@ -196,7 +196,8 @@ const JdSettings: React.FC = () => {
}; };
return ( return (
<> <>
<ProTable<AliOssConfigItem> <div style={{ height: "65vh" }}>
<ProTable<JdOssConfigItem>
columns={columns} columns={columns}
dataSource={data} dataSource={data}
actionRef={actionRef} actionRef={actionRef}
@@ -218,9 +219,11 @@ const JdSettings: React.FC = () => {
listsHeight: 400, listsHeight: 400,
}, },
}} }}
pagination={{ pagination={
{
pageSize: 5, pageSize: 5,
}} } as any
}
dateFormatter="string" dateFormatter="string"
headerTitle="京东云OSS配置" headerTitle="京东云OSS配置"
bordered={false} bordered={false}
@@ -237,6 +240,7 @@ const JdSettings: React.FC = () => {
]} ]}
/> />
<AddJdOssConfigDrawer /> <AddJdOssConfigDrawer />
</div>
</> </>
); );
}; };

View File

@@ -3,9 +3,9 @@ import { PlusOutlined } from "@ant-design/icons";
import type { ActionType, ProColumns } from "@ant-design/pro-components"; import type { ActionType, ProColumns } from "@ant-design/pro-components";
import { ProTable, TableDropdown } from "@ant-design/pro-components"; import { ProTable, TableDropdown } from "@ant-design/pro-components";
import { Button, Col, Drawer, Form, Input, Row, Space } from "antd"; import { Button, Col, Drawer, Form, Input, Row, Space } from "antd";
import React, { useRef, useState } from "react"; import React, { ReactNode, useRef, useState } from "react";
type AliOssConfigItem = { type JinshanOssConfigItem = {
id: number; id: number;
userId: number; userId: number;
endpoint: string; endpoint: string;
@@ -18,7 +18,7 @@ type AliOssConfigItem = {
updatedTime: string; updatedTime: string;
status: string; status: string;
}; };
const data: AliOssConfigItem[] = [ const data: JinshanOssConfigItem[] = [
{ {
id: 1, id: 1,
userId: 1, userId: 1,
@@ -34,7 +34,7 @@ const data: AliOssConfigItem[] = [
}, },
]; ];
const columns: ProColumns<AliOssConfigItem>[] = [ const columns: ProColumns<JinshanOssConfigItem>[] = [
{ {
dataIndex: "index", dataIndex: "index",
valueType: "indexBorder", valueType: "indexBorder",
@@ -114,7 +114,7 @@ const columns: ProColumns<AliOssConfigItem>[] = [
key: "option", key: "option",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error // @ts-expect-error
render: (text, record, _, action) => [ render: (text: ReactNode, record: JinshanOssConfigItem, _: number, action: any) => [
<a <a
key="editable" key="editable"
onClick={() => { onClick={() => {
@@ -230,7 +230,8 @@ const JinshanSettings: React.FC = () => {
}; };
return ( return (
<> <>
<ProTable<AliOssConfigItem> <div style={{ height: "65vh" }}>
<ProTable<JinshanOssConfigItem>
columns={columns} columns={columns}
dataSource={data} dataSource={data}
actionRef={actionRef} actionRef={actionRef}
@@ -252,9 +253,11 @@ const JinshanSettings: React.FC = () => {
listsHeight: 400, listsHeight: 400,
}, },
}} }}
pagination={{ pagination={
{
pageSize: 5, pageSize: 5,
}} } as any
}
dateFormatter="string" dateFormatter="string"
headerTitle="金山OBS配置" headerTitle="金山OBS配置"
bordered={false} bordered={false}
@@ -271,6 +274,7 @@ const JinshanSettings: React.FC = () => {
]} ]}
/> />
<AddJinshanOssConfigDrawer /> <AddJinshanOssConfigDrawer />
</div>
</> </>
); );
}; };

View File

@@ -2,37 +2,47 @@
import { PlusOutlined } from "@ant-design/icons"; import { PlusOutlined } from "@ant-design/icons";
import type { ActionType, ProColumns } from "@ant-design/pro-components"; import type { ActionType, ProColumns } from "@ant-design/pro-components";
import { ProTable, TableDropdown } from "@ant-design/pro-components"; import { ProTable, TableDropdown } from "@ant-design/pro-components";
import { Button, Col, Drawer, Form, Input, Row, Space } from "antd"; import { Button, Col, Drawer, Flex, Form, Input, message, Modal, Row, Space } from "antd";
import React, { useRef, useState } from "react"; import React, { ReactNode, useEffect, useRef, useState } from "react";
import {
addMinioOSSConfig,
deleteMinioConfig,
getAllMinioConfig,
getMinioConfigDetailById,
initMinioOSS,
setMinioShutdown,
updateMinioConfig,
} from "@/api/oss/minio";
import useStore from "@/utils/store/useStore.tsx";
import { observer } from "mobx-react";
type AliOssConfigItem = { type MinioOssConfigItem = {
id: number; id: number;
userId: number; userId: number;
endpoint: string; endpoint: string;
accessKey: string; accessKey: string;
secretKey: string; secretKey: string;
createdTime: string; createdTime: string;
updatedTime: string; updateTime: string;
status: string; status: string;
}; };
const data: AliOssConfigItem[] = [
{
id: 1,
userId: 1,
endpoint: "https://oss.aliyuncs.com",
accessKey: "LTAI5tG3",
secretKey: "G3",
createdTime: "2022-01-01",
updatedTime: "2022-01-01",
status: "正常",
},
];
const columns: ProColumns<AliOssConfigItem>[] = [ const MinioSettings: React.FC = observer(() => {
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<any>({} as any);
const userStore = useStore("user");
const userId: any = userStore.getUserId();
const columns: ProColumns<MinioOssConfigItem>[] = [
{ {
dataIndex: "index", dataIndex: "index",
valueType: "indexBorder", valueType: "indexBorder",
width: 48, width: 48,
editable: false,
}, },
{ {
title: "ID", title: "ID",
@@ -40,12 +50,14 @@ const columns: ProColumns<AliOssConfigItem>[] = [
copyable: true, copyable: true,
ellipsis: true, ellipsis: true,
tooltip: "Id", tooltip: "Id",
editable: false,
}, },
{ {
disable: true, disable: true,
title: "服务地址", title: "服务地址",
dataIndex: "endpoint", dataIndex: "endpoint",
tooltip: "endpoint", tooltip: "endpoint",
copyable: true,
ellipsis: true, ellipsis: true,
}, },
{ {
@@ -54,6 +66,7 @@ const columns: ProColumns<AliOssConfigItem>[] = [
dataIndex: "accessKey", dataIndex: "accessKey",
tooltip: "access key", tooltip: "access key",
ellipsis: true, ellipsis: true,
copyable: true,
}, },
{ {
disable: true, disable: true,
@@ -61,25 +74,34 @@ const columns: ProColumns<AliOssConfigItem>[] = [
dataIndex: "secretKey", dataIndex: "secretKey",
tooltip: "secret key", tooltip: "secret key",
ellipsis: true, ellipsis: true,
copyable: true,
}, },
{
disable: true,
title: "状态",
dataIndex: "status",
search: true,
},
{ {
title: "创建时间", title: "创建时间",
dataIndex: "createdTime", dataIndex: "createdTime",
valueType: "dateTime", valueType: "dateTime",
sorter: true, sorter: true,
hideInSearch: true, hideInSearch: true,
editable: false,
}, },
{ {
title: "更新时间", title: "更新时间",
dataIndex: "updatedTime", dataIndex: "updateTime",
valueType: "dateTime", valueType: "dateTime",
sorter: true, sorter: true,
editable: false,
},
{
disable: true,
title: "状态",
dataIndex: "status",
search: true,
valueEnum: {
true: { text: "开启", status: "Success" },
false: { text: "关闭", status: "Error" },
},
editable: false,
}, },
{ {
title: "操作", title: "操作",
@@ -87,7 +109,7 @@ const columns: ProColumns<AliOssConfigItem>[] = [
key: "option", key: "option",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error // @ts-expect-error
render: (text, record, _, action) => [ render: (text: ReactNode, record: any, _: number, action: any) => [
<a <a
key="editable" key="editable"
onClick={() => { onClick={() => {
@@ -95,24 +117,35 @@ const columns: ProColumns<AliOssConfigItem>[] = [
}}> }}>
</a>, </a>,
<a target="_blank" rel="noopener noreferrer" key="view"> <a
target="_blank"
rel="noopener noreferrer"
key="view"
onClick={() => {
getConfigDetail(record.id).then(() => {
setOpenModal(true);
setLoading(false);
});
}}>
</a>, </a>,
<TableDropdown <TableDropdown
key="actionGroup" key="actionGroup"
onSelect={() => action?.reload()} onSelect={(key: string) => {
if (key === "open") {
init(record.id).then();
} else if (key === "close") {
shutdown(record.id).then();
}
}}
menus={[ menus={[
{ key: "copy", name: "复制" }, { key: "open", name: "开启" },
{ key: "delete", name: "删除" }, { key: "close", name: "关闭" },
]} ]}
/>, />,
], ],
}, },
]; ];
const MinioSettings: React.FC = () => {
const actionRef = useRef<ActionType>();
const [open, setOpen] = useState(false);
const showDrawer = () => { const showDrawer = () => {
setOpen(true); setOpen(true);
}; };
@@ -120,12 +153,111 @@ const MinioSettings: React.FC = () => {
const onClose = () => { const onClose = () => {
setOpen(false); setOpen(false);
}; };
async function getConfigDetail(id: any) {
getMinioConfigDetailById(id).then((res: any) => {
if (res && res.success && res.data) {
setConfigDetail(res.data);
}
});
}
async function init(id: any) {
initMinioOSS(userId, 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(userId, 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(userId).then((res: any) => {
if (res && res.success && res.data) {
setConfigs(res.data);
}
});
}
async function addMinioConfig() {
const fieldsValue = form.getFieldsValue();
const MinioOssConfig = {
userId: userId,
...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();
}, []);
const AddMinioOssConfigDrawer = () => { const AddMinioOssConfigDrawer = () => {
return ( return (
<> <>
<Drawer <Drawer
title="创建连接配置" title="创建连接配置"
width={720} width={"45%"}
onClose={onClose} onClose={onClose}
open={open} open={open}
styles={{ styles={{
@@ -136,12 +268,12 @@ const MinioSettings: React.FC = () => {
extra={ extra={
<Space> <Space>
<Button onClick={onClose}></Button> <Button onClick={onClose}></Button>
<Button onClick={onClose} type="primary"> <Button onClick={addMinioConfig} type="primary">
</Button> </Button>
</Space> </Space>
}> }>
<Form layout="vertical"> <Form layout="vertical" form={form}>
<Row gutter={16}> <Row gutter={16}>
<Col span={12}> <Col span={12}>
<Form.Item <Form.Item
@@ -177,13 +309,52 @@ const MinioSettings: React.FC = () => {
}; };
return ( return (
<> <>
<ProTable<AliOssConfigItem> <div style={{ minHeight: "65vh" }}>
<ProTable<MinioOssConfigItem>
columns={columns} columns={columns}
dataSource={data} dataSource={configs}
actionRef={actionRef} actionRef={actionRef}
cardBordered={true} cardBordered={true}
editable={{ editable={{
type: "multiple", type: "multiple",
onSave: async (__: any, originRow: any, _: any) => {
const updateForm: any = {
id: originRow.id,
endpoint: originRow.endpoint,
accessKey: originRow.accessKey,
secretKey: originRow.secretKey,
};
updateMinioConfig(updateForm).then((res: any) => {
if (res && res.success) {
message.open({
content: "修改成功!",
type: "success",
});
getAllConfig().then();
} else {
message.open({
content: res.data,
type: "error",
});
}
});
},
onDelete: async (row: any) => {
deleteConfig(row).then((res: any) => {
if (res && res.success) {
message.open({
content: "删除成功",
type: "success",
});
getAllConfig().then();
} else {
message.open({
content: res.data,
type: "error",
});
}
});
},
}} }}
columnsState={{ columnsState={{
persistenceKey: "pro-table-singe-demos", persistenceKey: "pro-table-singe-demos",
@@ -199,9 +370,11 @@ const MinioSettings: React.FC = () => {
listsHeight: 400, listsHeight: 400,
}, },
}} }}
pagination={{ pagination={
{
pageSize: 5, pageSize: 5,
}} } as any
}
dateFormatter="string" dateFormatter="string"
headerTitle="MinIO配置" headerTitle="MinIO配置"
bordered={false} bordered={false}
@@ -218,8 +391,46 @@ const MinioSettings: React.FC = () => {
]} ]}
/> />
<AddMinioOssConfigDrawer /> <AddMinioOssConfigDrawer />
<Modal
title={<p></p>}
loading={loading}
footer={false}
afterClose={() => {
setConfigDetail({});
}}
open={openModal}
onCancel={() => setOpenModal(false)}>
<Flex vertical={false} align={"center"}>
<h4>ID:</h4> <span style={{ marginLeft: 10 }}>{configDetail.id}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>endpoint: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.endpoint}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>accessKey: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.accessKey} </span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>secretKey: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.secretKey}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>status: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.status}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>createdTime: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.createdTime}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>updateTime: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.updateTime}</span>
</Flex>
</Modal>
</div>
</> </>
); );
}; });
export default MinioSettings; export default MinioSettings;

View File

@@ -3,9 +3,9 @@ import { PlusOutlined } from "@ant-design/icons";
import type { ActionType, ProColumns } from "@ant-design/pro-components"; import type { ActionType, ProColumns } from "@ant-design/pro-components";
import { ProTable, TableDropdown } from "@ant-design/pro-components"; import { ProTable, TableDropdown } from "@ant-design/pro-components";
import { Button, Col, Drawer, Form, Input, Row, Space } from "antd"; import { Button, Col, Drawer, Form, Input, Row, Space } from "antd";
import React, { useRef, useState } from "react"; import React, { ReactNode, useRef, useState } from "react";
type AliOssConfigItem = { type PinanOssConfigItem = {
id: number; id: number;
userId: number; userId: number;
endpoint: string; endpoint: string;
@@ -15,7 +15,7 @@ type AliOssConfigItem = {
updated_time: string; updated_time: string;
status: string; status: string;
}; };
const data: AliOssConfigItem[] = [ const data: PinanOssConfigItem[] = [
{ {
id: 1, id: 1,
userId: 1, userId: 1,
@@ -28,7 +28,7 @@ const data: AliOssConfigItem[] = [
}, },
]; ];
const columns: ProColumns<AliOssConfigItem>[] = [ const columns: ProColumns<PinanOssConfigItem>[] = [
{ {
dataIndex: "index", dataIndex: "index",
valueType: "indexBorder", valueType: "indexBorder",
@@ -87,7 +87,7 @@ const columns: ProColumns<AliOssConfigItem>[] = [
key: "option", key: "option",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error // @ts-expect-error
render: (text, record, _, action) => [ render: (text: ReactNode, record: PinanOssConfigItem, _: number, action: any) => [
<a <a
key="editable" key="editable"
onClick={() => { onClick={() => {
@@ -177,7 +177,8 @@ const PinanSettings: React.FC = () => {
}; };
return ( return (
<> <>
<ProTable<AliOssConfigItem> <div style={{ height: "65vh" }}>
<ProTable<PinanOssConfigItem>
columns={columns} columns={columns}
dataSource={data} dataSource={data}
actionRef={actionRef} actionRef={actionRef}
@@ -199,9 +200,11 @@ const PinanSettings: React.FC = () => {
listsHeight: 400, listsHeight: 400,
}, },
}} }}
pagination={{ pagination={
{
pageSize: 5, pageSize: 5,
}} } as any
}
dateFormatter="string" dateFormatter="string"
headerTitle="平安云OSS配置" headerTitle="平安云OSS配置"
bordered={false} bordered={false}
@@ -218,6 +221,7 @@ const PinanSettings: React.FC = () => {
]} ]}
/> />
<AddPinanOssConfigDrawer /> <AddPinanOssConfigDrawer />
</div>
</> </>
); );
}; };

View File

@@ -3,9 +3,9 @@ import { PlusOutlined } from "@ant-design/icons";
import type { ActionType, ProColumns } from "@ant-design/pro-components"; import type { ActionType, ProColumns } from "@ant-design/pro-components";
import { ProTable, TableDropdown } from "@ant-design/pro-components"; import { ProTable, TableDropdown } from "@ant-design/pro-components";
import { Button, Col, Drawer, Form, Input, Row, Space } from "antd"; import { Button, Col, Drawer, Form, Input, Row, Space } from "antd";
import React, { useRef, useState } from "react"; import React, { ReactNode, useRef, useState } from "react";
type AliOssConfigItem = { type QingyunOssConfigItem = {
id: number; id: number;
userId: number; userId: number;
zone: string; zone: string;
@@ -17,7 +17,7 @@ type AliOssConfigItem = {
updatedTime: string; updatedTime: string;
status: string; status: string;
}; };
const data: AliOssConfigItem[] = [ const data: QingyunOssConfigItem[] = [
{ {
id: 1, id: 1,
userId: 1, userId: 1,
@@ -32,7 +32,7 @@ const data: AliOssConfigItem[] = [
}, },
]; ];
const columns: ProColumns<AliOssConfigItem>[] = [ const columns: ProColumns<QingyunOssConfigItem>[] = [
{ {
dataIndex: "index", dataIndex: "index",
valueType: "indexBorder", valueType: "indexBorder",
@@ -98,7 +98,7 @@ const columns: ProColumns<AliOssConfigItem>[] = [
key: "option", key: "option",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error // @ts-expect-error
render: (text, record, _, action) => [ render: (text: ReactNode, record: QingyunOssConfigItem, _: number, action: any) => [
<a <a
key="editable" key="editable"
onClick={() => { onClick={() => {
@@ -206,7 +206,8 @@ const QingyunSettings: React.FC = () => {
}; };
return ( return (
<> <>
<ProTable<AliOssConfigItem> <div style={{ height: "65vh" }}>
<ProTable<QingyunOssConfigItem>
columns={columns} columns={columns}
dataSource={data} dataSource={data}
actionRef={actionRef} actionRef={actionRef}
@@ -228,9 +229,11 @@ const QingyunSettings: React.FC = () => {
listsHeight: 400, listsHeight: 400,
}, },
}} }}
pagination={{ pagination={
{
pageSize: 5, pageSize: 5,
}} } as any
}
dateFormatter="string" dateFormatter="string"
headerTitle="青云OSS配置" headerTitle="青云OSS配置"
bordered={false} bordered={false}
@@ -247,6 +250,7 @@ const QingyunSettings: React.FC = () => {
]} ]}
/> />
<AddQingyunOssConfigDrawer /> <AddQingyunOssConfigDrawer />
</div>
</> </>
); );
}; };

View File

@@ -2,37 +2,47 @@
import { PlusOutlined } from "@ant-design/icons"; import { PlusOutlined } from "@ant-design/icons";
import type { ActionType, ProColumns } from "@ant-design/pro-components"; import type { ActionType, ProColumns } from "@ant-design/pro-components";
import { ProTable, TableDropdown } from "@ant-design/pro-components"; import { ProTable, TableDropdown } from "@ant-design/pro-components";
import { Button, Col, Drawer, Form, Input, Row, Space } from "antd"; import { Button, Col, Drawer, Flex, Form, Input, message, Modal, Row, Space } from "antd";
import React, { useRef, useState } from "react"; import React, { ReactNode, useEffect, useRef, useState } from "react";
import {
addQiniuOSSConfig,
deleteQiniuConfig,
getAllQiniuConfigs,
getQiniuConfigDetailById,
initQiniuOSS,
setQiniuShutdown,
updateQiniuConfig,
} from "@/api/oss/qiniu";
import useStore from "@/utils/store/useStore.tsx";
import { observer } from "mobx-react";
type AliOssConfigItem = { type QiniuOssConfigItem = {
id: number; id: number;
userId: number; userId: number;
bucketName: string; bucketName: string;
accessKey: string; accessKey: string;
secretKey: string; secretKey: string;
createdTime: string; createdTime: string;
updatedTime: string; updateTime: string;
status: string; status: string;
}; };
const data: AliOssConfigItem[] = [
{
id: 1,
userId: 1,
bucketName: "https://oss.aliyuncs.com",
accessKey: "LTAI5tG3",
secretKey: "G3",
createdTime: "2022-01-01",
updatedTime: "2022-01-01",
status: "正常",
},
];
const columns: ProColumns<AliOssConfigItem>[] = [ const QiniuSettings: React.FC = observer(() => {
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<any>({} as any);
const userStore = useStore("user");
const userId: any = userStore.getUserId();
const columns: ProColumns<QiniuOssConfigItem>[] = [
{ {
dataIndex: "index", dataIndex: "index",
valueType: "indexBorder", valueType: "indexBorder",
width: 48, width: 48,
editable: false,
}, },
{ {
title: "ID", title: "ID",
@@ -40,46 +50,58 @@ const columns: ProColumns<AliOssConfigItem>[] = [
copyable: true, copyable: true,
ellipsis: true, ellipsis: true,
tooltip: "Id", tooltip: "Id",
editable: false,
}, },
{ {
disable: true, disable: true,
title: "服务地址", title: "服务地址",
dataIndex: "endpoint", dataIndex: "endpoint",
tooltip: "endpoint", tooltip: "endpoint",
copyable: true,
ellipsis: true, ellipsis: true,
}, },
{ {
disable: true, disable: true,
title: "密钥ID", title: "密钥ID",
dataIndex: "accessKeyId", dataIndex: "accessKey",
tooltip: "access key id", tooltip: "access key id",
copyable: true,
ellipsis: true, ellipsis: true,
}, },
{ {
disable: true, disable: true,
title: "密钥值", title: "密钥值",
dataIndex: "accessKeySecret", dataIndex: "secretKey",
tooltip: "access key secret", tooltip: "access key secret",
copyable: true,
ellipsis: true, ellipsis: true,
}, },
{
disable: true,
title: "状态",
dataIndex: "status",
search: true,
},
{ {
title: "创建时间", title: "创建时间",
dataIndex: "createdTime", dataIndex: "createdTime",
valueType: "dateTime", valueType: "dateTime",
sorter: true, sorter: true,
hideInSearch: true, hideInSearch: true,
editable: false,
}, },
{ {
title: "更新时间", title: "更新时间",
dataIndex: "updatedTime", dataIndex: "updateTime",
valueType: "dateTime", valueType: "dateTime",
sorter: true, sorter: true,
editable: false,
},
{
disable: true,
title: "状态",
dataIndex: "status",
search: true,
valueEnum: {
true: { text: "开启", status: "Success" },
false: { text: "关闭", status: "Error" },
},
editable: false,
}, },
{ {
title: "操作", title: "操作",
@@ -87,7 +109,7 @@ const columns: ProColumns<AliOssConfigItem>[] = [
key: "option", key: "option",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error // @ts-expect-error
render: (text, record, _, action) => [ render: (text: ReactNode, record: QiniuOssConfigItem, _: number, action: any) => [
<a <a
key="editable" key="editable"
onClick={() => { onClick={() => {
@@ -95,24 +117,99 @@ const columns: ProColumns<AliOssConfigItem>[] = [
}}> }}>
</a>, </a>,
<a target="_blank" rel="noopener noreferrer" key="view"> <a
target="_blank"
rel="noopener noreferrer"
key="view"
onClick={() => {
getConfigDetail(record.id).then(() => {
setOpenModal(true);
setLoading(false);
});
}}>
</a>, </a>,
<TableDropdown <TableDropdown
key="actionGroup" key="actionGroup"
onSelect={() => action?.reload()} onSelect={(key: string) => {
if (key === "open") {
init(record.id).then();
} else if (key === "close") {
shutdown(record.id).then();
}
}}
menus={[ menus={[
{ key: "copy", name: "复制" }, { key: "open", name: "开启" },
{ key: "delete", name: "删除" }, { key: "close", name: "关闭" },
]} ]}
/>, />,
], ],
}, },
]; ];
const QiniuSettings: React.FC = () => { async function init(id: any) {
const actionRef = useRef<ActionType>(); initQiniuOSS(userId, id).then((res: any) => {
const [open, setOpen] = useState(false); 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(userId, 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 = () => { const showDrawer = () => {
setOpen(true); setOpen(true);
}; };
@@ -120,12 +217,48 @@ const QiniuSettings: React.FC = () => {
const onClose = () => { const onClose = () => {
setOpen(false); setOpen(false);
}; };
async function getAllConfigs() {
getAllQiniuConfigs(userId).then((res: any) => {
if (res && res.success && res.data) {
setConfig(res.data);
}
});
}
async function addQiniuoConfig() {
const fieldsValue = form.getFieldsValue();
const QiniuOssConfig = {
userId: userId,
...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();
}, []);
const AddQiniuOssConfigDrawer = () => { const AddQiniuOssConfigDrawer = () => {
return ( return (
<> <>
<Drawer <Drawer
title="创建连接配置" title="创建连接配置"
width={720} width={"45%"}
onClose={onClose} onClose={onClose}
open={open} open={open}
styles={{ styles={{
@@ -136,12 +269,12 @@ const QiniuSettings: React.FC = () => {
extra={ extra={
<Space> <Space>
<Button onClick={onClose}></Button> <Button onClick={onClose}></Button>
<Button onClick={onClose} type="primary"> <Button onClick={addQiniuoConfig} type="primary">
</Button> </Button>
</Space> </Space>
}> }>
<Form layout="vertical"> <Form layout="vertical" form={form}>
<Row gutter={16}> <Row gutter={16}>
<Col span={12}> <Col span={12}>
<Form.Item <Form.Item
@@ -151,6 +284,14 @@ const QiniuSettings: React.FC = () => {
<Input placeholder="请输入存储桶!" /> <Input placeholder="请输入存储桶!" />
</Form.Item> </Form.Item>
</Col> </Col>
<Col span={12}>
<Form.Item
name="endpoint"
label="服务地址"
rules={[{ required: true, message: "请输入服务地址!" }]}>
<Input placeholder="请输入服务地址!" />
</Form.Item>
</Col>
</Row> </Row>
<Row gutter={16}> <Row gutter={16}>
<Col span={12}> <Col span={12}>
@@ -177,13 +318,52 @@ const QiniuSettings: React.FC = () => {
}; };
return ( return (
<> <>
<ProTable<AliOssConfigItem> <div style={{ height: "65vh" }}>
<ProTable<QiniuOssConfigItem>
columns={columns} columns={columns}
dataSource={data} dataSource={config}
actionRef={actionRef} actionRef={actionRef}
cardBordered={true} cardBordered={true}
editable={{ editable={{
type: "multiple", type: "multiple",
onSave: async (__: any, originRow: any, _: any) => {
const updateForm: any = {
id: originRow.id,
endpoint: originRow.endpoint,
accessKey: originRow.accessKey,
secretKey: originRow.secretKey,
};
updateQiniuConfig(updateForm).then((res: any) => {
if (res && res.success) {
message.open({
content: "修改成功!",
type: "success",
});
getAllConfigs().then();
} else {
message.open({
content: res.data,
type: "error",
});
}
});
},
onDelete: async (row: any) => {
deleteConfig(row).then((res: any) => {
if (res && res.success) {
message.open({
content: "删除成功",
type: "success",
});
getAllConfigs().then();
} else {
message.open({
content: res.data,
type: "error",
});
}
});
},
}} }}
columnsState={{ columnsState={{
persistenceKey: "pro-table-singe-demos", persistenceKey: "pro-table-singe-demos",
@@ -199,9 +379,11 @@ const QiniuSettings: React.FC = () => {
listsHeight: 400, listsHeight: 400,
}, },
}} }}
pagination={{ pagination={
{
pageSize: 5, pageSize: 5,
}} } as any
}
dateFormatter="string" dateFormatter="string"
headerTitle="七牛Kodo配置" headerTitle="七牛Kodo配置"
bordered={false} bordered={false}
@@ -218,8 +400,46 @@ const QiniuSettings: React.FC = () => {
]} ]}
/> />
<AddQiniuOssConfigDrawer /> <AddQiniuOssConfigDrawer />
<Modal
title={<p></p>}
loading={loading}
afterClose={() => {
setConfigDetail({});
}}
footer={false}
open={openModal}
onCancel={() => setOpenModal(false)}>
<Flex vertical={false} align={"center"}>
<h4>ID:</h4> <span style={{ marginLeft: 10 }}>{configDetail.id}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>endpoint: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.endpoint}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>accessKey: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.accessKey} </span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>secretKey: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.secretKey}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>status: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.status}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>createdTime: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.createdTime}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>updateTime: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.updateTime}</span>
</Flex>
</Modal>
</div>
</> </>
); );
}; });
export default QiniuSettings; export default QiniuSettings;

View File

@@ -2,37 +2,49 @@
import { PlusOutlined } from "@ant-design/icons"; import { PlusOutlined } from "@ant-design/icons";
import type { ActionType, ProColumns } from "@ant-design/pro-components"; import type { ActionType, ProColumns } from "@ant-design/pro-components";
import { ProTable, TableDropdown } from "@ant-design/pro-components"; import { ProTable, TableDropdown } from "@ant-design/pro-components";
import { Button, Col, Drawer, Form, Input, Row, Space } from "antd"; import { Button, Col, Drawer, Flex, Form, Input, message, Modal, Row, Space } from "antd";
import React, { useRef, useState } from "react"; import React, { ReactNode, useEffect, useRef, useState } from "react";
import {
addTencentOSSConfig,
deleteTencentConfig,
getAllTencentOSsConfig,
getTencentConfigDetailById,
initTencentOSS,
setTencentShutdown,
updateTencentConfig,
} from "@/api/oss/tencent";
import useStore from "@/utils/store/useStore.tsx";
import { observer } from "mobx-react";
type AliOssConfigItem = { type TencentOssConfigItem = {
id: number; id: number;
userId: number; userId: number;
bucketName: string; endpoint: number;
secretId: string; secretId: string;
secretKey: string; secretKey: string;
appId: string;
region: string;
createdTime: string; createdTime: string;
updatedTime: string; updateTime: string;
status: string; status: string;
}; };
const data: AliOssConfigItem[] = [
{
id: 1,
userId: 1,
bucketName: "https://oss.aliyuncs.com",
secretId: "LTAI5tG3",
secretKey: "G3",
createdTime: "2022-01-01",
updatedTime: "2022-01-01",
status: "正常",
},
];
const columns: ProColumns<AliOssConfigItem>[] = [ const TencentSettings: React.FC = observer(() => {
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<any>({} as any);
const userStore = useStore("user");
const userId: any = userStore.getUserId();
const columns: ProColumns<TencentOssConfigItem>[] = [
{ {
dataIndex: "index", dataIndex: "index",
valueType: "indexBorder", valueType: "indexBorder",
width: 48, width: 48,
editable: false,
}, },
{ {
title: "ID", title: "ID",
@@ -40,18 +52,21 @@ const columns: ProColumns<AliOssConfigItem>[] = [
copyable: true, copyable: true,
ellipsis: true, ellipsis: true,
tooltip: "Id", tooltip: "Id",
editable: false,
}, },
{ {
disable: true, disable: true,
title: "存储桶", title: "服务地址",
dataIndex: "bucketName", dataIndex: "endpoint",
tooltip: "bucketName", copyable: true,
tooltip: "endpoint",
ellipsis: true, ellipsis: true,
}, },
{ {
disable: true, disable: true,
title: "密钥ID", title: "密钥ID",
dataIndex: "secretId", dataIndex: "secretId",
copyable: true,
tooltip: "secret Id", tooltip: "secret Id",
ellipsis: true, ellipsis: true,
}, },
@@ -60,13 +75,24 @@ const columns: ProColumns<AliOssConfigItem>[] = [
title: "密钥值", title: "密钥值",
dataIndex: "secretKey", dataIndex: "secretKey",
tooltip: "secret Key", tooltip: "secret Key",
copyable: true,
ellipsis: true, ellipsis: true,
}, },
{ {
disable: true, disable: true,
title: "状态", title: "appId",
dataIndex: "status", dataIndex: "appId",
search: true, tooltip: "appId",
copyable: true,
ellipsis: true,
},
{
disable: true,
title: "地域",
dataIndex: "region",
tooltip: "region",
copyable: true,
ellipsis: true,
}, },
{ {
title: "创建时间", title: "创建时间",
@@ -74,12 +100,25 @@ const columns: ProColumns<AliOssConfigItem>[] = [
valueType: "dateTime", valueType: "dateTime",
sorter: true, sorter: true,
hideInSearch: true, hideInSearch: true,
editable: false,
}, },
{ {
title: "更新时间", title: "更新时间",
dataIndex: "updatedTime", dataIndex: "updateTime",
valueType: "dateTime", valueType: "dateTime",
sorter: true, sorter: true,
editable: false,
},
{
disable: true,
title: "状态",
dataIndex: "status",
search: true,
valueEnum: {
true: { text: "开启", status: "Success" },
false: { text: "关闭", status: "Error" },
},
editable: false,
}, },
{ {
title: "操作", title: "操作",
@@ -87,7 +126,7 @@ const columns: ProColumns<AliOssConfigItem>[] = [
key: "option", key: "option",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error // @ts-expect-error
render: (text, record, _, action) => [ render: (text: ReactNode, record: TencentOssConfigItem, _: number, action: any) => [
<a <a
key="editable" key="editable"
onClick={() => { onClick={() => {
@@ -95,24 +134,97 @@ const columns: ProColumns<AliOssConfigItem>[] = [
}}> }}>
</a>, </a>,
<a target="_blank" rel="noopener noreferrer" key="view"> <a
target="_blank"
rel="noopener noreferrer"
key="view"
onClick={() => {
getConfigDetail(record.id).then(() => {
setOpenModal(true);
setLoading(false);
});
}}>
</a>, </a>,
<TableDropdown <TableDropdown
key="actionGroup" key="actionGroup"
onSelect={() => action?.reload()} onSelect={(key: string) => {
if (key === "open") {
init(record.id).then();
} else if (key === "close") {
shutdown(record.id).then();
}
}}
menus={[ menus={[
{ key: "copy", name: "复制" }, { key: "open", name: "开启" },
{ key: "delete", name: "删除" }, { key: "close", name: "关闭" },
]} ]}
/>, />,
], ],
}, },
]; ];
const TencentSettings: React.FC = () => { async function init(id: any) {
const actionRef = useRef<ActionType>(); initTencentOSS(userId, id).then((res: any) => {
const [open, setOpen] = useState(false); 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(userId, 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 = () => { const showDrawer = () => {
setOpen(true); setOpen(true);
}; };
@@ -120,6 +232,42 @@ const TencentSettings: React.FC = () => {
const onClose = () => { const onClose = () => {
setOpen(false); setOpen(false);
}; };
async function getAllConfigs() {
getAllTencentOSsConfig(userId).then((res: any) => {
if (res && res.success && res.data) {
setConfigs(res.data);
}
});
}
async function addTencentConfig() {
const fieldsValue = form.getFieldsValue();
const TencentOssConfig = {
userId: userId,
...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();
}, []);
const AddTencentOssConfigDrawer = () => { const AddTencentOssConfigDrawer = () => {
return ( return (
<> <>
@@ -136,12 +284,12 @@ const TencentSettings: React.FC = () => {
extra={ extra={
<Space> <Space>
<Button onClick={onClose}></Button> <Button onClick={onClose}></Button>
<Button onClick={onClose} type="primary"> <Button onClick={addTencentConfig} type="primary">
</Button> </Button>
</Space> </Space>
}> }>
<Form layout="vertical"> <Form layout="vertical" form={form}>
<Row gutter={16}> <Row gutter={16}>
<Col span={12}> <Col span={12}>
<Form.Item <Form.Item
@@ -160,6 +308,24 @@ const TencentSettings: React.FC = () => {
</Form.Item> </Form.Item>
</Col> </Col>
</Row> </Row>
<Row gutter={16}>
<Col span={12}>
<Form.Item
name="appId"
label="appId"
rules={[{ required: true, message: "请输入appID!" }]}>
<Input placeholder="请输入appID" />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
name="region"
label="地区"
rules={[{ required: true, message: "请输入地区!" }]}>
<Input placeholder="请输入地区!" />
</Form.Item>
</Col>
</Row>
</Form> </Form>
</Drawer> </Drawer>
</> </>
@@ -167,13 +333,54 @@ const TencentSettings: React.FC = () => {
}; };
return ( return (
<> <>
<ProTable<AliOssConfigItem> <div style={{ height: "65vh" }}>
<ProTable<TencentOssConfigItem>
columns={columns} columns={columns}
dataSource={data} dataSource={configs}
actionRef={actionRef} actionRef={actionRef}
cardBordered={true} cardBordered={true}
editable={{ editable={{
type: "multiple", type: "multiple",
onSave: async (__: any, originRow: any, _: any) => {
const updateForm: any = {
id: originRow.id,
endpoint: originRow.endpoint,
secretId: originRow.secretId,
secretKey: originRow.secretKey,
appId: originRow.appId,
region: originRow.region,
};
updateTencentConfig(updateForm).then((res: any) => {
if (res && res.success) {
message.open({
content: "修改成功!",
type: "success",
});
getAllConfigs().then();
} else {
message.open({
content: res.data,
type: "error",
});
}
});
},
onDelete: async (row: any) => {
deleteConfig(row).then((res: any) => {
if (res && res.success) {
message.open({
content: "删除成功",
type: "success",
});
getAllConfigs().then();
} else {
message.open({
content: res.data,
type: "error",
});
}
});
},
}} }}
columnsState={{ columnsState={{
persistenceKey: "pro-table-singe-demos", persistenceKey: "pro-table-singe-demos",
@@ -189,9 +396,11 @@ const TencentSettings: React.FC = () => {
listsHeight: 400, listsHeight: 400,
}, },
}} }}
pagination={{ pagination={
{
pageSize: 5, pageSize: 5,
}} } as any
}
dateFormatter="string" dateFormatter="string"
headerTitle="腾讯云COS配置" headerTitle="腾讯云COS配置"
bordered={false} bordered={false}
@@ -208,8 +417,54 @@ const TencentSettings: React.FC = () => {
]} ]}
/> />
<AddTencentOssConfigDrawer /> <AddTencentOssConfigDrawer />
<Modal
title={<p></p>}
loading={loading}
footer={false}
open={openModal}
afterClose={() => {
setConfigDetail({});
}}
onCancel={() => setOpenModal(false)}>
<Flex vertical={false} align={"center"}>
<h4>ID:</h4> <span style={{ marginLeft: 10 }}>{configDetail.id}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>endpoint: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.endpoint}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>secretId: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.secretId} </span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>secretKey: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.secretKey}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>appId: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.appId}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>region: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.region}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>status: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.status}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>createdTime: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.createdTime}</span>
</Flex>
<Flex vertical={false} align={"center"}>
<h4>updateTime: </h4>{" "}
<span style={{ marginLeft: 10 }}>{configDetail.updateTime}</span>
</Flex>
</Modal>
</div>
</> </>
); );
}; });
export default TencentSettings; export default TencentSettings;

View File

@@ -3,9 +3,9 @@ import { PlusOutlined } from "@ant-design/icons";
import type { ActionType, ProColumns } from "@ant-design/pro-components"; import type { ActionType, ProColumns } from "@ant-design/pro-components";
import { ProTable, TableDropdown } from "@ant-design/pro-components"; import { ProTable, TableDropdown } from "@ant-design/pro-components";
import { Button, Col, Drawer, Form, Input, Row, Space } from "antd"; import { Button, Col, Drawer, Form, Input, Row, Space } from "antd";
import React, { useRef, useState } from "react"; import React, { ReactNode, useRef, useState } from "react";
type AliOssConfigItem = { type UcloudOssConfigItem = {
id: number; id: number;
userId: number; userId: number;
bucketName: string; bucketName: string;
@@ -16,7 +16,7 @@ type AliOssConfigItem = {
updatedTime: string; updatedTime: string;
status: string; status: string;
}; };
const data: AliOssConfigItem[] = [ const data: UcloudOssConfigItem[] = [
{ {
id: 1, id: 1,
userId: 1, userId: 1,
@@ -30,7 +30,7 @@ const data: AliOssConfigItem[] = [
}, },
]; ];
const columns: ProColumns<AliOssConfigItem>[] = [ const columns: ProColumns<UcloudOssConfigItem>[] = [
{ {
dataIndex: "index", dataIndex: "index",
valueType: "indexBorder", valueType: "indexBorder",
@@ -96,7 +96,7 @@ const columns: ProColumns<AliOssConfigItem>[] = [
key: "option", key: "option",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error // @ts-expect-error
render: (text, record, _, action) => [ render: (text: ReactNode, record: UcloudOssConfigItem, _: number, action: any) => [
<a <a
key="editable" key="editable"
onClick={() => { onClick={() => {
@@ -194,7 +194,8 @@ const UcloudSettings: React.FC = () => {
}; };
return ( return (
<> <>
<ProTable<AliOssConfigItem> <div style={{ height: "65vh" }}>
<ProTable<UcloudOssConfigItem>
columns={columns} columns={columns}
dataSource={data} dataSource={data}
actionRef={actionRef} actionRef={actionRef}
@@ -216,9 +217,11 @@ const UcloudSettings: React.FC = () => {
listsHeight: 400, listsHeight: 400,
}, },
}} }}
pagination={{ pagination={
{
pageSize: 5, pageSize: 5,
}} } as any
}
dateFormatter="string" dateFormatter="string"
headerTitle="UCloud US3配置" headerTitle="UCloud US3配置"
bordered={false} bordered={false}
@@ -235,6 +238,7 @@ const UcloudSettings: React.FC = () => {
]} ]}
/> />
<AddUcloudOssConfigDrawer /> <AddUcloudOssConfigDrawer />
</div>
</> </>
); );
}; };

View File

@@ -3,9 +3,9 @@ import { PlusOutlined } from "@ant-design/icons";
import type { ActionType, ProColumns } from "@ant-design/pro-components"; import type { ActionType, ProColumns } from "@ant-design/pro-components";
import { ProTable, TableDropdown } from "@ant-design/pro-components"; import { ProTable, TableDropdown } from "@ant-design/pro-components";
import { Button, Col, Drawer, Form, Input, Row, Space } from "antd"; import { Button, Col, Drawer, Form, Input, Row, Space } from "antd";
import React, { useRef, useState } from "react"; import React, { ReactNode, useRef, useState } from "react";
type AliOssConfigItem = { type UpOssConfigItem = {
id: number; id: number;
userId: number; userId: number;
bucketName: string; bucketName: string;
@@ -15,7 +15,7 @@ type AliOssConfigItem = {
updatedTime: string; updatedTime: string;
status: string; status: string;
}; };
const data: AliOssConfigItem[] = [ const data: UpOssConfigItem[] = [
{ {
id: 1, id: 1,
userId: 1, userId: 1,
@@ -28,7 +28,7 @@ const data: AliOssConfigItem[] = [
}, },
]; ];
const columns: ProColumns<AliOssConfigItem>[] = [ const columns: ProColumns<UpOssConfigItem>[] = [
{ {
dataIndex: "index", dataIndex: "index",
valueType: "indexBorder", valueType: "indexBorder",
@@ -87,7 +87,7 @@ const columns: ProColumns<AliOssConfigItem>[] = [
key: "option", key: "option",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error // @ts-expect-error
render: (text, record, _, action) => [ render: (text: ReactNode, record: UpOssConfigItem, _: number, action: any) => [
<a <a
key="editable" key="editable"
onClick={() => { onClick={() => {
@@ -177,7 +177,8 @@ const UpSettings: React.FC = () => {
}; };
return ( return (
<> <>
<ProTable<AliOssConfigItem> <div style={{ height: "65vh" }}>
<ProTable<UpOssConfigItem>
columns={columns} columns={columns}
dataSource={data} dataSource={data}
actionRef={actionRef} actionRef={actionRef}
@@ -199,9 +200,11 @@ const UpSettings: React.FC = () => {
listsHeight: 400, listsHeight: 400,
}, },
}} }}
pagination={{ pagination={
{
pageSize: 5, pageSize: 5,
}} } as any
}
dateFormatter="string" dateFormatter="string"
headerTitle="又拍云USS配置" headerTitle="又拍云USS配置"
bordered={false} bordered={false}
@@ -218,6 +221,7 @@ const UpSettings: React.FC = () => {
]} ]}
/> />
<AddUpOssConfigDrawer /> <AddUpOssConfigDrawer />
</div>
</> </>
); );
}; };

View File

@@ -3,9 +3,9 @@ import { PlusOutlined } from "@ant-design/icons";
import type { ActionType, ProColumns } from "@ant-design/pro-components"; import type { ActionType, ProColumns } from "@ant-design/pro-components";
import { ProTable, TableDropdown } from "@ant-design/pro-components"; import { ProTable, TableDropdown } from "@ant-design/pro-components";
import { Button, Col, Drawer, Form, Input, Row, Space } from "antd"; import { Button, Col, Drawer, Form, Input, Row, Space } from "antd";
import React, { useRef, useState } from "react"; import React, { ReactNode, useRef, useState } from "react";
type AliOssConfigItem = { type WangyiOssConfigItem = {
id: number; id: number;
userId: number; userId: number;
endpoint: string; endpoint: string;
@@ -16,7 +16,7 @@ type AliOssConfigItem = {
updatedTime: string; updatedTime: string;
status: string; status: string;
}; };
const data: AliOssConfigItem[] = [ const data: WangyiOssConfigItem[] = [
{ {
id: 1, id: 1,
userId: 1, userId: 1,
@@ -30,7 +30,7 @@ const data: AliOssConfigItem[] = [
}, },
]; ];
const columns: ProColumns<AliOssConfigItem>[] = [ const columns: ProColumns<WangyiOssConfigItem>[] = [
{ {
dataIndex: "index", dataIndex: "index",
valueType: "indexBorder", valueType: "indexBorder",
@@ -96,7 +96,7 @@ const columns: ProColumns<AliOssConfigItem>[] = [
key: "option", key: "option",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error // @ts-expect-error
render: (text, record, _, action) => [ render: (text: ReactNode, record: WangyiOssConfigItem, _: number, action: any) => [
<a <a
key="editable" key="editable"
onClick={() => { onClick={() => {
@@ -194,7 +194,8 @@ const WangyiSettings: React.FC = () => {
}; };
return ( return (
<> <>
<ProTable<AliOssConfigItem> <div style={{ height: "65vh" }}>
<ProTable<WangyiOssConfigItem>
columns={columns} columns={columns}
dataSource={data} dataSource={data}
actionRef={actionRef} actionRef={actionRef}
@@ -216,9 +217,11 @@ const WangyiSettings: React.FC = () => {
listsHeight: 400, listsHeight: 400,
}, },
}} }}
pagination={{ pagination={
{
pageSize: 5, pageSize: 5,
}} } as any
}
dateFormatter="string" dateFormatter="string"
headerTitle="阿里云OSS配置" headerTitle="阿里云OSS配置"
bordered={false} bordered={false}
@@ -235,6 +238,7 @@ const WangyiSettings: React.FC = () => {
]} ]}
/> />
<AddWangyiOssConfigDrawer /> <AddWangyiOssConfigDrawer />
</div>
</> </>
); );
}; };

View File

@@ -1,60 +0,0 @@
/** @format */
const selectOptions = [
{
name: "阿里云OSS",
value: "ali",
},
{
name: "腾讯云COS",
value: "tencent",
},
{
name: "华为云OBS",
value: "huawei",
},
{
name: "百度云BOS",
value: "baidu",
},
{
name: "MinIO",
value: "minio",
},
{
name: "京东云OSS",
value: "jd",
},
{
name: "亚马逊S3",
value: "aws",
},
{
name: "网易数帆NOS",
value: "wangyi",
},
{
name: "七牛云Kodo",
value: "qiniu",
},
{
name: "又拍云USS",
value: "up",
},
{
name: "平安云OSS",
value: "pinan",
},
{
name: "青云QingCloud",
value: "qingyun",
},
{
name: "UCloud US3",
value: "ucloud",
},
{
name: "金山云OBS",
value: "jinshan",
},
];
export default selectOptions;

View File

@@ -1,6 +1,7 @@
.settings_container{ .settings_container{
height: 75vh; width: 100%;
height: 100%;
} }
.settings_header{ .settings_header{
display: flex; display: flex;

View File

@@ -5,9 +5,10 @@ import { Avatar, Card, Empty, Select } from "antd";
import styles from "./index.module.less"; import styles from "./index.module.less";
import { Outlet, useLocation, useNavigate } from "react-router-dom"; import { Outlet, useLocation, useNavigate } from "react-router-dom";
import { Suspense } from "react"; import { Suspense } from "react";
import selectOptions from "@/components/Main/Settings/defaultSettings.ts"; import selectOptions from "@/components/Main/Settings/settings.ts";
import StorageIcon from "@/context/stroage-icon.ts"; import StorageIcon from "@/constant/stroage-icon.ts";
export default () => { import { observer } from "mobx-react";
export default observer(() => {
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
return ( return (
@@ -27,10 +28,13 @@ export default () => {
onSelect={(value: any) => { onSelect={(value: any) => {
navigate("/main/setting/" + value); navigate("/main/setting/" + value);
}} }}
placeholder={"请选择存储商"}> fieldNames={{
{selectOptions.map((storage: any, index: any) => { label: "name",
value: "value",
}}
labelRender={(label: any) => {
return ( return (
<Select.Option value={storage.value} key={index}> <>
<Card <Card
bordered={false} bordered={false}
style={{ style={{
@@ -40,25 +44,50 @@ export default () => {
flexDirection: "row", flexDirection: "row",
}} }}
size={"small"}> size={"small"}>
<Avatar src={StorageIcon[storage.value]} size={"small"} />{" "} <Avatar src={StorageIcon[label.value]} size={"small"} />
<span <span
style={{ style={{
marginLeft: "10px", marginLeft: "10px",
fontWeight: "bolder", fontWeight: "bolder",
}}> }}>
{storage.name} {label.label}
</span> </span>
</Card> </Card>
</Select.Option> </>
); );
})} }}
</Select> options={selectOptions}
optionRender={(item: any) => {
return (
<>
<Card
bordered={false}
style={{
height: 35,
display: "flex",
alignItems: "center",
flexDirection: "row",
}}
size={"small"}>
<Avatar src={StorageIcon[item.value]} size={"small"} />
<span
style={{
marginLeft: "10px",
fontWeight: "bolder",
}}>
{item.label}
</span>
</Card>
</>
);
}}
placeholder={"请选择存储商"}></Select>
</div> </div>
</ProCard> </ProCard>
<ProCard style={{ marginTop: 20, height: "60vh" }} bordered boxShadow> <ProCard style={{ marginTop: 20, height: "100%" }} bordered boxShadow>
{location.pathname === "/main/setting" || location.pathname === "/main/setting/" ? ( {location.pathname === "/main/setting" || location.pathname === "/main/setting/" ? (
<> <>
<Empty description={"请选择存储商"} /> <Empty description={"请选择存储商"} style={{ height: "65vh" }} />
</> </>
) : ( ) : (
<> <>
@@ -70,4 +99,4 @@ export default () => {
</ProCard> </ProCard>
</div> </div>
); );
}; });

View File

@@ -0,0 +1,60 @@
/** @format */
const selectOptions = [
{
name: "阿里云OSS",
value: "ali",
},
{
name: "腾讯云COS",
value: "tencent",
},
// {
// name: "华为云OBS",
// value: "huawei",
// },
// {
// name: "百度云BOS",
// value: "baidu",
// },
{
name: "MinIO",
value: "minio",
},
// {
// name: "京东云OSS",
// value: "jd",
// },
// {
// name: "亚马逊S3",
// value: "aws",
// },
// {
// name: "网易数帆NOS",
// value: "wangyi",
// },
{
name: "七牛云Kodo",
value: "qiniu",
},
// {
// name: "又拍云USS",
// value: "up",
// },
// {
// name: "平安云OSS",
// value: "pinan",
// },
// {
// name: "青云QingCloud",
// value: "qingyun",
// },
// {
// name: "UCloud US3",
// value: "ucloud",
// },
// {
// name: "金山云OBS",
// value: "jinshan",
// },
];
export default selectOptions;

View File

@@ -0,0 +1,377 @@
/** @format */
import React, { useEffect, useRef, useState } from "react";
import { AiEditor } from "aieditor";
import "aieditor/dist/style.css";
import { useNavigate } from "react-router-dom";
import styles from "./index.module.less";
import { ProCard } from "@ant-design/pro-components";
import type { ColorPickerProps } from "antd";
import {
Button,
Card,
Col,
ColorPicker,
Divider,
Flex,
Form,
FormListFieldData,
FormProps,
Input,
message,
Row,
Select,
theme,
} 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";
import { cyan, generate, green, presetPalettes, red } from "@ant-design/colors";
type Presets = Required<ColorPickerProps>["presets"][number];
const ShareAdd: React.FunctionComponent = () => {
const navigate = useNavigate();
const divRef = useRef(null);
const [form] = Form.useForm();
const [isDisabled, setIsDisabled] = useState(false);
const store = useStore("share");
const userStore = useStore("user");
const userId: any = userStore.getUserId();
const onFinish: FormProps["onFinish"] = (values) => {
const formData: any = {
circleId: store.getCircleId(),
userId: userId,
...values,
};
addShareDetail(formData).then((res: any) => {
if (res && res.success) {
message
.open({
content: "新增成功",
type: "success",
})
.then();
} else {
message
.open({
content: "新增失败",
type: "warning",
})
.then();
}
});
};
const genPresets = (presets: any = presetPalettes) =>
Object.entries(presets).map<Presets>(([label, colors]: [label: any, colors: any]) => ({
label,
colors,
}));
const HorizontalLayoutDemo = () => {
const { token } = theme.useToken();
const presets = genPresets({
primary: generate(token.colorPrimary),
red,
green,
cyan,
});
const customPanelRender: ColorPickerProps["panelRender"] = (
_,
{ components: { Picker, Presets } },
) => (
<Row justify="space-between" wrap={false}>
<Col span={12}>
<Presets />
</Col>
<Divider type="vertical" style={{ height: "auto" }} />
<Col flex="auto">
<Picker />
</Col>
</Row>
);
return (
<ColorPicker
defaultValue={token.colorPrimary}
styles={{ popupOverlayInner: { width: 480 } }}
presets={presets}
panelRender={customPanelRender}
/>
);
};
useEffect(() => {
if (divRef.current) {
const aiEditor = new AiEditor({
element: divRef.current,
placeholder: "点击输入描述...",
contentRetention: true,
textSelectionBubbleMenu: {
enable: true,
items: ["ai", "Bold", "Italic", "Underline", "Strike", "code", "comment"],
},
ai: {
models: {
wenxin: {
access_token: "****",
protocol: "ws",
version: "v3.5",
},
},
},
onChange: async (value: any) => {
form.setFieldValue("content" as any, value.getHtml());
},
} as any);
return () => {
aiEditor.destroy();
};
}
}, []);
return (
<>
<div className={styles.share_add_main}>
<ProCard bordered={true}>
<Flex vertical={false} align={"center"}>
<Button
shape="circle"
icon={<LeftOutlined />}
onClick={() => {
navigate("/main/share/list/" + store.getCircleId());
}}
/>
<Flex
vertical={false}
align={"center"}
justify={"center"}
style={{ width: "90%" }}>
<h3></h3>
</Flex>
</Flex>
</ProCard>
<div className={styles.share_add_content}>
<Form
onFinish={onFinish}
autoComplete="off"
form={form}
initialValues={{ color: "#1677ff" }}>
<Form.Item
label={
<>
<h4></h4>
</>
}
name="title"
id={"title"}
rules={[{ required: true, message: "请输入标题" }]}>
<Input name="title" maxLength={50} showCount></Input>
</Form.Item>
<Form.Item
label={
<>
<h4></h4>
</>
}
id={"icon"}
name="icon"
rules={[{ required: true, message: "请输入图标" }]}>
<Input name="icon"></Input>
</Form.Item>
<Form.Item
label={
<>
<h4></h4>
</>
}
id={"description"}
name="description"
rules={[{ required: true, message: "请输入摘要" }]}>
<Input name="description" maxLength={100} showCount></Input>
</Form.Item>
<Form.Item
label={
<>
<h4></h4>
</>
}
rules={[{ required: true, message: "请输入介绍" }]}
name="content"
id={"content"}>
<div
ref={divRef}
style={{ height: "300px", maxWidth: "100%", minWidth: "100%" }}
/>
</Form.Item>
<Form.List
name="tags"
rules={[
{
validator: async (_, tags) => {
if (!tags) {
return Promise.reject(new Error("请至少填写一个标签"));
}
if (tags.length >= 4) {
setIsDisabled(true);
return Promise.reject(
new Error("最多只能添加三个标签"),
);
}
},
},
]}>
{(fields: FormListFieldData[], { add, remove }, { errors }) => (
<>
{fields.map((field: FormListFieldData, index: number) => (
<Flex vertical={false} align={"center"} key={index}>
<Form.Item
required={true}
label={
<>
<h4>{"标签" + (index + 1)}</h4>
</>
}
key={index}
id={"tags"}>
<Flex vertical={false} align={"center"}>
<Form.Item
validateTrigger={["onChange", "onBlur"]}
name={[field.name, "tagName"] as any}
noStyle>
<Input
placeholder="请输入标签"
maxLength={10}
showCount
style={{ width: "250px" }}
/>
</Form.Item>
<Form.Item
validateTrigger={["onChange", "onBlur"]}
name={[field.name, "color"] as any}
noStyle>
<Input
addonBefore={"#"}
style={{ width: 150 }}></Input>
</Form.Item>
<HorizontalLayoutDemo />
{fields.length > 0 ? (
<MinusCircleOutlined
className="dynamic-delete-button"
onClick={() => {
setIsDisabled(false);
remove(field.name);
}}
/>
) : null}
</Flex>
</Form.Item>
</Flex>
))}
<Form.Item>
<Button
type="default"
disabled={isDisabled}
onClick={() => add()}
style={{ width: "100%" }}
icon={<PlusOutlined />}>
</Button>
<Form.ErrorList errors={errors} />
</Form.Item>
</>
)}
</Form.List>
<Form.List name="urls">
{(fields: FormListFieldData[], { add, remove }) => (
<div
style={{
display: "flex",
rowGap: 16,
flexDirection: "column",
}}>
{fields.map((field: FormListFieldData, index: number) => (
<Card
size="small"
title={
<>
<h4>{`链接 ${index + 1}`}</h4>
</>
}
key={index}
bordered
hoverable
extra={
<CloseOutlined
onClick={() => {
remove(field.name);
}}
/>
}>
<Form.Item
name={[field.name, "type"] as any}
label="分享类型">
<Select
size="middle"
style={{ width: "20%" }}
showSearch={true}
allowClear={true}
notFoundContent={"未找到哦"}
placeholder={"请选择分享类型"}>
{selectOptions.map(
(storage: any, index: any) => {
return (
<Select.Option
value={storage.value}
key={index}>
{storage.name}
</Select.Option>
);
},
)}
</Select>
</Form.Item>
<Form.Item
name={[field.name, "description"] as any}
label="资源描述">
<Input name={"description"} />
</Form.Item>
<Form.Item
name={[field.name, "url"] as any}
label="资源链接">
<Input name={"url"} />
</Form.Item>
<Form.Item
name={[field.name, "password"] as any}
label={"提取密码"}>
<Input name={"password"} />
</Form.Item>
</Card>
))}
<Button type="default" onClick={() => add()} block>
+
</Button>
</div>
)}
</Form.List>
<Form.Item
style={{
display: "flex",
flexDirection: "row",
justifyContent: "flex-end",
marginTop: "2%",
}}>
<Button type="primary" htmlType="submit">
</Button>
</Form.Item>
</Form>
</div>
</div>
</>
);
};
export default observer(ShareAdd);

View File

@@ -0,0 +1,7 @@
.share_add_main{
display: flex;
flex-direction: column;
.share_add_content{
margin-top: 2%;
}
}

View File

@@ -1,7 +0,0 @@
/** @format */
import React from "react";
const ShareList: React.FunctionComponent = () => {
return <></>;
};
export default ShareList;

View File

@@ -0,0 +1,374 @@
/** @format */
import React, { useEffect, useState } from "react";
import { Avatar, Button, Card, Divider, Flex, message, Skeleton, Tag, Tooltip } from "antd";
import {
CommentOutlined,
ExportOutlined,
EyeOutlined,
HeartOutlined,
InfoCircleOutlined,
LeftOutlined,
LikeOutlined,
StarOutlined,
TagsOutlined,
WarningOutlined,
} from "@ant-design/icons";
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 { Typography } from "antd";
import Comment from "@/components/Main/Share/components/ShareDetail/components/Comment";
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 { addLikeDetail, deletedLikeDetail, getShareDetail } from "@/api/share";
import StorageIcon from "@/constant/stroage-icon.ts";
import { observer } from "mobx-react";
import { addFavorites, deleteFavorites } from "@/api/user";
const ShareDetail: React.FunctionComponent = () => {
const navigate = useNavigate();
const store = useStore("share");
const userStore = useStore("user");
const userId: any = userStore.getUserId();
const params = useParams();
const [detail, setDetail] = useState<any>({} as 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);
}
});
}
async function addLike() {
const data: any = {
circleId: store.getCircleId(),
userId: userId,
detailId: params.id,
};
addLikeDetail(data).then();
}
async function delLike() {
const data: any = {
circleId: store.getCircleId(),
userId: userId,
detailId: params.id,
};
deletedLikeDetail(data).then();
}
async function setFavorites() {
const data: any = {
userId: userId,
detailId: params.id,
circleId: store.getCircleId(),
};
addFavorites(data).then((res: any) => {
if (res && res.success) {
message.open({
type: "success",
content: "收藏成功",
});
getDetail().then();
} else {
message.open({
type: "error",
content: "收藏失败",
});
}
});
}
async function cancelFavorites() {
const data: any = {
userId: userId,
detailId: params.id,
circleId: store.getCircleId(),
};
deleteFavorites(data).then((res: any) => {
if (res && res.success) {
message.open({
type: "success",
content: "取消成功",
});
getDetail().then();
} else {
message.open({
type: "error",
content: "取消失败",
});
}
});
}
useEffect(() => {
getDetail().then();
}, []);
return (
<>
<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={() => {
const circleId = store.getCircleId();
navigate("/main/share/list/" + circleId);
}}
/>
<Flex vertical={false} align={"center"}>
<h3>{detail.title as string}</h3>
</Flex>
<Flex
vertical={false}
align={"center"}
justify={"space-between"}
style={{ width: "20%" }}>
<Flex vertical={false} align={"center"}>
<Avatar src={detail.avatar as any} size={"small"} />
<span
style={{
fontSize: 12,
color: "gray",
overflow: "hidden",
}}>
{detail.nickname || "unknown"}
</span>
</Flex>
<Flex vertical={false} align={"center"}>
<HeartOutlined />
<span
style={{
fontSize: 12,
color: "gray",
}}>
{detail.likesCount || 0}
</span>
</Flex>
<Flex vertical={false} align={"center"}>
<CommentOutlined />
<span
style={{
fontSize: 12,
color: "gray",
}}>
{detail.commentCount || 0}
</span>
</Flex>
<Flex vertical={false} align={"center"}>
<EyeOutlined style={{ color: "gray" }} />{" "}
<span
style={{
fontSize: 12,
color: "gray",
}}>
{detail.views || 0}
</span>
</Flex>
</Flex>
</Flex>
</ProCard>
<div className={styles.share_detail_container}>
<ProCard bordered={true}>
<div
style={{ height: 500 }}
dangerouslySetInnerHTML={{ __html: detail.content }}></div>
<Card style={{ borderRadius: "10px", borderColor: "#1677FF" }}>
<Flex vertical={false} align={"center"} justify={"space-between"}>
<span
style={{
fontSize: 18,
color: "#1677FF",
fontWeight: "bolder",
}}>
</span>
<Button icon={<WarningOutlined />} type={"primary"}>
</Button>
</Flex>
<Flex
vertical={false}
align={"center"}
style={{ marginTop: 10 }}
justify={"space-between"}>
<span style={{ fontSize: 16 }}>{detail.title}</span>
</Flex>
<Flex
vertical={false}
align={"center"}
style={{ marginTop: 10 }}
justify={"space-between"}>
<span
style={{
fontSize: 18,
color: "#1677FF",
fontWeight: "bolder",
}}>
</span>
</Flex>
<Flex
vertical={false}
align={"center"}
style={{ marginTop: 10 }}
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,
}}>
<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,
}}>
{url.typeName}
</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>
);
})}
</Flex>
<Flex vertical={false} style={{ marginTop: 10 }}>
<span style={{ color: "grey" }}>
<InfoCircleOutlined />
使24
</span>
</Flex>
</Card>
<Flex vertical={false} align={"center"} style={{ marginTop: 20 }}>
<TagsOutlined style={{ fontSize: 30, color: "#1677FF" }} />
<Flex vertical={false} align={"center"} style={{ marginLeft: 10 }}>
{detail.tags &&
Array.from(detail.tags).map((tag: any, index: number) => {
return (
<div key={index}>
<Tag bordered={false} color={"#" + detail.color}>
{tag.tagName}
</Tag>
</div>
);
})}
</Flex>
</Flex>
<Flex
vertical={false}
align={"center"}
justify={"center"}
style={{ height: 50 }}>
{detail.isLike === true ? (
<>
<Tooltip title={"取消点赞"} placement={"bottom"}>
<Avatar
className={styles.like_icon}
src={like as any}
onClick={() => {
delLike().then();
}}
size={"large"}></Avatar>
</Tooltip>
</>
) : (
<>
<Tooltip title={"点赞"} placement={"bottom"}>
<LikeOutlined
className={styles.detail_link_btn}
onClick={() => {
addLike().then();
}}></LikeOutlined>
</Tooltip>
</>
)}
{detail.isFavor === true ? (
<>
<Tooltip title={"取消收藏"} placement={"bottom"}>
<Avatar
className={styles.favtorie_icon}
src={favorite as any}
onClick={() => {
cancelFavorites().then();
}}
size={"large"}></Avatar>
</Tooltip>
</>
) : (
<>
<Tooltip title={"收藏"} placement={"bottom"}>
<StarOutlined
onClick={() => {
setFavorites().then();
}}
className={styles.detail_link_btn}
/>
</Tooltip>
</>
)}
</Flex>
</ProCard>
</div>
<Comment auth={detail} />
</Skeleton>
</>
);
};
export default observer(ShareDetail);

View File

@@ -0,0 +1,19 @@
.like_icon {
font-size: 13px;
color: grey;
margin-left: 10px;
}
.like_icon:hover{
cursor: pointer;
color: #ce1111;
}
.comment_icon{
font-size: 13px;
color: grey;
margin-left: 10px;
}
.comment_icon:hover{
cursor: pointer;
color: #08a327;
}

View File

@@ -0,0 +1,939 @@
/** @format */
import { ProCard } from "@ant-design/pro-components";
import { Avatar, Button, Empty, Flex, Input, message, Segmented, Skeleton, Tag } from "antd";
import logo from "@/assets/icons/gitee.svg";
import {
BarsOutlined,
CloseOutlined,
CommentOutlined,
CrownOutlined,
FireOutlined,
LikeFilled,
LikeOutlined,
PictureOutlined,
SendOutlined,
SmileOutlined,
} from "@ant-design/icons";
import { useEffect, useState } from "react";
import styles from "./index.module.less";
import { useParams } from "react-router-dom";
import {
addComment,
addLikeComment,
addReply,
deletedLikeComment,
listComment,
listCommentHot,
listReply,
returnAllCommentAndReplyCount,
} from "@/api/share";
import useStore from "@/utils/store/useStore.tsx";
import { observer } from "mobx-react";
const Comment = observer((props: any) => {
const params = useParams();
const [isReply, setIsReply] = useState<any>(null);
const [isReplyReply, setIsReplyReply] = useState<any>(null);
const [loading, setLoading] = useState<boolean>(true);
const [isReplyComment, setIsReplyComment] = useState<any>(null);
const [comment, setComment] = useState<any[]>([]);
const [reply, setReply] = useState<any[]>([]);
const [commentData, setCommentData] = useState<any>("");
const [replyData, setReplyData] = useState<any>("");
const [replyReplyData, setReplyReplyData] = useState<any>("");
const [count, setCount] = useState<any>(0);
const userStore = useStore("user");
const userId: any = userStore.getUserId();
async function listComments() {
setComment([]);
listComment(params.id, userId).then((res: any) => {
if (res && res.success && res.data) {
setComment(res.data);
setLoading(false);
}
});
}
async function addLike(commentId: any) {
setComment([]);
const data: any = {
detailId: params.id,
userId: userId,
commentId: commentId,
};
addLikeComment(data).then();
listComments().then();
}
async function delLike(commentId: any) {
setComment([]);
const data: any = {
detailId: params.id,
userId: userId,
commentId: commentId,
};
deletedLikeComment(data).then();
listComments().then();
}
async function getCommentHot() {
setComment([]);
listCommentHot(params.id, userId).then((res: any) => {
if (res && res.success && res.data) {
setComment(res.data);
setLoading(false);
}
});
}
async function getAllCommentAndReply(detailId: any) {
returnAllCommentAndReplyCount(detailId).then((res: any) => {
if (res && res.success && res.data) {
setCount(res.data);
}
});
}
async function addComments(data: any) {
addComment(data).then((res: any) => {
if (res && res.success) {
message.open({
content: "评论成功",
type: "success",
});
listComments().then();
} else {
message.open({
content: "评论失败",
type: "error",
});
}
});
}
async function replyComment(commentId: any) {
listReply(commentId, userId).then((res: any) => {
if (res && res.success && res.data) {
setReply(res.data);
}
});
}
async function addReplies(data: any) {
addReply(data).then((res: any) => {
if (res && res.success) {
message.open({
content: "回复成功",
type: "success",
});
listComments().then();
} else {
message.open({
content: "回复失败",
type: "error",
});
}
});
}
const handleExpandClick = (index: any) => {
setIsReply(isReply === index ? null : index);
};
const handleExpandReplyReplyClick = (index: any) => {
setIsReplyReply(isReplyReply === index ? null : index);
};
const handleExpandReplyCommentClick = (index: any) => {
setIsReplyComment(isReplyComment === index ? null : index);
};
useEffect(() => {
getCommentHot().then();
getAllCommentAndReply(params.id).then();
}, []);
return (
<>
<div>
<ProCard title={"评论 " + count}>
<Skeleton active={true} loading={loading} paragraph={{ rows: 2 }}>
<Flex vertical={false}>
<Flex vertical={true} justify={"flex-start"}>
<Avatar src={logo as any} size={"large"} />
</Flex>
<Flex vertical={true} style={{ width: "100%" }}>
<Input.TextArea
style={{ marginLeft: 10 }}
rows={5}
maxLength={500}
onChange={(e: any) => {
setCommentData(e.target.value);
}}
showCount
placeholder={"平等表达,友善交流"}></Input.TextArea>
<Flex
vertical={false}
align={"center"}
style={{ marginTop: 10, marginLeft: 10 }}>
<SmileOutlined style={{ fontSize: 20, color: "grey" }} />
<PictureOutlined
style={{ marginLeft: 20, fontSize: 20, color: "grey" }}
/>
</Flex>
<Flex vertical={false} align={"center"} justify={"flex-end"}>
<Button
icon={<SendOutlined />}
type={"primary"}
onClick={() => {
const data: any = {
userId: userId,
detailId: params.id,
content: commentData,
};
addComments(data).then(() => {
listComments().then();
});
}}>
</Button>
</Flex>
</Flex>
</Flex>
<Flex vertical={true}>
<Segmented
block={false}
style={{ width: 145 }}
size={"middle"}
defaultValue={"hot"}
onChange={(value: any) => {
if (value === "hot") {
getCommentHot().then();
}
if (value === "new") {
listComments().then();
}
}}
options={[
{
label: "最热",
value: "hot",
icon: <FireOutlined />,
},
{
label: "最新",
value: "new",
icon: <BarsOutlined />,
},
]}
/>
{comment.length === 0 ? (
<Empty description={"暂无评论!"} />
) : (
<>
{comment &&
Array.from(comment).map((item: any, index: number) => {
return (
<Flex
key={index}
vertical={true}
style={{ marginTop: 20, width: "100%" }}>
<Flex vertical={false}>
<Flex
vertical={true}
justify={"flex-start"}>
<Avatar
src={item.avatar as any}
size={"large"}></Avatar>
</Flex>
<Flex
vertical={true}
style={{
marginLeft: 15,
width: "100%",
}}>
<Flex vertical={false} align={"center"}>
<span
style={{
fontSize: 16,
fontWeight: 500,
}}>
{item.nick}
</span>
{props.auth.userId ==
item.userId ? (
<Tag
bordered={false}
icon={<CrownOutlined />}
color={"cyan"}
style={{ marginLeft: 5 }}>
</Tag>
) : null}
</Flex>
<ProCard
bordered={true}
boxShadow={false}
extra={<></>}
style={{ width: "100%" }}>
<span>{item.content}</span>
<Flex
vertical={false}
align={"center"}
justify={"space-between"}>
<Flex
vertical={false}
align={"center"}
style={{
marginTop: 10,
width: "100%",
}}>
<span
style={{
fontSize: 13,
color: "grey",
}}>
{item.createdTime}
</span>
<Flex
vertical={false}
align={"center"}>
{item.isLike ===
true ? (
<LikeFilled
onClick={() => {
delLike(
item.id,
).then();
}}
style={{
color: "red",
}}
/>
) : (
<LikeOutlined
className={
styles.like_icon
}
onClick={() => {
addLike(
item.id,
).then();
}}
/>
)}
<span
style={{
fontSize: 13,
color: "grey",
}}>
{item.likes}
</span>
</Flex>
<Flex
vertical={false}
align={"center"}>
<CommentOutlined
onClick={() => {
setReply([]);
if (item.id) {
replyComment(
item.id,
).then(
() => {
handleExpandReplyCommentClick(
index,
);
},
);
}
}}
className={
styles.comment_icon
}
/>
<span
style={{
fontSize: 13,
color: "grey",
}}>
{item.replyCount}
</span>
</Flex>
<Flex
vertical={false}
align={"center"}>
<Button
type="text"
size={"small"}
onClick={() => {
handleExpandClick(
index,
);
}}
style={{
fontSize: 13,
color: "grey",
}}>
</Button>
</Flex>
</Flex>
<Flex
vertical={false}
align={"center"}>
<span
style={{
textWrap: "nowrap",
fontSize: 12,
color: "grey",
textAlign: "end",
}}>
{item.location ||
"unknown"}
</span>
<span
style={{
marginLeft: 10,
fontSize: 13,
color: "grey",
}}>
{item.browser ||
"unknown"}
</span>
<span
style={{
marginLeft: 10,
fontSize: 13,
color: "grey",
}}>
{item.browserVersion ||
"unknown"}
</span>
</Flex>
</Flex>
</ProCard>
{isReply === index && (
<Flex vertical={true}>
<Flex
vertical={false}
align={"center"}>
<span
style={{
fontSize: 13,
}}>
</span>
<span
style={{
fontSize: 13,
}}>
{item.nick}
</span>
<Button
icon={<CloseOutlined />}
style={{
fontSize: 13,
marginLeft: 5,
}}
type={"dashed"}
onClick={() => {
setIsReply(null);
}}
size={"small"}>
</Button>
</Flex>
<Flex
vertical={false}
style={{ marginTop: 5 }}>
<Flex
vertical={true}
justify={"flex-start"}>
<Avatar
src={logo as any}
size={"default"}
/>
</Flex>
<Flex
vertical={true}
style={{
width: "100%",
}}>
<Input.TextArea
style={{
marginLeft: 10,
width: "100%",
}}
rows={3}
maxLength={500}
showCount
onChange={(e) => {
setReplyData(
e.target
.value,
);
}}
placeholder={
"平等表达,友善交流"
}></Input.TextArea>
<Flex
vertical={false}
align={"center"}
style={{
marginTop: 10,
marginLeft: 10,
}}>
<SmileOutlined
style={{
fontSize: 20,
color: "grey",
}}
/>
<PictureOutlined
style={{
marginLeft: 20,
fontSize: 20,
color: "grey",
}}
/>
</Flex>
<Flex
vertical={false}
align={"center"}
justify={
"flex-end"
}>
<Button
onClick={() => {
const data: any =
{
userId: userId,
detailId:
params.id,
content:
replyData,
toId: item.id,
replyId:
item.id,
};
addReplies(
data,
).then();
}}
icon={
<SendOutlined />
}
type={
"primary"
}>
</Button>
</Flex>
</Flex>
</Flex>
</Flex>
)}
{isReplyComment === index &&
reply &&
Array.from(reply).map(
(
replyItem: any,
replyIndex: number,
) => {
return (
<Flex
key={replyIndex}
vertical={false}>
<Flex
vertical={true}
justify={
"flex-start"
}>
<Avatar
src={
replyItem.avatar as any
}
size={
"default"
}></Avatar>
</Flex>
<Flex
vertical={true}
style={{
marginLeft: 15,
width: "100%",
}}>
<Flex
vertical={
false
}
align={
"center"
}>
<span
style={{
fontSize: 14,
fontWeight: 500,
}}>
{
replyItem.nick
}{" "}
{
replyItem.nickto
}
</span>
{props.auth
.userId ==
replyItem.userId ? (
<Tag
bordered={
false
}
icon={
<CrownOutlined />
}
color={
"cyan"
}
style={{
marginLeft: 5,
}}>
</Tag>
) : null}
</Flex>
<ProCard
bordered={
true
}
boxShadow={
false
}
extra={
<></>
}
style={{
width: "100%",
}}>
<span>
{
replyItem.content
}
</span>
<Flex
vertical={
false
}
align={
"center"
}
justify={
"space-between"
}>
<Flex
vertical={
false
}
style={{
marginTop: 10,
width: "100%",
}}>
<span
style={{
fontSize: 13,
color: "grey",
}}>
{
replyItem.createdTime
}
</span>
<Flex
vertical={
false
}
align={
"center"
}>
{replyItem.isLike ===
false ? (
<LikeOutlined
onClick={() => {
addLike(
replyItem.id,
).then();
}}
className={
styles.like_icon
}
/>
) : (
<LikeFilled
className={
styles.like_icon
}
style={{
color: "red",
}}
onClick={() => {
delLike(
replyItem.id,
).then();
}}
/>
)}
<span
style={{
fontSize: 13,
color: "grey",
}}>
{
replyItem.likes
}
</span>
</Flex>
<Flex
vertical={
false
}
align={
"center"
}>
<Button
type="text"
size={
"small"
}
onClick={() => {
handleExpandReplyReplyClick(
replyItem.id,
);
}}
style={{
fontSize: 13,
color: "grey",
}}>
</Button>
</Flex>
</Flex>
<Flex
vertical={
false
}
align={
"center"
}>
<span
style={{
textWrap:
"nowrap",
fontSize: 12,
color: "grey",
textAlign:
"end",
}}>
{replyItem.location ||
"unknown"}
</span>
<span
style={{
marginLeft: 10,
fontSize: 13,
color: "grey",
}}>
{replyItem.browser ||
"unknown"}
</span>
<span
style={{
marginLeft: 10,
fontSize: 13,
color: "grey",
}}>
{replyItem.browserVersion ||
"unknown"}
</span>
</Flex>
</Flex>
</ProCard>
{isReplyReply ===
replyItem.id && (
<>
<Flex
vertical={
true
}>
<Flex
vertical={
false
}
align={
"center"
}>
<span
style={{
fontSize: 13,
}}>
</span>
<span
style={{
fontSize: 13,
}}>
{
replyItem.nick
}
</span>
<Button
icon={
<CloseOutlined />
}
style={{
fontSize: 13,
marginLeft: 5,
}}
type={
"dashed"
}
onClick={() => {
setIsReplyReply(
null,
);
}}
size={
"small"
}>
</Button>
</Flex>
<Flex
vertical={
false
}
style={{
marginTop: 5,
}}>
<Flex
vertical={
true
}
justify={
"flex-start"
}>
<Avatar
src={
logo as any
}
size={
"default"
}
/>
</Flex>
<Flex
vertical={
true
}
style={{
width: "100%",
}}>
<Input.TextArea
style={{
marginLeft: 10,
width: "100%",
}}
rows={
3
}
onChange={(
e: any,
) => {
setReplyReplyData(
e
.target
.value,
);
}}
maxLength={
500
}
showCount
placeholder={
"平等表达,友善交流"
}></Input.TextArea>
<Flex
vertical={
false
}
align={
"center"
}
style={{
marginTop: 10,
marginLeft: 10,
}}>
<SmileOutlined
style={{
fontSize: 20,
color: "grey",
}}
/>
<PictureOutlined
style={{
marginLeft: 20,
fontSize: 20,
color: "grey",
}}
/>
</Flex>
<Flex
vertical={
false
}
align={
"center"
}
justify={
"flex-end"
}>
<Button
onClick={() => {
const data: any =
{
userId: userId,
detailId:
params.id,
content:
replyReplyData,
toId: item.id,
replyId:
replyItem.id,
};
addReplies(
data,
).then();
}}
icon={
<SendOutlined />
}
type={
"primary"
}>
</Button>
</Flex>
</Flex>
</Flex>
</Flex>
</>
)}
</Flex>
</Flex>
);
},
)}
</Flex>
</Flex>
</Flex>
);
})}
</>
)}
</Flex>
</Skeleton>
</ProCard>
</div>
</>
);
});
export default Comment;

View File

@@ -0,0 +1,30 @@
.share_detail_container{
margin-top: 10px;
}
.link_btn{
font-size: 20px;
color: grey;
}
.link_btn:hover{
color: #1890ff;
}
.like_icon:hover{
cursor: pointer;
width: 50px;
height: auto;
}
.favtorie_icon{
margin-left: 20px;
}
.favtorie_icon:hover{
cursor: pointer;
width: 50px;
height: auto;
}
.detail_link_btn{
font-size: 30px;
}
.detail_link_btn:hover{
font-size: 35px;
color: red;
}

View File

@@ -1,86 +0,0 @@
/** @format */
import { LikeOutlined, MessageOutlined, StarOutlined } from "@ant-design/icons";
import { ProList } from "@ant-design/pro-components";
import { Button, Tag } from "antd";
import React from "react";
const IconText = ({ icon, text }: { icon: any; text: string }) => (
<span>
{React.createElement(icon, { style: { marginInlineEnd: 8 } })}
{text}
</span>
);
const dataSource = [
{
title: "语雀的天空",
},
{
title: "Ant Design",
},
{
title: "蚂蚁金服体验科技",
},
{
title: "TechUI",
},
];
const ShareList = () => {
return (
<ProList<{ title: string }>
toolBarRender={() => {
return [
<Button key="3" type="primary">
</Button>,
];
}}
itemLayout="vertical"
rowKey="id"
// headerTitle="竖排样式"
dataSource={dataSource}
metas={{
title: {},
description: {
render: () => (
<>
<Tag></Tag>
<Tag></Tag>
<Tag></Tag>
</>
),
},
actions: {
render: () => [
<IconText icon={StarOutlined} text="156" key="list-vertical-star-o" />,
<IconText icon={LikeOutlined} text="156" key="list-vertical-like-o" />,
<IconText icon={MessageOutlined} text="2" key="list-vertical-message" />,
],
},
extra: {
render: () => (
<img
width={272}
alt="logo"
src="https://gw.alipayobjects.com/zos/rmsportal/mqaQswcyDLcXyDKnZfES.png"
/>
),
},
content: {
render: () => {
return (
<div>
design.alipay.com
design.alipay.com
</div>
);
},
},
}}
/>
);
};
export default ShareList;

View File

@@ -0,0 +1,192 @@
/** @format */
import { ProCard } from "@ant-design/pro-components";
import { Avatar, Button, Flex, Input, List, Skeleton, Tag } from "antd";
import { useEffect, useState } from "react";
import styles from "./index.module.less";
import { Link, useNavigate, useParams } from "react-router-dom";
import {
CommentOutlined,
EyeOutlined,
HeartOutlined,
LeftOutlined,
ShareAltOutlined,
} from "@ant-design/icons";
import { shareDetailList } from "@/api/share";
import useStore from "@/utils/store/useStore.tsx";
import { observer } from "mobx-react";
interface DataType {
gender: string;
name: {
title: string;
first: string;
last: string;
};
email: string;
picture: {
large: string;
medium: string;
thumbnail: string;
};
nat: string;
}
export default observer(() => {
const navigate = useNavigate();
const params = useParams();
const [loading, setLoading] = useState(true);
const [data, setData] = useState<DataType[]>([]);
const store = useStore("share");
async function getShareDetailList() {
store.setCircleId(params.id as string);
shareDetailList(params.id).then((res: any) => {
console.log(res);
if (res && res.data && res.data) {
setData(res.data);
setLoading(false);
}
});
}
useEffect(() => {
getShareDetailList().then();
}, []);
return (
<>
<div className={styles.share_list_main}>
<ProCard bordered={false} boxShadow={false}>
<div className={styles.share_list_header}>
<div>
<Button
shape="circle"
icon={<LeftOutlined />}
onClick={() => {
navigate("/main/share");
}}
/>
<Input
placeholder="搜索"
style={{ borderRadius: 20, width: 500, marginLeft: 20 }}
/>
</div>
<Button
icon={<ShareAltOutlined />}
type={"primary"}
onClick={() => {
navigate("/main/share/add");
}}>
</Button>
</div>
</ProCard>
<ProCard bordered={false} boxShadow={false}>
<Skeleton loading={loading} active={true} paragraph={{ rows: 14 }}>
<List
dataSource={data}
header={
<>
<h4></h4>
</>
}
renderItem={(item: any) => (
<List.Item key={item.id}>
<List.Item.Meta
avatar={<Avatar src={item.icon} />}
title={
<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={"#" + tag.color}
style={{ marginLeft: 10 }}>
{tag.tagName}
</Tag>
</Flex>
);
},
)}
</Flex>
}
description={
<>
<Flex
vertical={false}
justify={"space-between"}
align={"center"}>
{item.description}
<Flex
vertical={false}
align={"center"}
justify={"space-between"}
style={{ width: "300px" }}>
<Flex vertical={false} align={"center"}>
<Avatar
src={item.avatar as any}
size={"small"}
/>
<span
style={{
fontSize: 12,
color: "gray",
overflow: "hidden",
}}>
{item.nickname}
</span>
</Flex>
<Flex vertical={false} align={"center"}>
<HeartOutlined />
<span
style={{
fontSize: 12,
color: "gray",
}}>
{item.likesCount}
</span>
</Flex>
<Flex vertical={false} align={"center"}>
<CommentOutlined />
<span
style={{
fontSize: 12,
color: "gray",
}}>
{item.commentCount}
</span>
</Flex>
<Flex vertical={false} align={"center"}>
<EyeOutlined
style={{ color: "gray" }}
/>{" "}
<span
style={{
fontSize: 12,
color: "gray",
}}>
{item.views}
</span>
</Flex>
</Flex>
</Flex>
</>
}
/>
</List.Item>
)}
/>
</Skeleton>
</ProCard>
</div>
</>
);
});

View File

@@ -0,0 +1,10 @@
.share_list_main{
display: flex;
flex-direction: column;
min-height: 83vh;
.share_list_header{
display: flex;
flex-direction: row;
justify-content: space-between;
}
}

View File

@@ -1,10 +1,11 @@
.share_main{ .share_main{
min-height: 83vh;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
.share_header{ .share_header{
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: center;
align-items: center; align-items: center;
} }
} }

View File

@@ -1,39 +1,257 @@
/** @format */ /** @format */
import { FunctionComponent } from "react"; import { FunctionComponent, useEffect, useState } from "react";
import { ProCard } from "@ant-design/pro-components"; import { ProCard } from "@ant-design/pro-components";
import styles from "./index.module.less"; import styles from "./index.module.less";
import { Avatar, Button, Card } from "antd"; import {
import setting from "@/assets/icons/setting.svg"; Avatar,
import Search from "antd/es/input/Search"; Button,
import Meta from "antd/es/card/Meta"; Card,
Drawer,
Flex,
FloatButton,
Form,
Image,
Input,
message,
Skeleton,
Tooltip,
} from "antd";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
const MainShare: FunctionComponent = () => { import TextArea from "antd/es/input/TextArea";
import { EyeOutlined, PlusOutlined, UnorderedListOutlined } from "@ant-design/icons";
import Meta from "antd/es/card/Meta";
import { addShareCircle, getShareCircleList } from "@/api/share";
import useStore from "@/utils/store/useStore.tsx";
import { observer } from "mobx-react";
const MainShare: FunctionComponent = observer(() => {
const store = useStore("user");
const userId: any = store.getUserId();
const navigate = useNavigate(); 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 ( return (
<> <>
<div className={styles.share_main}> <div className={styles.share_main}>
<ProCard bordered={true} boxShadow={true}> <ProCard bordered={false} boxShadow={false}>
<div className={styles.share_header}> <div className={styles.share_header}>
<Search placeholder="搜索圈子" style={{ width: 500 }} /> <Input
<Button></Button> size={"large"}
</div> placeholder="搜索圈子"
</ProCard> style={{ width: 500, borderRadius: 20 }}
<ProCard bordered={true} boxShadow={true} style={{ marginTop: 20 }}>
<Card
style={{ width: 250, marginTop: 16 }}
hoverable
onClick={() => {
navigate("/main/share/list/1");
}}>
<Meta
avatar={<Avatar src={setting} />}
title="Card title"
description="This is the description"
/> />
</div>
</ProCard>
<ProCard title={<h3></h3>} bordered={false} boxShadow={false}>
<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> </Card>
</div>
);
})}
</Skeleton>
</Flex>
</ProCard> </ProCard>
</div> </div>
<Drawer
title="创建圈子"
width={"40%"}
onClose={onClose}
open={open}
styles={{
body: {
paddingBottom: 80,
},
}}
// extra={
// <Space>
// <Button onClick={onClose}>取消</Button>
// <Button onClick={onClose} type="primary">
// 提交
// </Button>
// </Space>
// }
>
<Form
layout="vertical"
form={form}
onFinish={(values: any) => {
const formData: any = {
userId: userId,
...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="名称"
rules={[{ required: true, message: "请输入圈子名称!" }]}>
<Input maxLength={30} showCount placeholder="请输入圈子名称!" />
</Form.Item>
<Form.Item
name="icon"
label="图标"
rules={[{ required: true, message: "请输入图标!" }]}>
<Input placeholder="请输入图标!" />
</Form.Item>
<Form.Item
name="description"
label="描述"
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
type={"primary"}
style={{ right: "5%", width: 50, height: 50 }}
icon={<PlusOutlined />}
onClick={() => setOpen(true)}
/>
</> </>
); );
}; });
export default MainShare; export default MainShare;

View File

@@ -0,0 +1,10 @@
.share_list_main{
display: flex;
flex-direction: column;
min-height: 83vh;
.share_list_header{
display: flex;
flex-direction: row;
justify-content: space-between;
}
}

View File

@@ -0,0 +1,187 @@
/** @format */
import { ProCard } from "@ant-design/pro-components";
import { Avatar, Button, Empty, Flex, Input, List, Skeleton, Tag } from "antd";
import { useEffect, useState } from "react";
import styles from "./index.module.less";
import { Link, useNavigate } from "react-router-dom";
import { CommentOutlined, EyeOutlined, HeartOutlined } from "@ant-design/icons";
import { getMyFavor } from "@/api/share";
import useStore from "@/utils/store/useStore.tsx";
import { observer } from "mobx-react";
export default observer(() => {
const navigate = useNavigate();
const [loading, setLoading] = useState(true);
const [data, setData] = useState<[]>([]);
const store = useStore("user");
const userId: any = store.getUserId();
async function getMyShare() {
getMyFavor(userId).then((res: any) => {
if (res && res.success && res.data) {
setData(res.data);
setLoading(false);
}
});
}
useEffect(() => {
getMyShare().then();
}, []);
return (
<>
<div className={styles.share_list_main}>
<ProCard bordered={false} boxShadow={false}>
<div className={styles.share_list_header}>
<Flex
vertical={false}
align={"center"}
justify={"space-between"}
style={{ width: "100%" }}>
<div>
<Input
placeholder="搜索"
style={{ borderRadius: 20, width: 500, marginLeft: 20 }}
/>
</div>
<div>
<Button
type={"primary"}
onClick={() => {
navigate("/main/share/add");
}}>
</Button>
</div>
</Flex>
</div>
</ProCard>
<ProCard bordered={false} boxShadow={false}>
{data.length === 0 ? (
<Empty description={"暂无数据"}></Empty>
) : (
<>
<Skeleton loading={loading} active={true} paragraph={{ rows: 14 }}>
<List
dataSource={data}
header={
<>
<h4></h4>
</>
}
renderItem={(item: any) => (
<List.Item key={item.id}>
<List.Item.Meta
avatar={<Avatar src={item.icon} />}
title={
<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={
"#" + tag.color
}
style={{
marginLeft: 10,
}}>
{tag.tagName}
</Tag>
</Flex>
);
},
)}
</Flex>
}
description={
<>
<Flex
vertical={false}
justify={"space-between"}
align={"center"}>
{item.description}
<Flex
vertical={false}
align={"center"}
justify={"space-between"}
style={{ width: "300px" }}>
<Flex
vertical={false}
align={"center"}>
<Avatar
src={item.avatar as any}
size={"small"}
/>
<span
style={{
fontSize: 12,
color: "gray",
overflow: "hidden",
}}>
{item.nickname}
</span>
</Flex>
<Flex
vertical={false}
align={"center"}>
<HeartOutlined />
<span
style={{
fontSize: 12,
color: "gray",
}}>
{item.likesCount}
</span>
</Flex>
<Flex
vertical={false}
align={"center"}>
<CommentOutlined />
<span
style={{
fontSize: 12,
color: "gray",
}}>
{item.commentCount}
</span>
</Flex>
<Flex
vertical={false}
align={"center"}>
<EyeOutlined
style={{ color: "gray" }}
/>{" "}
<span
style={{
fontSize: 12,
color: "gray",
}}>
{item.views}
</span>
</Flex>
</Flex>
</Flex>
</>
}
/>
</List.Item>
)}
/>
</Skeleton>
</>
)}
</ProCard>
</div>
</>
);
});

View File

@@ -0,0 +1,10 @@
.share_list_main{
display: flex;
flex-direction: column;
min-height: 83vh;
.share_list_header{
display: flex;
flex-direction: row;
justify-content: space-between;
}
}

View File

@@ -0,0 +1,187 @@
/** @format */
import { ProCard } from "@ant-design/pro-components";
import { Avatar, Button, Empty, Flex, Input, List, Skeleton, Tag } from "antd";
import { useEffect, useState } from "react";
import styles from "./index.module.less";
import { Link, useNavigate } from "react-router-dom";
import { CommentOutlined, EyeOutlined, HeartOutlined } from "@ant-design/icons";
import { getMyShareList } from "@/api/share";
import useStore from "@/utils/store/useStore.tsx";
import { observer } from "mobx-react";
export default observer(() => {
const navigate = useNavigate();
const [loading, setLoading] = useState(true);
const [data, setData] = useState<[]>([]);
const store = useStore("user");
const userId: any = store.getUserId();
async function getMyShare() {
getMyShareList(userId).then((res: any) => {
if (res && res.success && res.data) {
setData(res.data);
setLoading(false);
}
});
}
useEffect(() => {
getMyShare().then();
}, []);
return (
<>
<div className={styles.share_list_main}>
<ProCard bordered={false} boxShadow={false}>
<div className={styles.share_list_header}>
<Flex
vertical={false}
align={"center"}
justify={"space-between"}
style={{ width: "100%" }}>
<div>
<Input
placeholder="搜索"
style={{ borderRadius: 20, width: 500, marginLeft: 20 }}
/>
</div>
<div>
<Button
type={"primary"}
onClick={() => {
navigate("/main/share/add");
}}>
</Button>
</div>
</Flex>
</div>
</ProCard>
<ProCard bordered={false} boxShadow={false}>
{data.length === 0 ? (
<Empty description={"暂无数据"}></Empty>
) : (
<>
<Skeleton loading={loading} active={true} paragraph={{ rows: 14 }}>
<List
dataSource={data}
header={
<>
<h4></h4>
</>
}
renderItem={(item: any) => (
<List.Item key={item.id}>
<List.Item.Meta
avatar={<Avatar src={item.icon} />}
title={
<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={
"#" + tag.color
}
style={{
marginLeft: 10,
}}>
{tag.tagName}
</Tag>
</Flex>
);
},
)}
</Flex>
}
description={
<>
<Flex
vertical={false}
justify={"space-between"}
align={"center"}>
{item.description}
<Flex
vertical={false}
align={"center"}
justify={"space-between"}
style={{ width: "300px" }}>
<Flex
vertical={false}
align={"center"}>
<Avatar
src={item.avatar as any}
size={"small"}
/>
<span
style={{
fontSize: 12,
color: "gray",
overflow: "hidden",
}}>
{item.nickname}
</span>
</Flex>
<Flex
vertical={false}
align={"center"}>
<HeartOutlined />
<span
style={{
fontSize: 12,
color: "gray",
}}>
{item.likesCount}
</span>
</Flex>
<Flex
vertical={false}
align={"center"}>
<CommentOutlined />
<span
style={{
fontSize: 12,
color: "gray",
}}>
{item.commentCount}
</span>
</Flex>
<Flex
vertical={false}
align={"center"}>
<EyeOutlined
style={{ color: "gray" }}
/>{" "}
<span
style={{
fontSize: 12,
color: "gray",
}}>
{item.views}
</span>
</Flex>
</Flex>
</Flex>
</>
}
/>
</List.Item>
)}
/>
</Skeleton>
</>
)}
</ProCard>
</div>
</>
);
});

View File

@@ -1,38 +1,50 @@
/** @format */ /** @format */
import { FunctionComponent, useEffect, useState } from "react"; import { FunctionComponent, useEffect, useState } from "react";
import { Avatar, Card, Empty, List, Skeleton } from "antd"; import { Avatar, Card, Empty, Flex, List, Skeleton } from "antd";
import { import { BankOutlined, BulbOutlined, EnvironmentOutlined } from "@ant-design/icons";
AntDesignOutlined,
BankOutlined,
BulbOutlined,
EnvironmentOutlined,
} from "@ant-design/icons";
import styles from "./index.module.less"; import styles from "./index.module.less";
import { ProCard } from "@ant-design/pro-components"; import { ProCard } from "@ant-design/pro-components";
import Meta from "antd/es/card/Meta"; import Meta from "antd/es/card/Meta";
import gitee from "@/assets/icons/gitee.svg"; import { Link, useNavigate } from "react-router-dom";
import { Link } from "react-router-dom"; import { getAllStorage } from "@/api/oss";
import StorageIcon from "@/constant/stroage-icon.ts";
import { getRecentShare, getUserInfoApi } from "@/api/user";
import useStore from "@/utils/store/useStore.tsx";
import { observer } from "mobx-react";
const UserInfo: FunctionComponent = () => { const UserInfo: FunctionComponent = observer(() => {
const navigate = useNavigate();
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const data = [ const [loadingStorage, setLoadingStorage] = useState(true);
{ const [userStorage, setUserStorage] = useState([]);
title: "Ant Design Title 1", const [recentShare, setRecentShare] = useState([]);
}, const [userInfo, setUserInfo] = useState<any>({} as any);
{ const store = useStore("user");
title: "Ant Design Title 2", const userId: any = store.getUserId();
}, async function getUserStorage() {
{ const res: any = await getAllStorage(userId);
title: "Ant Design Title 3", if (res && res.success && res.data) {
}, setUserStorage(res.data);
{ setLoadingStorage(false);
title: "Ant Design Title 4", }
}, }
]; async function getUserRecentShare() {
useEffect(() => { const res: any = await getRecentShare(userId);
setTimeout(() => { if (res && res.success && res.data) {
setRecentShare(res.data);
setLoading(false); setLoading(false);
}, 2000); }
}
const getUserInfo = async () => {
const res = await getUserInfoApi(userId);
if (res && res.success && res.data) {
setUserInfo(res.data);
}
};
useEffect(() => {
getUserInfo().then();
getUserStorage().then();
getUserRecentShare().then();
}, []); }, []);
return ( return (
<> <>
@@ -40,18 +52,18 @@ const UserInfo: FunctionComponent = () => {
<div className={styles.user_info_header_avatar}> <div className={styles.user_info_header_avatar}>
<Avatar <Avatar
size={{ xs: 24, sm: 32, md: 40, lg: 64, xl: 80, xxl: 100 }} size={{ xs: 24, sm: 32, md: 40, lg: 64, xl: 80, xxl: 100 }}
icon={<AntDesignOutlined />} src={userInfo.avatar}
/> />
<span className={styles.user_info_header_name}></span> <span className={styles.user_info_header_name}>{userInfo.nickName}</span>
<div className={styles.user_info_header_desc}> <div className={styles.user_info_header_desc}>
<div> <div>
<BulbOutlined /> <BulbOutlined /> {userInfo.introduce || "-------"}
</div> </div>
<div> <div>
<EnvironmentOutlined /> <EnvironmentOutlined /> {userInfo.location || "-------"}
</div> </div>
<div> <div>
<BankOutlined /> <BankOutlined /> {userInfo.company || "-------"}
</div> </div>
</div> </div>
</div> </div>
@@ -59,61 +71,92 @@ const UserInfo: FunctionComponent = () => {
<div className={styles.user_info_center_content}> <div className={styles.user_info_center_content}>
<ProCard <ProCard
bordered bordered
style={{ maxWidth: "64%" }} style={{ maxWidth: "100%" }}
title="我的存储商" title="我的存储商"
boxShadow boxShadow
extra={<Link to={"#"}></Link>}> extra={<Link to={"/main/setting"}></Link>}>
<Skeleton loading={loading} active> <Flex vertical={false} align={"center"} justify={"space-between"} wrap={true}>
<Card style={{ width: 300, marginTop: 16 }} hoverable={true}> {userStorage && userStorage.length === 0 ? (
<Meta <Empty description={"暂无数据"}></Empty>
avatar={<Avatar src={gitee} />} ) : (
title="Card title" <>
description="This is the description" <Skeleton loading={loadingStorage} active>
/> {userStorage.map((item: any, index: number) => {
</Card> return (
</Skeleton> <div key={index}>
</ProCard> <Card
onClick={() => {
<ProCard bordered style={{ maxWidth: "34%" }} title="我的存储桶" boxShadow> navigate(`/main/setting/${item.ossType}`);
<Skeleton loading={loading} active> }}
<Card style={{ width: 300, marginTop: 16 }} hoverable={true}> style={{
width: 350,
marginTop: 16,
marginLeft: 10,
}}
hoverable={true}>
<Meta <Meta
avatar={ avatar={
<Avatar src="https://api.dicebear.com/7.x/miniavs/svg?seed=1" /> <Avatar
} src={StorageIcon[item.ossType]}
title="Card title"
description="This is the description"
/> />
}
title={item.name}
description={
<>
<span>
{" "}
{item.configCount}
</span>
<span style={{ marginLeft: 10 }}>
{item.bucketCount}
</span>
</>
}></Meta>
</Card> </Card>
</div>
);
})}
</Skeleton> </Skeleton>
</>
)}
</Flex>
</ProCard> </ProCard>
{/*<ProCard bordered style={{ maxWidth: "34%" }} title="我的存储桶" boxShadow>*/}
{/* <Skeleton loading={loading} active>*/}
{/* <Card style={{ width: 300, marginTop: 16 }} hoverable={true}>*/}
{/* <Meta*/}
{/* avatar={*/}
{/* <Avatar src="https://api.dicebear.com/7.x/miniavs/svg?seed=1" />*/}
{/* }*/}
{/* title="Card title"*/}
{/* description="This is the description"*/}
{/* />*/}
{/* </Card>*/}
{/* </Skeleton>*/}
{/*</ProCard>*/}
</div> </div>
<div className={styles.user_info_bottom_content}> <div className={styles.user_info_bottom_content}>
<ProCard <ProCard bordered style={{ maxWidth: "64%" }} title="最近动态" boxShadow>
bordered {recentShare && recentShare.length === 0 ? (
style={{ maxWidth: "64%" }} <Empty description={"暂无数据"}></Empty>
title="最近动态" ) : (
boxShadow
extra={<Link to={"#"}></Link>}>
<Skeleton loading={loading} active avatar> <Skeleton loading={loading} active avatar>
<List <List
itemLayout="horizontal" itemLayout="horizontal"
dataSource={data} dataSource={recentShare}
renderItem={(item, index) => ( renderItem={(item: any) => (
<List.Item> <List.Item>
<List.Item.Meta <List.Item.Meta
avatar={ avatar={<Avatar src={userInfo.avatar} />}
<Avatar title={item.title}
src={`https://api.dicebear.com/7.x/miniavs/svg?seed=${index}`} description={item.date}
/>
}
title={<a href="https://ant.design">{item.title}</a>}
description="Ant Design, a design language for background applications, is refined by Ant UED Team"
/> />
</List.Item> </List.Item>
)} )}
/> />
</Skeleton> </Skeleton>
)}
</ProCard> </ProCard>
<ProCard bordered style={{ maxWidth: "34%" }} title="站内通知" boxShadow> <ProCard bordered style={{ maxWidth: "34%" }} title="站内通知" boxShadow>
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE}></Empty> <Empty image={Empty.PRESENTED_IMAGE_SIMPLE}></Empty>
@@ -121,5 +164,5 @@ const UserInfo: FunctionComponent = () => {
</div> </div>
</> </>
); );
}; });
export default UserInfo; export default UserInfo;

View File

@@ -1,95 +1,114 @@
/** @format */ /** @format */
import { FunctionComponent, useEffect, useState } from "react"; import { FunctionComponent, useEffect, useState } from "react";
import { ProCard, ProForm, ProFormCascader } from "@ant-design/pro-components"; import { ProCard } from "@ant-design/pro-components";
import { AntDesignOutlined } from "@ant-design/icons";
import { import {
Avatar, Avatar,
Button, Button,
Descriptions, Flex,
DescriptionsProps,
Form, Form,
FormProps, FormProps,
Input, Input,
message,
Select, Select,
Skeleton, Skeleton,
Space, Space,
Tabs, Tabs,
Tag,
} from "antd"; } from "antd";
import styles from "./index.module.less"; import styles from "./index.module.less";
import TextArea from "antd/es/input/TextArea"; import TextArea from "antd/es/input/TextArea";
import { city } from "@/context/five-level-address.ts"; import { getUserInfoApi, updateUserInfo } from "@/api/user";
import { observer } from "mobx-react";
import useStore from "@/utils/store/useStore.tsx";
const UserSetting: FunctionComponent = () => { const UserSetting: FunctionComponent = observer(() => {
const [disable, setDisable] = useState(true); const [disable, setDisable] = useState<boolean>(true);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState<boolean>(true);
const items: DescriptionsProps["items"] = [ const [userInfo, setUserInfo] = useState<any>({} as any);
{ const [password, setPassword] = useState<string>("");
key: "1", const [phone, setPhone] = useState<string>("");
label: "用户名", const [email, setEmail] = useState<string>("");
children: "Zhou Maomao", const store = useStore("user");
}, const userId: any = store.getUserId();
{ async function getUserInfo() {
key: "2", getUserInfoApi(userId).then((res: any) => {
label: "账号ID", console.log(res);
children: "1810000000", if (res && res.success && res.data) {
}, setUserInfo(res.data);
{ setLoading(false);
key: "3", }
label: "注册时间", });
children: "Hangzhou, Zhejiang", }
},
{
key: "4",
label: "邮箱",
children: "landaiqing@126.com",
},
{
key: "5",
label: "手机号",
children: "13333333333333",
},
];
type FieldType = { type FieldType = {
email?: string; nickName?: string;
nickname?: string; // location?: string;
location?: string;
introduce?: string; introduce?: string;
gender?: string; gender?: string;
company?: string; company?: string;
blog?: string; blog?: string;
}; };
async function updateUser(data: any) {
updateUserInfo(data).then((res: any) => {
if (res && res.success) {
message.open({
type: "success",
content: "修改成功",
});
} else {
message.open({
type: "error",
content: "修改失败",
});
}
});
}
const onFinish: FormProps<FieldType>["onFinish"] = (values) => { const onFinish: FormProps<FieldType>["onFinish"] = (values) => {
console.log("Success:", values); const data: any = {
id: userId,
...values,
};
updateUserInfo(data).then((res: any) => {
if (res && res.success) {
message.open({
type: "success",
content: "修改成功",
});
} else {
message.open({
type: "error",
content: "修改失败",
});
}
});
}; };
const onFinishFailed: FormProps<FieldType>["onFinishFailed"] = (errorInfo) => { const onFinishFailed: FormProps<FieldType>["onFinishFailed"] = (errorInfo: any) => {
console.log("Failed:", errorInfo); message.open({
type: "error",
content: errorInfo,
});
}; };
useEffect(() => {
getUserInfo().then();
}, []);
const prefixSelector = ( const prefixSelector = (
<Form.Item name="prefix" noStyle>
<Select style={{ width: 90 }}> <Select style={{ width: 90 }}>
<Select.Option value="https://">https://</Select.Option> <Select.Option value="https://">https://</Select.Option>
<Select.Option value="http://">http://</Select.Option> <Select.Option value="http://">http://</Select.Option>
</Select> </Select>
</Form.Item>
); );
const ProFormText = (props: any) => { // const ProFormText = (props: any) => {
return ( // return (
<ProForm.Item {...props} style={{ height: "10px" }}> // <ProForm.Item {...props} style={{ height: "10px" }}>
<ProFormCascader // <ProFormCascader
request={async () => city} // request={async () => city}
width="md" // width="md"
name="location" // name="location"
disabled={disable}></ProFormCascader> // disabled={disable}></ProFormCascader>
</ProForm.Item> // </ProForm.Item>
); // );
}; // };
useEffect(() => { useEffect(() => {}, []);
setTimeout(() => {
setLoading(false);
}, 2000);
}, []);
const TabItems = [ const TabItems = [
{ {
label: <span></span>, label: <span></span>,
@@ -107,21 +126,21 @@ const UserSetting: FunctionComponent = () => {
onFinishFailed={onFinishFailed} onFinishFailed={onFinishFailed}
initialValues={{ prefix: "https://" }} initialValues={{ prefix: "https://" }}
autoComplete="off"> autoComplete="off">
<Form.Item<FieldType> {/*<Form.Item<FieldType>*/}
label="邮箱" {/* label="邮箱"*/}
name="email" {/* name="email"*/}
rules={[ {/* rules={[*/}
{ {/* {*/}
type: "email", {/* type: "email",*/}
message: "请输入正确的邮箱!", {/* message: "请输入正确的邮箱!",*/}
}, {/* },*/}
{ required: true, message: "请输入邮箱!" }, {/* { required: true, message: "请输入邮箱!" },*/}
]}> {/* ]}>*/}
<Input allowClear disabled={disable} /> {/* <Input allowClear disabled={disable} />*/}
</Form.Item> {/*</Form.Item>*/}
<Form.Item<FieldType> <Form.Item<FieldType>
label="昵称" label="昵称"
name="nickname" name="nickName"
rules={[ rules={[
{ {
type: "string", type: "string",
@@ -146,12 +165,12 @@ const UserSetting: FunctionComponent = () => {
<Select.Option value="UNKNOWN"></Select.Option> <Select.Option value="UNKNOWN"></Select.Option>
</Select> </Select>
</Form.Item> </Form.Item>
<Form.Item<FieldType> {/*<Form.Item<FieldType>*/}
label="地区" {/* label="地区"*/}
name="location" {/* name="location"*/}
rules={[{ required: true, message: "请输入地区!" }]}> {/* rules={[{ required: true, message: "请输入地区!" }]}>*/}
<ProFormText /> {/* <ProFormText />*/}
</Form.Item> {/*</Form.Item>*/}
<Form.Item<FieldType> label="公司" name="company"> <Form.Item<FieldType> label="公司" name="company">
<Input allowClear disabled={disable} /> <Input allowClear disabled={disable} />
</Form.Item> </Form.Item>
@@ -206,13 +225,76 @@ const UserSetting: FunctionComponent = () => {
<Skeleton loading={loading} active> <Skeleton loading={loading} active>
<Space direction={"vertical"} style={{ width: "100%" }}> <Space direction={"vertical"} style={{ width: "100%" }}>
<ProCard hoverable bordered> <ProCard hoverable bordered>
<Flex vertical={false} align={"center"}>
<span style={{ width: 100 }}></span>
<Space.Compact style={{ width: "100%" }}>
<Input.Password
onChange={(e: any) => {
setPassword(e.target.value);
}}
variant="borderless"
/>
<Button
type="text"
onClick={async () => {
const data: any = {
id: userId,
password: password,
};
await updateUser(data);
}}>
</Button>
</Space.Compact>
</Flex>
</ProCard> </ProCard>
<ProCard hoverable bordered> <ProCard hoverable bordered>
<Flex vertical={false} align={"center"}>
<span style={{ width: 100 }}></span>
<Space.Compact style={{ width: "100%" }}>
<Input.Password
onChange={(e: any) => {
setPhone(e.target.value);
}}
variant="borderless"
/>
<Button
type="text"
onClick={async () => {
const data: any = {
id: userId,
phone: phone,
};
await updateUser(data);
}}>
</Button>
</Space.Compact>
</Flex>
</ProCard> </ProCard>
<ProCard hoverable bordered> <ProCard hoverable bordered>
<Flex vertical={false} align={"center"}>
<span style={{ width: 100 }}></span>
<Space.Compact style={{ width: "100%" }}>
<Input.Password
onChange={(e: any) => {
setEmail(e.target.value);
}}
variant="borderless"
/>
<Button
type="text"
onClick={async () => {
const data: any = {
id: userId,
email: email,
};
await updateUser(data);
}}>
</Button>
</Space.Compact>
</Flex>
</ProCard> </ProCard>
</Space> </Space>
</Skeleton> </Skeleton>
@@ -229,14 +311,61 @@ const UserSetting: FunctionComponent = () => {
<div className={styles.user_setting_header}> <div className={styles.user_setting_header}>
<Avatar <Avatar
size={{ xs: 24, sm: 32, md: 40, lg: 64, xl: 80, xxl: 100 }} size={{ xs: 24, sm: 32, md: 40, lg: 64, xl: 80, xxl: 100 }}
icon={<AntDesignOutlined />} src={userInfo.avatar}
/> />
<Descriptions <Flex
style={{ width: "80%", marginLeft: "50px" }} vertical={false}
items={items} align={"center"}
column={3} style={{ height: "100px", marginLeft: 40 }}>
size={"small"}></Descriptions> <Flex vertical={true} justify={"flex-start"}>
<Flex vertical={false} align={"center"}>
<span style={{ color: "grey" }}></span>
<span style={{ width: 200 }}>{userInfo.userName}</span>
</Flex>
<Flex
vertical={false}
align={"center"}
style={{ marginTop: 20 }}>
<span style={{ color: "grey" }}>ID</span>
<span style={{ width: 200 }}>{userInfo.id}</span>
</Flex>
</Flex>
<Flex vertical={true} style={{ marginLeft: 50 }}>
<Flex vertical={false} align={"center"}>
<span style={{ color: "grey" }}> </span>
<span style={{ width: 130 }}>
{userInfo.email || "---------"}
</span>
</Flex>
<Flex
vertical={false}
align={"center"}
style={{ marginTop: 20 }}>
<span style={{ color: "grey" }}></span>
<span style={{ width: 130 }}>
{userInfo.phone || "---------"}
</span>
</Flex>
</Flex>
<Flex vertical={true} style={{ marginLeft: 50 }}>
<Flex vertical={false} align={"center"}>
<span style={{ color: "grey" }}></span>
<span style={{ width: 130 }}>{userInfo.createdTime}</span>
</Flex>
<Flex
vertical={false}
align={"center"}
style={{ marginTop: 20 }}>
<span style={{ color: "grey" }}> </span>
{userInfo.status === 0 ? (
<Tag color={"success"}></Tag>
) : (
<Tag color={"red"}></Tag>
)}
</Flex>
</Flex>
</Flex>
</div> </div>
</Skeleton> </Skeleton>
</ProCard> </ProCard>
@@ -249,5 +378,5 @@ const UserSetting: FunctionComponent = () => {
</div> </div>
</> </>
); );
}; });
export default UserSetting; export default UserSetting;

View File

@@ -0,0 +1,24 @@
/** @format */
const RandomColor: any = [
"processing",
"success",
"error",
"warning",
"magenta",
"blue",
"geekblue",
"purple",
"#f50",
"#2db7f5",
"#87d068",
"#108ee9",
"red",
"volcano",
"orange",
"gold",
"lime",
"green",
];
const getRandomColor = () => RandomColor[Math.floor(Math.random() * RandomColor.length)];
export default getRandomColor;

View File

@@ -14,6 +14,7 @@ import pinanyun from "@/assets/icons/pinanyun.svg";
import qingyun from "@/assets/icons/qingyun.svg"; import qingyun from "@/assets/icons/qingyun.svg";
import ucloud from "@/assets/icons/ucloud.svg"; import ucloud from "@/assets/icons/ucloud.svg";
import jinshan from "@/assets/icons/jinshan.svg"; import jinshan from "@/assets/icons/jinshan.svg";
import bucket from "@/assets/icons/bucket.svg";
const StorageIcon: any = { const StorageIcon: any = {
ali: aliyun, ali: aliyun,
@@ -30,5 +31,6 @@ const StorageIcon: any = {
qingyun: qingyun, qingyun: qingyun,
ucloud: ucloud, ucloud: ucloud,
jinshan: jinshan, jinshan: jinshan,
bucket:bucket,
}; };
export default StorageIcon; export default StorageIcon;

View File

@@ -0,0 +1,11 @@
/** @format */
import { lazy } from "react";
const ShareAdd = lazy(
() =>
new Promise((resolve: any) => {
resolve(import("@/components/Main/Share/components/ShareAdd/ShareAdd.tsx"));
}),
);
export default ShareAdd;

View File

@@ -0,0 +1,11 @@
/** @format */
import { lazy } from "react";
const ShareDetail = lazy(
() =>
new Promise((resolve: any) => {
resolve(import("@/components/Main/Share/components/ShareDetail/ShareDetail.tsx"));
}),
);
export default ShareDetail;

View File

@@ -5,7 +5,7 @@ import { lazy } from "react";
const ShareList = lazy( const ShareList = lazy(
() => () =>
new Promise((resolve: any) => { new Promise((resolve: any) => {
resolve(import("@/components/Main/Share/components/ShareList")); resolve(import("@/components/Main/Share/components/ShareList/ShareList.tsx"));
}), }),
); );
export default ShareList; export default ShareList;

View File

@@ -0,0 +1,11 @@
/** @format */
import { lazy } from "react";
const MyFavorites = lazy(
() =>
new Promise((resolve: any) => {
resolve(import("@/components/Main/User/MyFavorites/index.tsx"));
}),
);
export default MyFavorites;

View File

@@ -0,0 +1,11 @@
/** @format */
import { lazy } from "react";
const MyShare = lazy(
() =>
new Promise((resolve: any) => {
resolve(import("@/components/Main/User/MyShare/index.tsx"));
}),
);
export default MyShare;

View File

@@ -13,8 +13,8 @@ import Home from "@/router/modules/home";
import MainHome from "@/router/modules/main/home"; import MainHome from "@/router/modules/main/home";
import MainBucket from "@/router/modules/main/bucket"; import MainBucket from "@/router/modules/main/bucket";
import MainFile from "@/router/modules/main/file"; import MainFile from "@/router/modules/main/file";
import MainUserInfo from "@/router/modules/main/userInfo"; import MainUserInfo from "@/router/modules/main/user/userInfo";
import MainUserSetting from "@/router/modules/main/userSetting"; import MainUserSetting from "@/router/modules/main/user/userSetting";
import MainShare from "@/router/modules/main/share"; import MainShare from "@/router/modules/main/share";
import MainSetting from "@/router/modules/main/settings"; import MainSetting from "@/router/modules/main/settings";
import Ali from "@/router/modules/main/settings/ali/ali.ts"; import Ali from "@/router/modules/main/settings/ali/ali.ts";
@@ -32,6 +32,7 @@ import Wangyi from "@/router/modules/main/settings/wangyi/wangyi.ts";
import Jinshan from "@/router/modules/main/settings/jinshan/jinshan.ts"; import Jinshan from "@/router/modules/main/settings/jinshan/jinshan.ts";
import Qiniu from "@/router/modules/main/settings/qiniu/qiniu.ts"; import Qiniu from "@/router/modules/main/settings/qiniu/qiniu.ts";
import ShareList from "@/router/modules/main/share/modules/shareList.tsx"; import ShareList from "@/router/modules/main/share/modules/shareList.tsx";
import ShareDetail from "@/router/modules/main/share/modules/shareDetail.tsx";
import minioBucket from "@/router/modules/main/bucket/createBuckets/minio.ts"; import minioBucket from "@/router/modules/main/bucket/createBuckets/minio.ts";
import tencentBucket from "@/router/modules/main/bucket/createBuckets/tencent.ts"; import tencentBucket from "@/router/modules/main/bucket/createBuckets/tencent.ts";
import aliBucket from "@/router/modules/main/bucket/createBuckets/ali.ts"; import aliBucket from "@/router/modules/main/bucket/createBuckets/ali.ts";
@@ -45,11 +46,14 @@ import qiniuBucket from "@/router/modules/main/bucket/createBuckets/qiniu.ts";
import ucloudBucket from "@/router/modules/main/bucket/createBuckets/ucloud.ts"; import ucloudBucket from "@/router/modules/main/bucket/createBuckets/ucloud.ts";
import upBucket from "@/router/modules/main/bucket/createBuckets/up.ts"; import upBucket from "@/router/modules/main/bucket/createBuckets/up.ts";
import wangyiBucket from "@/router/modules/main/bucket/createBuckets/wangyi.ts"; import wangyiBucket from "@/router/modules/main/bucket/createBuckets/wangyi.ts";
import ShareAdd from "@/router/modules/main/share/modules/shareAdd.tsx";
import MyShare from "@/router/modules/main/user/myShare";
import MyFavorites from "@/router/modules/main/user/myFavorites";
const routes: RouteObject[] = [ const routes: RouteObject[] = [
{ {
path: "/", path: "/",
Component: (props) => ComponentLoading(Home, props), element: <Navigate to={"/home"} />,
}, },
{ {
path: "/register", path: "/register",
@@ -142,6 +146,10 @@ const routes: RouteObject[] = [
path: "/main/file", path: "/main/file",
Component: MainFile, Component: MainFile,
}, },
{
path: "/main/file/:bucket",
Component: MainFile,
},
{ {
path: "/main/user/info", path: "/main/user/info",
Component: MainUserInfo, Component: MainUserInfo,
@@ -150,6 +158,15 @@ const routes: RouteObject[] = [
path: "/main/user/setting", path: "/main/user/setting",
Component: MainUserSetting, Component: MainUserSetting,
}, },
{
path: "/main/user/myshare",
Component: MyShare,
},
{
path: "/main/user/favorites",
Component: MyFavorites,
},
{ {
path: "/main/share", path: "/main/share",
Component: MainShare, Component: MainShare,
@@ -158,6 +175,14 @@ const routes: RouteObject[] = [
path: "/main/share/list/:id", path: "/main/share/list/:id",
Component: ShareList, Component: ShareList,
}, },
{
path: "/main/share/detail/:id",
Component: ShareDetail,
},
{
path: "/main/share/add",
Component: ShareAdd,
},
{ {
path: "/main/setting", path: "/main/setting",
Component: MainSetting, Component: MainSetting,

View File

@@ -1,9 +1,9 @@
/** @format */ /** @format */
import matchAuth from "./matchRouter.ts"; import matchAuth from "./matchRouter.ts";
import { Navigate, useLocation } from "react-router-dom"; import { Navigate, useLocation } from "react-router-dom";
import { message } from "antd";
import { getStorageFromKey } from "@/utils/localStorage/config.ts"; import { getStorageFromKey } from "@/utils/localStorage/config.ts";
import React from "react"; import React from "react";
import { message } from "antd";
export default function AuthRoute(props: { children: React.ReactNode }) { export default function AuthRoute(props: { children: React.ReactNode }) {
const location: any = useLocation(); const location: any = useLocation();
const isLogin = getStorageFromKey("token"); const isLogin = getStorageFromKey("token");
@@ -12,13 +12,15 @@ export default function AuthRoute(props: { children: React.ReactNode }) {
const condition: boolean = const condition: boolean =
(currentPath === "/login" && !isLogin) || (currentPath === "/login" && !isLogin) ||
currentPath === "/404" || currentPath === "/404" ||
(currentPath === "register" && !isLogin) || (currentPath === "/register" && !isLogin) ||
(currentPath === "forget" && !isLogin); (currentPath === "/forget" && !isLogin) ||
(currentPath === "/" && !isLogin) ||
(currentPath === "/home" && !isLogin);
// 登录后访问login // 登录后访问login
const conditionWithLogin: boolean = const conditionWithLogin: boolean =
(currentPath === "/login" && isLogin) || (currentPath === "/login" && isLogin) ||
(currentPath === "register" && isLogin) || (currentPath === "/register" && isLogin) ||
(currentPath === "forget" && isLogin); (currentPath === "/forget" && isLogin);
const findPath: any = matchAuth(currentPath); const findPath: any = matchAuth(currentPath);
if (condition) { if (condition) {
@@ -32,14 +34,14 @@ export default function AuthRoute(props: { children: React.ReactNode }) {
return <Navigate to="/404" />; return <Navigate to="/404" />;
} }
// if (!isLogin) { if (!isLogin) {
// message message
// .open({ .open({
// content: "请先登录!", content: "请先登录!",
// type: "warning", type: "warning",
// }) })
// .then(); .then();
// return <Navigate to="/login" />; return <Navigate to="/login" />;
// } }
return props.children; return props.children;
} }

View File

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

View File

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

View File

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

View File

@@ -1,50 +0,0 @@
/** @format */
import { action, makeObservable, observable } from "mobx";
import { isHydrated, makePersistable } from "mobx-persist-store";
import { handleLocalforage } from "@/utils/localforage";
export class UseStorageSettingStore {
selectValue: string = "";
constructor() {
makeObservable(this, {
selectValue: observable,
setSelectValue: action,
getSelectValue: action,
isHydrated: action,
});
makePersistable(
this,
{
// 在构造函数内使用 makePersistable
name: "storage_setting", // 保存的name用于在storage中的名称标识只要不和storage中其他名称重复就可以
properties: ["selectValue"], // 要保存的字段这些字段会被保存在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)
}),
);
}
getSelectValue() {
return this.selectValue ? this.selectValue : null;
}
setSelectValue(value: any) {
this.selectValue = value;
}
isHydrated() {
return isHydrated(this);
}
}

View File

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

57
src/types/user.d.ts vendored Normal file
View File

@@ -0,0 +1,57 @@
/** @format */
/* eslint-disable */
declare namespace API {
type PhoneRegisterRequest = {
phone?: string;
userName?: string;
password?: string;
confirmPassword?: string;
activeCode?: string;
};
type LoginRequest = {
userName?: string;
password?: string;
token: string;
deg: number;
};
type LoginByPhoneRequest = {
phone?: string;
activeCode?: string;
token: string;
deg: number;
};
type findPasswordRequest = {
phone?: string;
password?: string;
confirmPassword?: string;
activeCode?: string;
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,261 +0,0 @@
/** @format */
/* eslint-disable */
declare namespace API {
type PhoneRegisterRequest = {
phone?: string;
userName?: string;
password?: string;
confirmPassword?: string;
activeCode?: string;
};
type LoginRequest = {
userName?: string;
password?: string;
token: string;
deg: number;
};
type LoginByPhoneRequest = {
phone?: string;
activeCode?: string;
token: string;
deg: number;
};
type findPasswordRequest = {
phone?: string;
password?: string;
confirmPassword?: string;
activeCode?: string;
token: string;
deg: number;
};
// type ApiResponse<T> = {
// success?: boolean;
// code?: number;
// codeMessage?: string;
// errorMessage?: Map<string, string>;
// showType?: ErrorShowType;
// data: T
// }
// type CurrentUser = {
// name?: string;
// avatar?: string;
// userid?: string;
// email?: string;
// signature?: string;
// title?: string;
// group?: string;
// tags?: { key?: string; label?: string }[];
// notifyCount?: number;
// unreadCount?: number;
// country?: string;
// access?: string;
// geographic?: {
// province?: { label?: string; key?: string };
// city?: { label?: string; key?: string };
// };
// address?: string;
// phone?: string;
// };
//
// type LoginResult = ResponseStructure & {
// data?: {
// token?: string;
// expireDateTime?: number;
// expire?: number;
// timeUnit?: any;
// }
// };
//
// type PageParams = {
// pageNum?: number;
// pageSize?: number;
// };
// type RuleListItem = {
// key?: number;
// disabled?: boolean;
// href?: string;
// avatar?: string;
// name?: string;
// owner?: string;
// desc?: string;
// callNo?: number;
// status?: number;
// updatedAt?: string;
// createdAt?: string;
// progress?: number;
// };
// type RuleList = {
// data?: RuleListItem[];
// /** 列表的内容总数 */
// total?: number;
// success?: boolean;
// };
// interface ResponseStructure {
// success?: boolean;
// code?: number;
// codeMessage?: string;
// errorMessage?: Map<string, string>;
// showType?: ErrorShowType;
// }
// type
// FakeCaptcha = ResponseStructure & {
// data?: boolean,
// };
//
// type PhonePasswordLoginParams = {
// clientId?: string;
// phone?: string;
// password?: string;
// code?: string;
// autoLogin?: boolean;
// };
//
// type PhoneSmsCodeLoginParams = {
// clientId?: string;
// phone?: string;
// smsCode?: string;
// autoLogin?: boolean;
// };
//
// type ErrorResponse = {
// /** 业务约定的错误码 */
// errorCode: string;
// /** 业务上的错误信息 */
// errorMessage?: Map<string, string>;
// /** 业务上的请求是否成功 */
// success?: boolean;
// };
//
// type NoticeIconList = {
// data?: NoticeIconItem[];
// /** 列表的内容总数 */
// total?: number;
// success?: boolean;
// };
// type NoticeIconItemType = 'notification' | 'message' | 'event';
// type NoticeIconItem = {
// id?: string;
// extra?: string;
// key?: string;
// read?: boolean;
// avatar?: string;
// title?: string;
// status?: string;
// datetime?: string;
// description?: string;
// type?: NoticeIconItemType;
// };
// type GenerateMpRegCode = {
// data?: {
// regCode?: string;
// qrCodeUrl?: string;
// expireSeconds?: number;
// ticket?: string;
// url?: string;
// };
// }
// type GetClientId = {
// data?: string;
// }
// type GenerateBase64Code = {
// data?: string;
// }
// type GetClientToken = {
// data?: {
// token?: string;
// expireDateTime?: number;
// expire?: number;
// };
// }
// type PhoneRegisterResponse = ResponseStructure & {
// data?: number;
// }
//
// type CustomValidate = {
// validateStatus?: boolean;
// msg?: string;
// formValue?: any;
// code?: number;
// codeMessage?: string;
// errors?: Map<string, string>;
// validateFields: ValidateFields<Values>;
// }
//
// type ListAccountBookVo = ResponseStructure & {
// data?: {
// total?: number;
// list?: ListAccountBookVoItem[]
// }
// }
// type ListAccountBookVoItem = {
// id: number;
// companyName?: string;
// valueAddedTaxCate?: string;
// accountingStandard?: string;
// startTime?: Date;
// createTime?: Date;
// enableVoucherVerify?: boolean;
// disable?: boolean;
// }
// type GetAccountBookVo = {
// id?: number;
// companyName?: string;
// unifiedSocialCreditCode?: string;
// industryId?: number;
// valueAddedTaxCate?: number;
// enableVoucherVerify?: boolean;
// startTime?: Date;
// accountingStandard?: number;
// enableFixedAssets?: boolean;
// enableCapital?: boolean;
// enablePsi?: boolean;
// }
// type DataDictionaryVo = {
// dataCode?: string;
// dataValue?: string;
// }
//
// type ListTreeMenuVo = {
// id?: number;
// key?: string;
// title?: any;
// icon?: string;
// checked?: boolean;
// children?: ListTreeMenuVo[];
// level?: number;
// };
//
// type ListTreeSelectMenuVo = {
// id?: number;
// value?: string;
// title?: any;
// children?: ListTreeMenuVo[];
// }
//
// type ListRoleVo = {
// id: number;
// roleName?: string;
// disable?: boolean;
// }
//
// type ListSubject = {
// id: number;
// pid?: number;
// valueAddedTaxCate?: string;
// accountingStandard?: string;
// startTime?: Date;
// createTime?: Date;
// enableVoucherVerify?: boolean;
// disable?: boolean;
// }
}

View File

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

View File

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

View File

@@ -7,24 +7,31 @@ import {
QuestionCircleFilled, QuestionCircleFilled,
} from "@ant-design/icons"; } from "@ant-design/icons";
import { DefaultFooter, PageContainer, ProCard, ProLayout } from "@ant-design/pro-components"; import { DefaultFooter, PageContainer, ProCard, ProLayout } from "@ant-design/pro-components";
import settings from "./settings.tsx"; import { Link, Outlet, useLocation, useNavigate } from "react-router-dom";
import { Link, Outlet, useLocation } from "react-router-dom";
import logo from "@/assets/images/logo.png"; import logo from "@/assets/images/logo.png";
import { Suspense } from "react"; import { Suspense } from "react";
import { Dropdown } from "antd"; import { Dropdown } from "antd";
// import { getUserMenuPermission } from "@/api/user"; import settings from "@/views/Main/settings.tsx";
import { logout } from "@/api/user";
import { observer } from "mobx-react";
import useStore from "@/utils/store/useStore.tsx";
import { getUserMenuPermission } from "@/api/user";
import { clearStorage } from "@/utils/localStorage/config.ts";
export default function Layout() { const Layout = () => {
const location = useLocation(); const location = useLocation();
const store = useStore("user");
const navigate = useNavigate();
return ( return (
<div <div
id="test-pro-layout" id="pro-layout"
style={{ style={{
height: "100vh", height: "100vh",
}}> }}>
<ProLayout <ProLayout
logo={logo} logo={logo as any}
layout={"mix"} layout={"mix"}
splitMenus={false}
pure={false} pure={false}
loading={false} loading={false}
contentWidth={"Fluid"} contentWidth={"Fluid"}
@@ -38,10 +45,10 @@ export default function Layout() {
}} }}
{...settings} {...settings}
menu={{ menu={{
// request: async () => { request: async () => {
// const res: any = await getUserMenuPermission("17"); const res: any = await getUserMenuPermission(store.getUserId() as any);
// return res.data.routes; return res.data.routes;
// }, },
type: "group", type: "group",
defaultOpenAll: false, defaultOpenAll: false,
hideMenuWhenCollapsed: false, hideMenuWhenCollapsed: false,
@@ -51,9 +58,10 @@ export default function Layout() {
location={{ location={{
pathname: location.pathname, pathname: location.pathname,
}} }}
avatarProps={{ avatarProps={
src: "https://gw.alipayobjects.com/zos/antfincdn/efFD%24IOql2/weixintupian_20170331104822.jpg", {
title: "七妮妮", src: store.getAvatar() || logo,
title: store.getNickName() || store.getName() || "unknown",
size: "large", size: "large",
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error // @ts-expect-error
@@ -66,6 +74,17 @@ export default function Layout() {
key: "logout", key: "logout",
icon: <LogoutOutlined />, icon: <LogoutOutlined />,
label: "退出登录", label: "退出登录",
onClick: () => {
logout(store.getUserId());
clearStorage();
store.setToken("");
store.setUserId("");
store.setAvatar("");
store.setNickName("");
setTimeout(() => {
navigate("/login");
}, 1000);
},
}, },
], ],
}}> }}>
@@ -73,7 +92,8 @@ export default function Layout() {
</Dropdown> </Dropdown>
); );
}, },
}} } as any
}
actionsRender={(props: any) => { actionsRender={(props: any) => {
// eslint-disable-next-line react/prop-types // eslint-disable-next-line react/prop-types
if (props.isMobile) return []; if (props.isMobile) return [];
@@ -94,15 +114,20 @@ export default function Layout() {
<DefaultFooter <DefaultFooter
links={[ links={[
{ {
key: "schisandra", key: "schisandra-1",
title: "schisandra", title: "五味子云存储",
href: "https://landaiqing.cn",
},
{
key: "schisandra-2",
title: "schisandra cloud storage",
href: "https://landaiqing.cn", href: "https://landaiqing.cn",
}, },
]} ]}
copyright="2024" copyright="2024 by schisandra"
/> />
)}> )}>
<PageContainer title={false}> <PageContainer title={false} breadcrumb={false as any} ghost={true}>
<ProCard> <ProCard>
<div> <div>
<Suspense> <Suspense>
@@ -114,4 +139,5 @@ export default function Layout() {
</ProLayout> </ProLayout>
</div> </div>
); );
} };
export default observer(Layout);

View File

@@ -23,55 +23,65 @@ import qingyun from "@/assets/icons/qingyun.svg";
import ucloud from "@/assets/icons/ucloud.svg"; import ucloud from "@/assets/icons/ucloud.svg";
import jinshan from "@/assets/icons/jinshan.svg"; import jinshan from "@/assets/icons/jinshan.svg";
export default { export default {
route: { // route: {
path: "/", // path: "/",
routes: [ // routes: [
{ // {
index: true, // index: true,
path: "main/home", // path: "main/home",
name: "仪表盘", // name: "仪表盘",
icon: "https://pic.imgdb.cn/item/668b9176d9c307b7e99e51b9.png", // icon: "https://pic.imgdb.cn/item/668b9176d9c307b7e99e51b9.png",
}, // },
{ // {
path: "main/setting", // path: "main/setting",
name: "存储商", // name: "存储商",
icon: "https://pic.imgdb.cn/item/668b918cd9c307b7e99e7fbc.png", // icon: "https://pic.imgdb.cn/item/668b918cd9c307b7e99e7fbc.png",
}, // },
{ // {
path: "main/bucket", // path: "main/bucket",
name: "存储桶", // name: "存储桶",
icon: "https://pic.imgdb.cn/item/668b91a5d9c307b7e99ea40d.png", // icon: "https://pic.imgdb.cn/item/668b91a5d9c307b7e99ea40d.png",
}, // },
{ // {
path: "main/file", // path: "main/file",
name: "文件", // name: "文件",
icon: "https://pic.imgdb.cn/item/668b91b7d9c307b7e99eb9fb.png", // icon: "https://pic.imgdb.cn/item/668b91b7d9c307b7e99eb9fb.png",
}, // },
{ // {
path: "main/share", // path: "main/share",
name: "分享圈", // name: "分享圈",
icon: "https://pic.imgdb.cn/item/668b91d1d9c307b7e99edb1c.png", // icon: "https://pic.imgdb.cn/item/668b91d1d9c307b7e99edb1c.png",
}, // },
{ // {
path: "/", // path: "/",
name: "个人中心", // name: "个人中心",
icon: "https://pic.imgdb.cn/item/668b91e1d9c307b7e99eed3a.png", // icon: "https://pic.imgdb.cn/item/668b91e1d9c307b7e99eed3a.png",
routes: [ // routes: [
{ // {
index: true, // index: true,
path: "main/user/info", // path: "main/user/info",
name: "用户信息", // name: "用户信息",
icon: "https://pic.imgdb.cn/item/668b91fbd9c307b7e99f0f90.png", // icon: "https://pic.imgdb.cn/item/668b91fbd9c307b7e99f0f90.png",
}, // },
{ // {
path: "main/user/setting", // path: "main/user/setting",
name: "用户设置", // name: "用户设置",
icon: "https://pic.imgdb.cn/item/668b921ad9c307b7e99f35b2.png", // icon: "https://pic.imgdb.cn/item/668b921ad9c307b7e99f35b2.png",
}, // },
], // {
}, // path: "main/user/myshare",
], // name: "我的分享",
}, // icon: "https://pic.imgdb.cn/item/668f8e5fd9c307b7e9f079bf.png",
// },
// {
// path: "main/user/favorites",
// name: "我的收藏",
// icon: "https://pic.imgdb.cn/item/6690e7c0d9c307b7e9738f02.png",
// },
// ],
// },
// ],
// },
location: { location: {
pathname: "/main/home", pathname: "/main/home",
}, },

View File

@@ -115,7 +115,7 @@ export default observer(() => {
async function wechatLogin() { async function wechatLogin() {
createClientId().then((res: any) => { createClientId().then((res: any) => {
generateQRCode(res.data).then((response: any) => { generateQRCode(res.data).then((response: any) => {
if (response.success) { if (res && response.success && res.data) {
setQRCode(response.data.qrCodeUrl); setQRCode(response.data.qrCodeUrl);
setLoading(false); setLoading(false);
timerManager.add(() => { timerManager.add(() => {
@@ -180,8 +180,6 @@ export default observer(() => {
className={styles.mp_code_img} className={styles.mp_code_img}
// src={generateMpRegCodeData.data?.qrCodeUrl} // src={generateMpRegCodeData.data?.qrCodeUrl}
src={QRCode} src={QRCode}
fallback="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMIAAADDCAYAAADQvc6UAAABRWlDQ1BJQ0MgUHJvZmlsZQAAKJFjYGASSSwoyGFhYGDIzSspCnJ3UoiIjFJgf8LAwSDCIMogwMCcmFxc4BgQ4ANUwgCjUcG3awyMIPqyLsis7PPOq3QdDFcvjV3jOD1boQVTPQrgSkktTgbSf4A4LbmgqISBgTEFyFYuLykAsTuAbJEioKOA7DkgdjqEvQHEToKwj4DVhAQ5A9k3gGyB5IxEoBmML4BsnSQk8XQkNtReEOBxcfXxUQg1Mjc0dyHgXNJBSWpFCYh2zi+oLMpMzyhRcASGUqqCZ16yno6CkYGRAQMDKMwhqj/fAIcloxgHQqxAjIHBEugw5sUIsSQpBobtQPdLciLEVJYzMPBHMDBsayhILEqEO4DxG0txmrERhM29nYGBddr//5/DGRjYNRkY/l7////39v///y4Dmn+LgeHANwDrkl1AuO+pmgAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAwqADAAQAAAABAAAAwwAAAAD9b/HnAAAHlklEQVR4Ae3dP3PTWBSGcbGzM6GCKqlIBRV0dHRJFarQ0eUT8LH4BnRU0NHR0UEFVdIlFRV7TzRksomPY8uykTk/zewQfKw/9znv4yvJynLv4uLiV2dBoDiBf4qP3/ARuCRABEFAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghgg0Aj8i0JO4OzsrPv69Wv+hi2qPHr0qNvf39+iI97soRIh4f3z58/u7du3SXX7Xt7Z2enevHmzfQe+oSN2apSAPj09TSrb+XKI/f379+08+A0cNRE2ANkupk+ACNPvkSPcAAEibACyXUyfABGm3yNHuAECRNgAZLuYPgEirKlHu7u7XdyytGwHAd8jjNyng4OD7vnz51dbPT8/7z58+NB9+/bt6jU/TI+AGWHEnrx48eJ/EsSmHzx40L18+fLyzxF3ZVMjEyDCiEDjMYZZS5wiPXnyZFbJaxMhQIQRGzHvWR7XCyOCXsOmiDAi1HmPMMQjDpbpEiDCiL358eNHurW/5SnWdIBbXiDCiA38/Pnzrce2YyZ4//59F3ePLNMl4PbpiL2J0L979+7yDtHDhw8vtzzvdGnEXdvUigSIsCLAWavHp/+qM0BcXMd/q25n1vF57TYBp0a3mUzilePj4+7k5KSLb6gt6ydAhPUzXnoPR0dHl79WGTNCfBnn1uvSCJdegQhLI1vvCk+fPu2ePXt2tZOYEV6/fn31dz+shwAR1sP1cqvLntbEN9MxA9xcYjsxS1jWR4AIa2Ibzx0tc44fYX/16lV6NDFLXH+YL32jwiACRBiEbf5KcXoTIsQSpzXx4N28Ja4BQoK7rgXiydbHjx/P25TaQAJEGAguWy0+2Q8PD6/Ki4R8EVl+bzBOnZY95fq9rj9zAkTI2SxdidBHqG9+skdw43borCXO/ZcJdraPWdv22uIEiLA4q7nvvCug8WTqzQveOH26fodo7g6uFe/a17W3+nFBAkRYENRdb1vkkz1CH9cPsVy/jrhr27PqMYvENYNlHAIesRiBYwRy0V+8iXP8+/fvX11Mr7L7ECueb/r48eMqm7FuI2BGWDEG8cm+7G3NEOfmdcTQw4h9/55lhm7DekRYKQPZF2ArbXTAyu4kDYB2YxUzwg0gi/41ztHnfQG26HbGel/crVrm7tNY+/1btkOEAZ2M05r4FB7r9GbAIdxaZYrHdOsgJ/wCEQY0J74TmOKnbxxT9n3FgGGWWsVdowHtjt9Nnvf7yQM2aZU/TIAIAxrw6dOnAWtZZcoEnBpNuTuObWMEiLAx1HY0ZQJEmHJ3HNvGCBBhY6jtaMoEiJB0Z29vL6ls58vxPcO8/zfrdo5qvKO+d3Fx8Wu8zf1dW4p/cPzLly/dtv9Ts/EbcvGAHhHyfBIhZ6NSiIBTo0LNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiEC/wGgKKC4YMA4TAAAAABJRU5ErkJggg=="
/> />
</Spin> </Spin>
<Alert <Alert

View File

@@ -116,10 +116,14 @@ export default observer(() => {
if (res && res.success && res.code === 0) { if (res && res.success && res.code === 0) {
store.setToken(res.data.token); store.setToken(res.data.token);
store.setUserId(res.data.user.id); store.setUserId(res.data.user.id);
store.setName(res.data.user.userName);
store.setAvatar(res.data.user.avatar);
store.setNickName(res.data.user.nickName);
setStorage("token", res.data.token, 24 * 60 * 30); setStorage("token", res.data.token, 24 * 60 * 30);
setStorage("userId", res.data.user.id, 24 * 60 * 30);
if (store.getToken() !== null || store.getUserId() !== null) { if (store.getToken() !== null || store.getUserId() !== null) {
setTimeout(() => { setTimeout(() => {
navigate("/main"); navigate("/main/home");
}, 3000); }, 3000);
} }
message.open({ message.open({
@@ -151,6 +155,7 @@ export default observer(() => {
store.setToken(res.data.token); store.setToken(res.data.token);
store.setUserId(res.data.user.id); store.setUserId(res.data.user.id);
setStorage("token", res.data.token, 24 * 60 * 30); setStorage("token", res.data.token, 24 * 60 * 30);
setStorage("userId", res.data.user.id, 24 * 60 * 30);
if (store.getToken() !== null || store.getUserId() !== null) { if (store.getToken() !== null || store.getUserId() !== null) {
setTimeout(() => { setTimeout(() => {
navigate("/main"); navigate("/main");
@@ -214,7 +219,7 @@ export default observer(() => {
async function wechatLogin() { async function wechatLogin() {
createClientId().then((res: any) => { createClientId().then((res: any) => {
generateQRCode(res.data).then((response: any) => { generateQRCode(res.data).then((response: any) => {
if (response.success) { if (res && response.success && res.data) {
setQRCode(response.data.qrCodeUrl); setQRCode(response.data.qrCodeUrl);
setLoading(false); setLoading(false);
timerManager.add(() => { timerManager.add(() => {
@@ -286,7 +291,6 @@ export default observer(() => {
className={styles.mp_code_img} className={styles.mp_code_img}
// src={generateMpRegCodeData.data?.qrCodeUrl} // src={generateMpRegCodeData.data?.qrCodeUrl}
src={QRCode} src={QRCode}
fallback="data:image/png;base64,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"
/> />
</Spin> </Spin>
<Alert <Alert
@@ -504,6 +508,15 @@ export default observer(() => {
}}> }}>
<Tooltip title="QQ登录" color={"blue"}> <Tooltip title="QQ登录" color={"blue"}>
<QqOutlined <QqOutlined
onClick={() => {
message
.open({
content: "敬请期待!",
type: "info",
duration: 3,
})
.then();
}}
style={{ ...iconStyles, color: "#1677FF" }} style={{ ...iconStyles, color: "#1677FF" }}
/> />
</Tooltip> </Tooltip>
@@ -521,6 +534,9 @@ export default observer(() => {
}}> }}>
<Tooltip title="企业微信登录" color={"green"}> <Tooltip title="企业微信登录" color={"green"}>
<WechatWorkOutlined <WechatWorkOutlined
onClick={() => {
oAuthLogin("wechat").then();
}}
style={{ ...iconStyles, color: "#08a327" }} style={{ ...iconStyles, color: "#08a327" }}
/> />
</Tooltip> </Tooltip>

View File

@@ -125,7 +125,7 @@ export default observer(() => {
async function wechatLogin() { async function wechatLogin() {
createClientId().then((res: any) => { createClientId().then((res: any) => {
generateQRCode(res.data).then((response: any) => { generateQRCode(res.data).then((response: any) => {
if (response.success) { if (res && response.success && res.data) {
setQRCode(response.data.qrCodeUrl); setQRCode(response.data.qrCodeUrl);
setLoading(false); setLoading(false);
timerManager.add(() => { timerManager.add(() => {
@@ -184,7 +184,6 @@ export default observer(() => {
className={styles.mp_code_img} className={styles.mp_code_img}
// src={generateMpRegCodeData.data?.qrCodeUrl} // src={generateMpRegCodeData.data?.qrCodeUrl}
src={QRCode} src={QRCode}
fallback="data:image/png;base64,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"
/> />
</Spin> </Spin>
<Alert <Alert

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

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

View File

@@ -35,7 +35,6 @@
"src/**/*.d.ts", "src/**/*.d.ts",
"src/**/*.tsx", "src/**/*.tsx",
"types/*.d.ts", "types/*.d.ts",
"vite.config.ts"
], ],
"references": [ "references": [
{ {

Some files were not shown because too many files have changed in this diff Show More