parent
377d1b03fa
commit
d901407d10
@ -0,0 +1,38 @@
|
||||
.App {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.App-logo {
|
||||
height: 40vmin;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.App-logo {
|
||||
animation: App-logo-spin infinite 20s linear;
|
||||
}
|
||||
}
|
||||
|
||||
.App-header {
|
||||
background-color: #282c34;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: calc(10px + 2vmin);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.App-link {
|
||||
color: #61dafb;
|
||||
}
|
||||
|
||||
@keyframes App-logo-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
import React from 'react';
|
||||
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
|
||||
import { Layout, Menu, ConfigProvider } from 'antd';
|
||||
import { HomeOutlined, SearchOutlined, OrderedListOutlined, ScanOutlined, UploadOutlined } from '@ant-design/icons';
|
||||
import HomePage from './pages/HomePage';
|
||||
import SearchPage from './pages/SearchPage';
|
||||
import ProductDetailPage from './pages/ProductDetailPage';
|
||||
import OrderPage from './pages/OrderPage';
|
||||
import ScannerPage from './pages/ScannerPage';
|
||||
import UploadFlowerPage from './pages/UploadFlowerPage'; // 新增
|
||||
import { theme } from './theme';
|
||||
|
||||
const { Header, Content, Footer } = Layout;
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<ConfigProvider theme={theme}>
|
||||
<Router>
|
||||
<Layout className="layout" style={{ minHeight: '100vh' }}>
|
||||
<Header style={{ background: theme.token.colorPrimary }}>
|
||||
<div className="logo" />
|
||||
<Menu mode="horizontal" defaultSelectedKeys={['1']} style={{ background: '#fff', borderBottom: 'none' }}>
|
||||
<Menu.Item key="1" icon={<HomeOutlined />}><Link to="/">主页面</Link></Menu.Item>
|
||||
<Menu.Item key="2" icon={<SearchOutlined />}><Link to="/search">搜索</Link></Menu.Item>
|
||||
<Menu.Item key="3" icon={<OrderedListOutlined />}><Link to="/orders">我的订单</Link></Menu.Item>
|
||||
<Menu.Item key="5" icon={<UploadOutlined />}><Link to="/upload">上传花卉</Link></Menu.Item>
|
||||
<Menu.Item key="4" icon={<ScanOutlined />}><Link to="/scanner">识别花卉</Link></Menu.Item>
|
||||
</Menu>
|
||||
</Header>
|
||||
<Content style={{ padding: '0 50px', marginTop: 64 }}>
|
||||
<div className="site-layout-content" style={{ background: '#fff', padding: 24, minHeight: 380 }}>
|
||||
<Routes>
|
||||
<Route path="/" element={<HomePage />} />
|
||||
<Route path="/search" element={<SearchPage />} />
|
||||
<Route path="/product/:id" element={<ProductDetailPage />} />
|
||||
<Route path="/order/:id" element={<OrderPage />} />
|
||||
<Route path="/orders" element={<OrderPage />} />
|
||||
<Route path="/scanner" element={<ScannerPage />} />
|
||||
<Route path="/upload" element={<UploadFlowerPage />} />
|
||||
</Routes>
|
||||
</div>
|
||||
</Content>
|
||||
<Footer style={{ textAlign: 'center', background: theme.token.colorBgBase }}>
|
||||
Flower Trading System ©2024
|
||||
</Footer>
|
||||
</Layout>
|
||||
</Router>
|
||||
</ConfigProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
@ -0,0 +1,8 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import App from './App';
|
||||
|
||||
test('renders learn react link', () => {
|
||||
render(<App />);
|
||||
const linkElement = screen.getByText(/learn react/i);
|
||||
expect(linkElement).toBeInTheDocument();
|
||||
});
|
@ -0,0 +1,60 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Modal, Input, InputNumber, Button, message } from 'antd';
|
||||
import axios from 'axios';
|
||||
|
||||
const EditProductModal = ({ visible, product, onCancel, onUpdate }) => {
|
||||
const [editedProduct, setEditedProduct] = useState(product);
|
||||
|
||||
const handleInputChange = (field, value) => {
|
||||
setEditedProduct({ ...editedProduct, [field]: value });
|
||||
};
|
||||
|
||||
const handleUpdate = async () => {
|
||||
try {
|
||||
const response = await axios.put(`http://localhost:5000/api/products/${product.id}`, editedProduct);
|
||||
if (response.data.message === "Product updated successfully") {
|
||||
message.success('Product updated successfully');
|
||||
onUpdate(editedProduct);
|
||||
} else {
|
||||
throw new Error('Update failed');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error updating product:', error);
|
||||
message.error('Failed to update product: ' + (error.response?.data?.error || error.message));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={`Edit ${product.name}`}
|
||||
visible={visible}
|
||||
onCancel={onCancel}
|
||||
footer={null}
|
||||
>
|
||||
<div className="p-4">
|
||||
<p>Name: <Input value={editedProduct.name} onChange={(e) => handleInputChange('name', e.target.value)} /></p>
|
||||
<p>Price: $<InputNumber
|
||||
value={editedProduct.price}
|
||||
onChange={(value) => handleInputChange('price', value)}
|
||||
min={0}
|
||||
step={0.01}
|
||||
/></p>
|
||||
<p>Available: <InputNumber
|
||||
value={editedProduct.quantity}
|
||||
onChange={(value) => handleInputChange('quantity', value)}
|
||||
min={0}
|
||||
/></p>
|
||||
<p>Description: <Input.TextArea
|
||||
value={editedProduct.description}
|
||||
onChange={(e) => handleInputChange('description', e.target.value)}
|
||||
/></p>
|
||||
<div className="flex justify-end mt-4">
|
||||
<Button onClick={onCancel} className="mr-2">取消</Button>
|
||||
<Button type="primary" onClick={handleUpdate}>完成</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditProductModal;
|
@ -0,0 +1,27 @@
|
||||
import axios from 'axios';
|
||||
|
||||
const API_URL = 'http://localhost:5000/api';
|
||||
|
||||
export const flowerApi = {
|
||||
async getAllFlowers(query = '') {
|
||||
try {
|
||||
const response = await axios.get(`${API_URL}/flowers`, {
|
||||
params: { query }
|
||||
});
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error fetching flowers:', error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
async addFlower(flowerData) {
|
||||
try {
|
||||
const response = await axios.post(`${API_URL}/flowers`, flowerData);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('Error adding flower:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
After Width: | Height: | Size: 175 KiB |
After Width: | Height: | Size: 644 KiB |
After Width: | Height: | Size: 20 KiB |
@ -0,0 +1,51 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||
monospace;
|
||||
}
|
||||
|
||||
.App {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
nav ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
nav ul li {
|
||||
display: inline;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
nav ul li a {
|
||||
text-decoration: none;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #4CAF50;
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 15px 32px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
margin: 4px 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
padding: 10px;
|
||||
margin: 10px 0;
|
||||
width: 300px;
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
After Width: | Height: | Size: 2.6 KiB |
@ -0,0 +1,182 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Tabs, List, Card, Button, message, Modal, Form, Input, InputNumber } from 'antd';
|
||||
import axios from 'axios';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
const OrderPage = () => {
|
||||
const [products, setProducts] = useState([]);
|
||||
const [orders, setOrders] = useState([]);
|
||||
const [editingProduct, setEditingProduct] = useState(null);
|
||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||
const [form] = Form.useForm();
|
||||
|
||||
useEffect(() => {
|
||||
fetchProducts();
|
||||
fetchOrders();
|
||||
}, []);
|
||||
|
||||
const fetchProducts = async () => {
|
||||
try {
|
||||
const response = await axios.get('http://localhost:5000/api/my-products');
|
||||
setProducts(response.data);
|
||||
} catch (error) {
|
||||
console.error('获取花卉信息失败:', error);
|
||||
message.error('获取花卉信息失败');
|
||||
}
|
||||
};
|
||||
|
||||
const fetchOrders = async () => {
|
||||
try {
|
||||
const response = await axios.get('http://localhost:5000/api/orders');
|
||||
setOrders(response.data);
|
||||
} catch (error) {
|
||||
console.error('获取订单信息失败:', error);
|
||||
message.error('获取订单信息失败');
|
||||
}
|
||||
};
|
||||
|
||||
const handleEditProduct = (product) => {
|
||||
console.log('Edit button clicked for product:', product);
|
||||
setEditingProduct(product);
|
||||
form.setFieldsValue(product);
|
||||
setIsModalVisible(true);
|
||||
};
|
||||
|
||||
const handleUpdateProduct = async (values) => {
|
||||
try {
|
||||
const response = await axios.put(`http://localhost:5000/api/products/${editingProduct.id}`, values);
|
||||
if (response.data.message === "Product updated successfully") {
|
||||
message.success('Product updated successfully');
|
||||
setIsModalVisible(false);
|
||||
setEditingProduct(null);
|
||||
form.resetFields();
|
||||
fetchProducts(); // Refresh the product list
|
||||
} else {
|
||||
throw new Error('Update failed');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error updating product:', error);
|
||||
message.error('Failed to update product: ' + (error.response?.data?.error || error.message));
|
||||
}
|
||||
};
|
||||
|
||||
const handleToggleProductStatus = async (productId) => {
|
||||
try {
|
||||
const response = await axios.post(`http://localhost:5000/api/products/${productId}/toggle-status`);
|
||||
if (response.data.message.includes('successfully')) {
|
||||
message.success(response.data.message);
|
||||
fetchProducts();
|
||||
} else {
|
||||
throw new Error('Toggle status failed');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error toggling product status:', error);
|
||||
message.error('Failed to update product status');
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancelOrder = async (orderId) => {
|
||||
try {
|
||||
const response = await axios.post(`http://localhost:5000/api/orders/${orderId}/cancel`);
|
||||
if (response.data.message === "Order cancelled successfully") {
|
||||
message.success('成功取消订单');
|
||||
fetchOrders();
|
||||
} else {
|
||||
throw new Error('取消订单失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to cancel order:', error);
|
||||
message.error('取消订单失败');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Tabs defaultActiveKey="1">
|
||||
<TabPane tab="我的上架" key="1">
|
||||
<List
|
||||
grid={{ gutter: 16, column: 3 }}
|
||||
dataSource={products}
|
||||
renderItem={product => (
|
||||
<List.Item>
|
||||
<Card
|
||||
title={product.name}
|
||||
extra={<Button onClick={() => handleEditProduct(product)}>编辑</Button>}
|
||||
actions={[
|
||||
<Button onClick={() => handleToggleProductStatus(product.id)}>
|
||||
{product.is_active ? '下架' : '上架'}
|
||||
</Button>
|
||||
]}
|
||||
>
|
||||
<p>价格: ¥{product.price}</p>
|
||||
<p>数量: {product.quantity}</p>
|
||||
<p>状态: {product.is_active ? '上架' : '未上架'}</p>
|
||||
</Card>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
</TabPane>
|
||||
<TabPane tab="我的订单" key="2">
|
||||
<List
|
||||
dataSource={orders}
|
||||
renderItem={order => (
|
||||
<List.Item>
|
||||
<Card
|
||||
title={`订单 #${order.id} - ${order.flowerName}`}
|
||||
extra={
|
||||
order.status === 'pending' ?
|
||||
<Button onClick={() => handleCancelOrder(order.id)}>取消订单</Button> :
|
||||
null
|
||||
}
|
||||
>
|
||||
<p>数量: {order.quantity}</p>
|
||||
<p>总价: ¥{order.totalPrice}</p>
|
||||
<p>状态: {order.status}</p>
|
||||
<p>订单日期: {new Date(order.createdAt).toLocaleString()}</p>
|
||||
</Card>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
|
||||
<Modal
|
||||
title="编辑花卉信息"
|
||||
visible={isModalVisible}
|
||||
onCancel={() => {
|
||||
setIsModalVisible(false);
|
||||
setEditingProduct(null);
|
||||
form.resetFields();
|
||||
}}
|
||||
footer={null}
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
onFinish={handleUpdateProduct}
|
||||
layout="vertical"
|
||||
>
|
||||
<Form.Item name="name" label="花名" rules={[{ required: true }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name="price" label="单价" rules={[{ required: true }]}>
|
||||
<InputNumber min={0} step={0.01} />
|
||||
</Form.Item>
|
||||
<Form.Item name="quantity" label="数量" rules={[{ required: true }]}>
|
||||
<InputNumber min={0} />
|
||||
</Form.Item>
|
||||
<Form.Item name="description" label="描述">
|
||||
<Input.TextArea />
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit">
|
||||
更新花卉
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default OrderPage;
|
@ -0,0 +1,27 @@
|
||||
import React from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
|
||||
function ProductDetailPage() {
|
||||
const { id } = useParams();
|
||||
const navigate = useNavigate();
|
||||
|
||||
// 模拟产品数据
|
||||
const product = { id, name: 'Sample Flower', price: 10, description: 'A beautiful flower' };
|
||||
|
||||
const handleOrder = () => {
|
||||
// 模拟下单过程
|
||||
const orderId = Math.floor(Math.random() * 1000);
|
||||
navigate(`/order/${orderId}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>{product.name}</h2>
|
||||
<p>单价 ¥{product.price}</p>
|
||||
<p>{product.description}</p>
|
||||
<button onClick={handleOrder}>Place Order</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ProductDetailPage;
|
@ -0,0 +1,87 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Upload, Button, Card, Typography, Space } from 'antd';
|
||||
import { UploadOutlined } from '@ant-design/icons';
|
||||
import axios from 'axios';
|
||||
|
||||
const { Title, Text } = Typography;
|
||||
|
||||
export default function FlowerScanner() {
|
||||
const [imageUrl, setImageUrl] = useState('');
|
||||
const [scanning, setScanning] = useState(false);
|
||||
const [result, setResult] = useState(null);
|
||||
|
||||
const handleUpload = async (info) => {
|
||||
const { status } = info.file;
|
||||
|
||||
if (status === 'done') {
|
||||
setScanning(true);
|
||||
const formData = new FormData();
|
||||
formData.append('image', info.file.originFileObj);
|
||||
|
||||
try {
|
||||
const response = await axios.post('http://localhost:5000/api/identify', formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
});
|
||||
|
||||
const url = URL.createObjectURL(info.file.originFileObj);
|
||||
setImageUrl(url);
|
||||
setScanning(false);
|
||||
setResult(response.data);
|
||||
} catch (error) {
|
||||
console.error('识别期间出现问题:', error);
|
||||
setScanning(false);
|
||||
setResult({ error: '识别失败,请重新尝试' });
|
||||
}
|
||||
} else if (status === 'error') {
|
||||
setResult({ error: `${info.file.name} file upload failed.` });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Space direction="vertical" size="large" style={{ display: 'flex' }}>
|
||||
<Title level={2}>花卉识别</Title>
|
||||
<Text>上传一张花卉图片以供识别:</Text>
|
||||
<Upload
|
||||
name="image"
|
||||
action="http://localhost:5000/api/identify"
|
||||
onChange={handleUpload}
|
||||
accept="image/*"
|
||||
showUploadList={false}
|
||||
>
|
||||
<Button icon={<UploadOutlined />} loading={scanning}>
|
||||
{scanning ? '识别中' : '上传图片'}
|
||||
</Button>
|
||||
</Upload>
|
||||
|
||||
{result && (
|
||||
<Card title="识别结果:" style={{ width: 300 }}>
|
||||
{result.error ? (
|
||||
<Text type="danger">{result.error}</Text>
|
||||
) : (
|
||||
<>
|
||||
<img src={imageUrl} alt="花卉识别图片" width={250} height={200}></img>
|
||||
<Text strong>花名: </Text>
|
||||
<Text>{result.name}</Text>
|
||||
<br />
|
||||
{result.probability && (
|
||||
<>
|
||||
<Text strong>识别正确概率: </Text>
|
||||
<Text>{(result.probability * 100).toFixed(2)}%</Text>
|
||||
<br />
|
||||
</>
|
||||
)}
|
||||
{result.description && (
|
||||
<>
|
||||
<Text strong>描述: </Text>
|
||||
<Text>{result.description}</Text>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Card>
|
||||
)}
|
||||
</Space>
|
||||
);
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Form, Input, InputNumber, Button, message, Upload } from 'antd';
|
||||
import { UploadOutlined } from '@ant-design/icons';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import axios from 'axios';
|
||||
|
||||
const UploadFlowerPage = () => {
|
||||
const [form] = Form.useForm();
|
||||
const [imageFile, setImageFile] = useState(null);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const onFinish = async (values) => {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('name', values.name.trim());
|
||||
formData.append('description', (values.description || '').trim());
|
||||
formData.append('price', values.price.toString());
|
||||
formData.append('quantity', values.quantity.toString());
|
||||
if (imageFile) {
|
||||
formData.append('image', imageFile);
|
||||
}
|
||||
|
||||
const response = await axios.post('http://localhost:5000/api/flowers', formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
});
|
||||
|
||||
message.success('成功上传花卉!');
|
||||
form.resetFields();
|
||||
setImageFile(null);
|
||||
navigate('/search');
|
||||
} catch (error) {
|
||||
console.error('上传花卉时出现错误:', error);
|
||||
if (error.response && error.response.data && error.response.data.error) {
|
||||
message.error(`上传花卉失败: ¥{error.response.data.error}`);
|
||||
} else {
|
||||
message.error('上传失败,请重新上传');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleImageUpload = ({ file, onSuccess }) => {
|
||||
setImageFile(file);
|
||||
onSuccess("好");
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>上传花卉</h1>
|
||||
<Form form={form} layout="vertical" onFinish={onFinish}>
|
||||
<Form.Item
|
||||
name="name"
|
||||
label="花名"
|
||||
rules={[{ required: true, message: '请输入花名!' }]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name="description" label="描述">
|
||||
<Input.TextArea />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="price"
|
||||
label="单价"
|
||||
rules={[
|
||||
{ required: true, message: '请输入价格!' },
|
||||
{ type: 'number', min: 0, message: '价格需要是有效数字!' }
|
||||
]}
|
||||
>
|
||||
<InputNumber style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="quantity"
|
||||
label="数量"
|
||||
rules={[
|
||||
{ required: true, message: '请输入花卉数量!' },
|
||||
{ type: 'number', min: 1, message: '数量应该至少为1!' }
|
||||
]}
|
||||
>
|
||||
<InputNumber style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
<Form.Item label="图片">
|
||||
<Upload
|
||||
customRequest={handleImageUpload}
|
||||
listType="picture"
|
||||
maxCount={1}
|
||||
beforeUpload={(file) => {
|
||||
const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
|
||||
if (!isJpgOrPng) {
|
||||
message.error('只能上传JPG/PNG文件!');
|
||||
}
|
||||
return isJpgOrPng;
|
||||
}}
|
||||
>
|
||||
<Button icon={<UploadOutlined />}>上传图片</Button>
|
||||
</Upload>
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Button type="primary" htmlType="submit">
|
||||
上传花卉
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UploadFlowerPage;
|
@ -0,0 +1,13 @@
|
||||
const reportWebVitals = onPerfEntry => {
|
||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||
getCLS(onPerfEntry);
|
||||
getFID(onPerfEntry);
|
||||
getFCP(onPerfEntry);
|
||||
getLCP(onPerfEntry);
|
||||
getTTFB(onPerfEntry);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default reportWebVitals;
|
@ -0,0 +1,12 @@
|
||||
export const theme = {
|
||||
token: {
|
||||
colorPrimary: '#FF69B4', // 热粉红色
|
||||
colorLink: '#FF1493', // 深粉红色
|
||||
colorSuccess: '#98FB98', // 淡绿色,与粉色搭配
|
||||
colorWarning: '#FFB6C1', // 浅粉红色
|
||||
colorError: '#FF69B4', // 热粉红色
|
||||
colorInfo: '#FFC0CB', // 粉红色
|
||||
colorTextBase: '#4B0082', // 靛青色,作为主要文字颜色
|
||||
colorBgBase: '#FFF0F5', // 浅粉红色,作为背景色
|
||||
},
|
||||
};
|
Loading…
Reference in new issue