上传的页面主要还是沿用上一章的登录、注册的样式,通过form
表单提交数据,目前只考虑APP的上传,不考虑组件的上传,组件主要还是用maven仓库的形式表现,最后通过服务器同步抓取maven数据更新组件(正常的管理平台不会有这玩意)。
- 上传的数据包括名称,包名,版本号,简介,业务线条(可不用),logo,Apk包,升级提示,联系人(用于找到实际对接人)。
- 上传的包和logo是通过异步的方式,需要自行处理相关地址并上传至服务器
- 上传页面可以包揽更新的能力,通过appId或者包名获取数据得到对应APP信息,用于自动填入已有信息
整个Uplaod
代码
import React, { useState, useEffect } from 'react';
import './upload.css';
import cookie from 'js-cookie';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { Button, Upload, message, Form, Input } from 'antd';
import { InboxOutlined, LoadingOutlined, PlusOutlined } from '@ant-design/icons';
import { array } from 'js-md5';
const userId = cookie.get("userId")//获取当前登录用户信息
const { Dragger } = Upload;//获取Upload组件里的Dragger组件,实现可拖拽文件上传
export default function UplaodPage () {
//获取当前query里的id,如有
let [params] = useSearchParams();
const detailId = params.get("id");
const navigate = useNavigate();
const [form] = Form.useForm();//获取表单对象
const [fileInfo, setFileInfo] = useState({})//上传的app包路径
const [logoUrl, setLogoUrl] = useState({//上传的logo路径,加一个loading状态
loading: false,
imageUrl: null
})
//上传apk包后,解析完成后返回包名和名字,其实也可以把版本号也扔回来的
const changeValue = (extra) => {
console.log(extra);
form.setFieldsValue({ packageName: `${extra.package}`, name: `${extra.name}` });
};
//用于apk上传的行为
const props = {
name: 'file',
multiple: true,
action: '/api/file/upload',
maxCount: 1,
onChange (info) {
const { status } = info.file;//获取file状态
if (status !== 'uploading') {//上传中
console.log(info.file, info.fileList);
}
if (status === 'done') {//上传结束
message.success(`${info.file.name} 文件上传成功~`);
setFileInfo(info.file.response.detail);//获取服务器返回数据
const extra = JSON.parse(info.file.response.detail.extraInfo);//json解析
changeValue(extra);//处理数据
} else if (status === 'error') {
message.error(`${info.file.name} 文件上传失败~`);
}
},
onDrop (e) {//拖入数据
console.log('Dropped files', e.dataTransfer.files);
},
progress: {//进度条类型
type: 'line'
},
};
//表单提交动作
const onFinish = (values: any) => {
console.log(values)
values.logoUrl = logoUrl.imageUrl//写入logo地址
values.downloadUrl = fileInfo.fileUrl//写入apk地址
values.userId = userId;//用户id
fetch("/app/uploadAppInfo", {
method: 'POST',
headers: { 'Content-Type': 'application/json', },
body: JSON.stringify(values),
})
.then(response => response.json())
.then(json => {
console.log(json)
if (json.result === 0) {
message.success('发布成功');
navigate('/')
} else {
message.error(json.resultNote)
}
})
.catch(e => console.log(e));
};
//logo上传数据处理
const handleChange = (info) => {
if (info.file.status === 'uploading') {
setLogoUrl({ loading: true })
return;
}
if (info.file.status === 'done') {
setLogoUrl({
imageUrl: info.file.response.detail.fileUrl,
loading: false
})
}
};
const layout = {
labelCol: {
span: 8,
},
wrapperCol: {
span: 16,
},
};
const tailLayout = {
wrapperCol: {
offset: 8,
span: 16,
},
};
const uploadButton = (<div>{logoUrl.loading ? <LoadingOutlined /> : <PlusOutlined />}<div style={{ marginTop: 8 }}>Upload</div>
</div>
);
//请求具体数据用于回写表单,useEffect保证之请求一次,react里会执行很多次,可能会造成循环,所以建议跟随生命周期
useEffect(() => {
if (detailId) {
getDetail(detailId);
}
}, []);
//请求数据
const getDetail = (id) => {
fetch("/api/app/detail/" + id)
.then(response => response.json())
.then(json => {
if (json.result === 0 && json.detail.userId === userId) {
const info = json.detail
if(info.name){
//通过form对象设置数据,具体api参考antd--form
form.setFieldsValue({
packageName: `${info.packageName}`,
name: `${info.name}`,
describe: `${info.describe}`,
appVersion: `${info.appVersion}`,
business: `${info.business}`,
updateNote: `${info.updateNote}`,
contact: `${info.contact}`,
});
setLogoUrl({
imageUrl: info.logoUrl,
loading: false
});
}
} else {
if (userId) {
message.error("出现了一些未知问题~");
} else {
message.error("登录超时,请重新登录");
navigate("/");
}
}
})
.catch(e => console.log(e))
}
return (
<div className='upload-main'>
<Form
{...layout}
form={form}
className='upload-form'
onFinish={onFinish}
scrollToFirstError
>
<Form.Item
{...tailLayout}>
<Dragger {...props} className='drag-container'>
<p className="ant-upload-drag-icon">
<InboxOutlined />
</p>
<p className="ant-upload-text">点击或拖拽APK到此区域上传</p>
</Dragger>
</Form.Item>
<Form.Item
name="packageName"
label="包名"
rules={[{ required: true, message: '请输入包名~', whitespace: true }]}
>
<Input />
</Form.Item>
<Form.Item
name="name"
label="名字"
rules={[{ required: true, message: '请输入名称~', whitespace: true }]}
>
<Input />
</Form.Item>
<Form.Item
name="logoUrl"
label="logo"
valuePropName='file'
>
<Upload
name="file"
listType="picture-card"
className="avatar-uploader"
showUploadList={false}
action="/api/file/upload"
beforeUpload={beforeUpload}
onChange={handleChange}
>
{logoUrl.imageUrl ? <img src={logoUrl.imageUrl} style={{ width: '100%' }} /> : uploadButton}
</Upload>
</Form.Item>
<Form.Item
name="describe"
label="简介"
rules={[{ required: true, message: '请输入简介内容~', whitespace: true }]}
>
<Input />
</Form.Item>
<Form.Item
name="appVersion"
label="版本"
rules={[{ required: true, message: '请输入版本号~', whitespace: true }]}
>
<Input />
</Form.Item>
<Form.Item
name="business"
label="业务线条"
rules={[{ required: true, message: '请输入业务线条~', whitespace: true }]}
>
<Input />
</Form.Item>
<Form.Item
name="updateNote"
label="升级提示"
rules={[{ required: true, message: '请输入提示语~~', whitespace: true }]}
>
<Input />
</Form.Item>
<Form.Item
name="contact"
label="联系方式"
rules={[{ required: true, message: '请输入联系方式~', whitespace: true }]}
>
<Input />
</Form.Item>
<Form.Item {...tailLayout}>
<Button htmlType='submit' className='form-submit' type='primary'>提交</Button>
</Form.Item>
</Form>
</div >
)
}
function beforeUpload (file) {
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
if (!isJpgOrPng) {
message.error('You can only upload JPG/PNG file!');
}
const isLt10M = file.size / 1024 / 1024 < 10;
if (!isLt10M) {
message.error('Image must smaller than 10MB!');
}
return isJpgOrPng && isLt10M;
}
css内容
.upload-main {
width: 100%;
padding: 10px;
}
.upload-form {
width: 70%;
}
.form-submit {
width: 100%;
}