diff --git a/package.json b/package.json index ad47d0d..c194215 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,6 @@ "private": true, "description": "An out-of-box UI solution for enterprise applications", "scripts": { - "openapi": "max openapi", "analyze": "cross-env ANALYZE=1 max build", "build": "max build", "deploy": "npm run build && npm run gh-pages", @@ -19,6 +18,7 @@ "lint:fix": "eslint --fix --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src ", "lint:js": "eslint --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src", "lint:prettier": "prettier -c --write \"**/**.{js,jsx,tsx,ts,less,md,json}\" --end-of-line auto", + "openapi": "max openapi", "prepare": "husky install", "prettier": "prettier -c --write \"**/**.{js,jsx,tsx,ts,less,md,json}\"", "preview": "npm run build && max preview --port 8000", @@ -50,6 +50,7 @@ "@ant-design/icons": "^4.8.1", "@ant-design/pro-components": "^2.6.48", "@umijs/route-utils": "^2.2.2", + "ali-oss": "^6.20.0", "antd": "^5.13.2", "antd-style": "^3.6.1", "classnames": "^2.5.1", @@ -61,7 +62,8 @@ "rc-util": "^5.38.1", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-helmet-async": "^1.3.0" + "react-helmet-async": "^1.3.0", + "spark-md5": "^3.0.2" }, "devDependencies": { "@ant-design/pro-cli": "^3.3.0", @@ -74,6 +76,7 @@ "@types/react": "^18.2.48", "@types/react-dom": "^18.2.18", "@types/react-helmet": "^6.1.11", + "@types/spark-md5": "^3.0.2", "@umijs/fabric": "^2.14.1", "@umijs/lint": "^4.1.1", "@umijs/max": "^4.1.1", diff --git a/src/pages/OSSUpload.tsx b/src/pages/OSSUpload.tsx new file mode 100644 index 0000000..1ca2c97 --- /dev/null +++ b/src/pages/OSSUpload.tsx @@ -0,0 +1,124 @@ +import { toMD5 } from '@/utils/encryptUtil'; +import OSS from 'ali-oss'; +import { message, Upload, UploadFile, UploadProps } from 'antd'; +import React, { useEffect, useState } from 'react'; +import { getSts } from '../services/matrix/admin'; + +interface OSSUploadProps { + onUploadSuccess?: (fileUrl: string) => void; + onUploadError?: (error: any) => void; +} + +const OSSUpload: React.FC = ({ onUploadSuccess, onUploadError }) => { + const [uploading, setUploading] = useState(false); + const [fileList, setFileList] = useState([]); + const [ossClient, setOssClient] = useState(null); + const { Dragger } = Upload; + + useEffect(() => { + async function initOssClient() { + try { + const stsResponse = await getSts(); // 假设你有一个 getSTS 函数可以获取 STS 凭证 + if ( + !stsResponse.data || + !stsResponse.data.accessKeySecret || + !stsResponse.data.accessKeyId + ) { + message.error('初始化失败,请稍后再试'); + return; + } + const client = new OSS({ + region: 'oss-cn-beijing', + accessKeyId: stsResponse.data.accessKeyId, + accessKeySecret: stsResponse.data.accessKeySecret, + stsToken: stsResponse.data.securityToken, + bucket: stsResponse.data.bucket, + }); + setOssClient(client); + } catch (error) { + message.error('初始化 OSS 客户端失败'); + if (onUploadError) { + onUploadError(error); + } + } + } + initOssClient(); + }, [onUploadError]); + + const onRemove = (file: UploadFile) => { + const files = (fileList || []).filter((v) => v.url !== file.url); + setFileList(files); + }; + + const handleUpload: UploadProps['customRequest'] = async (options) => { + const { file } = options; + if (file instanceof File) { + setUploading(true); + + try { + const md5 = await toMD5(file); + const fileName = `matrix/${md5}.${file.name.split('.').pop()}`; + await ossClient?.put(fileName, file, { + headers: { + 'Content-Type': file.type, + }, + }); + const fileUrl = `https://apks.bzgames.cn/${fileName}`; + + if (onUploadSuccess) { + onUploadSuccess(fileUrl); + } + setUploading(false); + // 将上传成功的文件信息添加到 fileList + setFileList((prevFileList) => [ + ...prevFileList, + { + uid: file.uid, + name: file.name + ':' + fileUrl, + status: 'done', + url: fileUrl, + }, + ]); + } catch (error) { + message.error('文件上传失败'); + if (onUploadError) { + onUploadError(error); + } + setFileList((prevFileList) => [ + ...prevFileList, + { + uid: file.uid, + name: file.name, + status: 'error', + }, + ]); + } finally { + setUploading(false); + } + } else { + message.error('上传文件类型错误'); + } + }; + + return ( + { + await navigator.clipboard.writeText(file.url || ''); + message.success('链接复制成功 ' + file.url); + }} + > +

+ +

+

点击或拖拽文件到此区域上传

+

支持单个或批量上传。文件名将使用文件的 MD5 值命名。

+
+ ); +}; + +export default OSSUpload; diff --git a/src/pages/SuperAdmin.tsx b/src/pages/SuperAdmin.tsx index 8bd0922..8b494fc 100644 --- a/src/pages/SuperAdmin.tsx +++ b/src/pages/SuperAdmin.tsx @@ -26,6 +26,8 @@ import TabPane from 'antd/es/tabs/TabPane'; import moment from 'moment'; import React, { useEffect, useState } from 'react'; +import OSSUpload from './OSSUpload'; + // import { RequestOptionsType, ProFieldRequestData } from "@ant-design/pro-utils"; const SuperAdmin: React.FC = () => { @@ -316,7 +318,7 @@ const SuperAdmin: React.FC = () => { {(fields, { add, remove }) => ( <> - {fields.map(({ key, name, ...restField }) => ( + {fields.map(({ key, name }) => (
{
+ + + ); diff --git a/src/services/matrix/admin.ts b/src/services/matrix/admin.ts index 2516b7f..490afbf 100644 --- a/src/services/matrix/admin.ts +++ b/src/services/matrix/admin.ts @@ -80,6 +80,14 @@ export async function deleteWhiteList( }); } +/** 此处后端没有提供注释 GET /api/admin/getSTS */ +export async function getSts(options?: { [key: string]: any }) { + return request('/api/admin/getSTS', { + method: 'GET', + ...(options || {}), + }); +} + /** 此处后端没有提供注释 POST /api/admin/grantApp */ export async function grantApp( // 叠加生成的Param类型 (非body参数swagger默认没有生成对象) diff --git a/src/services/matrix/index.ts b/src/services/matrix/index.ts index 6f2aa17..f15629e 100644 --- a/src/services/matrix/index.ts +++ b/src/services/matrix/index.ts @@ -7,9 +7,11 @@ import * as appController from './appController'; import * as device from './device'; import * as loginController from './loginController'; import * as matrixController from './matrixController'; +import * as registerController from './registerController'; export default { matrixController, loginController, + registerController, admin, device, appController, diff --git a/src/services/matrix/registerController.ts b/src/services/matrix/registerController.ts new file mode 100644 index 0000000..abb9254 --- /dev/null +++ b/src/services/matrix/registerController.ts @@ -0,0 +1,45 @@ +// @ts-ignore +/* eslint-disable */ +import { request } from '@umijs/max'; + +/** 此处后端没有提供注释 GET /api/citrus/register/getApp */ +export async function getApp( + // 叠加生成的Param类型 (非body参数swagger默认没有生成对象) + params: API.getAppParams, + options?: { [key: string]: any }, +) { + return request('/api/citrus/register/getApp', { + method: 'GET', + params: { + ...params, + }, + ...(options || {}), + }); +} + +/** 此处后端没有提供注释 POST /api/citrus/register/sendCode */ +export async function sendCode( + // 叠加生成的Param类型 (非body参数swagger默认没有生成对象) + params: API.sendCodeParams, + options?: { [key: string]: any }, +) { + return request('/api/citrus/register/sendCode', { + method: 'POST', + params: { + ...params, + }, + ...(options || {}), + }); +} + +/** 此处后端没有提供注释 POST /api/citrus/register/submitRegister */ +export async function submitRegister(body: API.RegisterBo, options?: { [key: string]: any }) { + return request('/api/citrus/register/submitRegister', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + data: body, + ...(options || {}), + }); +} diff --git a/src/services/matrix/typings.d.ts b/src/services/matrix/typings.d.ts index 77b48bb..ec44b21 100644 --- a/src/services/matrix/typings.d.ts +++ b/src/services/matrix/typings.d.ts @@ -78,6 +78,10 @@ declare namespace API { appId?: string; }; + type getAppParams = { + inviteCode: string; + }; + type grantAppParams = { appIds: string; adminId: number; @@ -207,6 +211,13 @@ declare namespace API { secret?: string; }; + type MatrixAppBo = { + name?: string; + code?: string; + img?: string; + url?: string; + }; + type MatrixMockSchedule = { id?: number; appId?: number; @@ -278,6 +289,13 @@ declare namespace API { data?: CurrentUser; }; + type RegisterBo = { + mobile?: string; + code?: string; + pwd?: string; + inviteCode?: string; + }; + type RInviteInfo = { code?: number; message?: string; @@ -338,6 +356,12 @@ declare namespace API { data?: MatrixAdmin; }; + type RMatrixAppBo = { + code?: number; + message?: string; + data?: MatrixAppBo; + }; + type ROverviewBo = { code?: number; message?: string; @@ -362,12 +386,32 @@ declare namespace API { data?: string; }; + type RSTSInfo = { + code?: number; + message?: string; + data?: STSInfo; + }; + type RVoid = { code?: number; message?: string; data?: Record; }; + type sendCodeParams = { + mobile: string; + }; + + type STSInfo = { + securityToken?: string; + expiration?: string; + accessKeySecret?: string; + accessKeyId?: string; + stsRole?: string; + bucket?: string; + endpoint?: string; + }; + type updateScoreParams = { score: number; }; diff --git a/src/utils/encryptUtil.ts b/src/utils/encryptUtil.ts new file mode 100644 index 0000000..186d92d --- /dev/null +++ b/src/utils/encryptUtil.ts @@ -0,0 +1,21 @@ +import SparkMD5 from 'spark-md5'; + +async function toMD5(file: File): Promise { + return new Promise((resolve, reject) => { + const spark = new SparkMD5.ArrayBuffer(); + const fileReader = new FileReader(); + + fileReader.onload = (e) => { + spark.append(e.target?.result as ArrayBuffer); + resolve(spark.end()); + }; + + fileReader.onerror = (err) => { + reject(err); + }; + + fileReader.readAsArrayBuffer(file); + }); +} + +export { toMD5 };