- 概述
- 示例代碼
- 串列頁面
- form 頁面
- model.js
- service.js
- 總結
概述
專案中經常會遇到在表格中展示圖片的需求(比如展示用戶資訊時, 有一列是用戶的頭像).
antd pro table 的功能很強大, 對于常規的資訊展示只需參照示例配置 column 就可以了. 但是對于檔案(比如圖片) 在表格中的展示, 介紹并不多.
下面通過示例來演示 antd pro table 中圖片的上傳和展示.
示例代碼
前端主要包含如下 2 部分:
- 串列頁面: 通過 antd pro table 顯示資料資訊
- 表單頁面: 新建/修改資料的頁面, 上傳圖片的功能就在其中
一個模塊主要包含如下幾個檔案:
- teacher.jsx: 顯示資料串列資訊
- teacher-form.jsx: 用于添加/修改資料
- model.js: list.jsx 和 form.jsx 之間共享資料
- service.js: 訪問后端的 API
下面的例子是實際專案中的一個簡單的模塊, 完成教師資訊的 CURD, 教師的頭像是圖片檔案
串列頁面
1 import React, { useState, useRef } from 'react';
2 import { connect } from 'umi';
3 import { PageHeaderWrapper } from '@ant-design/pro-layout';
4 import { Button, Card, Modal, Space, Popconfirm, Form, message } from 'antd';
5 import { PlusOutlined, DeleteOutlined, EditOutlined } from '@ant-design/icons';
6 import ProTable from '@ant-design/pro-table';
7 import { queryAllTeacher, addTeacher, updateTeacher, deleteTeacher } from './service';
8 import { getDictDataByCatagory, getDownloadUrl } from '@/utils/common';
9 import TeacherForm from './teacher-form';
10
11 const Teacher = (props) => {
12 const { dicts, form, avatarFid } = props;
13 const [createModalVisible, handleModalVisible] = useState(false);
14
15 // preview state
16 const [previewVisible, handlePreviewVisible] = useState(false);
17 const [previewImageUrl, handlePreviewImageUrl] = useState('');
18
19 const [record, handleRecord] = useState(null);
20 const tableRef = useRef();
21
22 const previewAvatar = (record) => {
23 handlePreviewVisible(true);
24 if (record.avatar) handlePreviewImageUrl(getDownloadUrl(record.avatar));
25 else handlePreviewImageUrl('/nopic.jpg');
26 };
27
28 const teacherColumns = [
29 {
30 title: '頭像圖片',
31 dataIndex: 'avatar',
32 hideInSearch: true,
33 render: (_, record) => (
34 <a onClick={() => previewAvatar(record)}>
35 {record.avatar ? (
36 <img src=https://www.cnblogs.com/wang_yb/p/{getDownloadUrl(record.avatar)} width={50} height={60} />
37 ) : (
38
193
194
195
196 );
197 };
198
199 export default connect(({ dict, teacher }) => ({
200 dicts: dict.dicts,
201 form: teacher.form,
202 avatarFid: teacher.avatarFid,
203 }))(Teacher);
form 頁面
1 import React, { useState, useEffect } from 'react';
2 import _ from 'lodash';
3 import { connect } from 'umi';
4 import { formLayout } from '@/utils/common';
5 import { Form, Select, Input, Upload, Modal } from 'antd';
6 import { PlusOutlined, LoadingOutlined } from '@ant-design/icons';
7 import { upload } from '@/services/file';
8
9 const FormItem = Form.Item;
10 const { Option } = Select;
11 const { TextArea } = Input;
12
13 const TeacherForm = (props) => {
14 const { dispatch, dicts, record } = props;
15 const sexes = ['男', '女'];
16 const [fileList, handleFileList] = useState([]);
17 const [loading, handleLoading] = useState(false);
18 const [previewVisible, handlePreviewVisible] = useState(false);
19 const [previewTitle, handlePreviewTitle] = useState('');
20 const [previewImageUrl, handlePreviewImageUrl] = useState('');
21
22 const [form] = Form.useForm();
23 useEffect(() => {
24 if (form) {
25 form.resetFields();
26 dispatch({ type: 'teacher/setForm', payload: form });
27 }
28
29 // 初始化avatar
30 if (record && record.avatarFile) handleFileList(record.avatarFile);
31
32 if (record) dispatch({ type: 'teacher/setAvatarFid', payload: record.avatar });
33 else dispatch({ type: 'teacher/setAvatarFid', payload: '' });
34 }, []);
35
36 const handleChange = async ({ file, fileList }) => {
37 handleFileList(fileList);
38 if (file.status === 'uploading') handleLoading(true);
39 if (file.status === 'done') handleLoading(false);
40 };
41
42 const uploadButton = (
43 <div disabled>
44 {loading ? <LoadingOutlined /> : <PlusOutlined />}
45 <div className="ant-upload-text">上傳照片</div>
46 </div>
47 );
48
49 const uploadAvatar = async ({ onSuccess, one rror, file }) => {
50 const response = await upload('avatar', file);
51 try {
52 const {
53 code,
54 data: { fid },
55 } = response;
56
57 onSuccess(response, file);
58
59 dispatch({ type: 'teacher/setAvatarFid', payload: fid });
60 } catch (e) {
61 one rror(e);
62 }
63 };
64
65 const previewImage = async (file) => {
66 handlePreviewVisible(true);
67 handlePreviewTitle(file.name);
68 let src = https://www.cnblogs.com/wang_yb/p/file.url;
69 if (!src) {
70 src = await new Promise((resolve) => {
71 const reader = new FileReader();
72 reader.readAsDataURL(file.originFileObj);
73 reader.onload = () => resolve(reader.result);
74 });
75 }
76 handlePreviewImageUrl(src);
77 };
78
79 const removeImage = () => {
80 handleFileList([]);
81 dispatch({ type:'teacher/setAvatarFid', payload: '' });
82 };
83
84 const normFile = (e) => {
85 if (Array.isArray(e)) {
86 return e;
87 }
88 return e && e.fileList;
89 };
90
91 const uploadProps = {
92 name: 'avatar',
93 listType: 'picture-card',
94 className: 'avatar-uploader',
95 customRequest: uploadAvatar,
96 onPreview: previewImage,
97 onRemove: removeImage,
98 fileList: fileList,
99 };
100
101 return (
102 <div>
103 <Form form={form} {...formLayout} initialValues={record ? { ...record } : ''}>
104 <FormItem
105 label="來源型別"
106 name="teacher_source"
107 rules={[
108 {
109 required: true,
110 },
111 ]}
112 >
113 <Select
114 style={{
115 width: '100%',
116 }}
117 >
118 {_.filter(dicts, (d) => d.catagory === 'teacher_source').map((r) => (
119 <Option key={r.id} value=https://www.cnblogs.com/wang_yb/p/{r.key}>
120 {r.val}
121
122 ))}
123
124
125
134
135
136
145
156
157
193
194 