主頁 > 企業開發 > antd pro table中的檔案上傳

antd pro table中的檔案上傳

2020-09-10 18:28:54 企業開發

  • 概述
  • 示例代碼
    • 串列頁面
    • form 頁面
    • model.js
    • service.js
  • 總結

概述

專案中經常會遇到在表格中展示圖片的需求(比如展示用戶資訊時, 有一列是用戶的頭像).

antd pro table 的功能很強大, 對于常規的資訊展示只需參照示例配置 column 就可以了. 但是對于檔案(比如圖片) 在表格中的展示, 介紹并不多.

下面通過示例來演示 antd pro table 中圖片的上傳和展示.

示例代碼

前端主要包含如下 2 部分:

  1. 串列頁面: 通過 antd pro table 顯示資料資訊
  2. 表單頁面: 新建/修改資料的頁面, 上傳圖片的功能就在其中

一個模塊主要包含如下幾個檔案:

  1. teacher.jsx: 顯示資料串列資訊
  2. teacher-form.jsx: 用于添加/修改資料
  3. model.js: list.jsx 和 form.jsx 之間共享資料
  4. 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              "preview"
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      
195 ); 196 }; 197 198 export default connect(({ dict }) => ({ 199 dicts: dict.dicts, 200 }))(TeacherForm);

model.js

 1  import { message } from 'antd';
 2  
 3  const Model = {
 4    namespace: 'teacher',
 5    state: {
 6      form: null,
 7      avatarFid: '',
 8    },
 9  
10    effects: {},
11    reducers: {
12      setForm(state, { payload }) {
13        return {
14          ...state,
15          form: payload,
16        };
17      },
18      setAvatarFid(state, { payload }) {
19        return {
20          ...state,
21          avatarFid: payload,
22        };
23      },
24    },
25  };
26  export default Model;

service.js

 1  import { graphql } from '@/services/graphql_client';
 2  import md5 from 'md5';
 3  import moment from 'moment';
 4  
 5  const gqlQueryAll = `
 6  query search_teacher($login_name: String, $mobile: String, $limit: Int!, $offset: Int!) {
 7    teacher(order_by: {updated_at: desc}, limit: $limit, offset: $offset, where: {login_name: {_ilike: $login_name}, mobile: {_ilike: $mobile}}) {
 8      id
 9      avatar
10      comment
11      identity_card
12      login_name
13      mobile
14      sex
15      teacher_source
16    }
17    teacher_aggregate(where: {login_name: {_ilike: $login_name}, mobile: {_ilike: $mobile}}) {
18      aggregate {
19        count
20      }
21    }
22  }
23  `;
24  
25  const qplAddTeacher = `
26  mutation add_teacher($avatar: uuid, $comment: String, $identity_card: String, $login_name: String!, $mobile: String, $sex: String!, $teacher_source: String!, $password: String!){
27    insert_teacher_one(object: {avatar: $avatar, comment: $comment, identity_card: $identity_card, login_name: $login_name, mobile: $mobile, sex: $sex, teacher_source: $teacher_source, password: $password}) {
28      id
29    }
30  }
31  `;
32  
33  const qplUpdateTeacher = `
34  mutation update_teacher($id: uuid!, $avatar: uuid, $comment: String, $identity_card: String, $login_name: String, $mobile: String, $sex: String, $teacher_source: String) {
35    update_teacher_by_pk(_set: {avatar: $avatar, comment: $comment, identity_card: $identity_card, login_name: $login_name, mobile: $mobile, sex: $sex, teacher_source: $teacher_source}, pk_columns: {id: $id}) {
36      id
37    }
38  }
39  `;
40  
41  const qplDeleteTeacher = `
42  mutation del_teacher($id: uuid!){
43    delete_teacher_by_pk(id: $id) {
44      id
45    }
46  }
47  `;
48  
49  export async function queryAllTeacher(params) {
50    let qplVar = {
51      limit: params.pageSize,
52      offset: (params.current - 1) * params.pageSize,
53    };
54  
55    if (params.login_name) qqlVar.login_name = '%' + params.login_name + '%';
56    if (params.mobile) qqlVar.mobile = '%' + params.mobile + '%';
57  
58    return graphql(gqlQueryAll, qplVar);
59  }
60  
61  export async function addTeacher(params) {
62    const { avatar, comment, identity_card, mobile, sex, login_name, teacher_source } = params;
63  
64    let insertVar = { login_name, sex, mobile, teacher_source };
65    if (avatar !== '') insertVar.avatar = avatar;
66    if (identity_card) insertVar.identity_card = identity_card;
67    if (comment) insertVar.comment = comment;
68    if (mobile) {
69      insertVar.mobile = mobile;
70      insertVar.password = md5(mobile.slice(-6));
71    } else {
72      // default password
73      insertVar.password = md5('123456');
74    }
75  
76    return graphql(qplAddTeacher, {
77      ...insertVar,
78    });
79  }
80  
81  export async function updateTeacher(id, params) {
82    let { avatar, comment, identity_card, mobile, sex, login_name, teacher_source } = params;
83    if (avatar === '') avatar = null;
84    return graphql(qplUpdateTeacher, {
85      id,
86      avatar,
87      comment,
88      identity_card,
89      mobile,
90      sex,
91      login_name,
92      teacher_source,
93    });
94  }
95  
96  export async function deleteTeacher(id) {
97    return graphql(qplDeleteTeacher, { id });
98  }

service.js 中的請求是 graphql api

總結

  1. 這個模塊的 增和改 用的同一個頁面, 因為是彈出的 modal, 所有實際的提交功能是在 teacher.jsx 中完成的

  2. antd upload 組件的 外圍 FormItem 需要加上如下屬性(valuePropName 和 getValueFromEvent):

    1  <FormItem
    2    label="用戶頭像"
    3    name="avatarFile"
    4    valuePropName="fileList"
    5    getValueFromEvent={normFile}
    6  >
    7      <Upload />
    8  </FormItem>
    
  3. antd upload 組件雖然有默認的上傳事件, 但是如果自定義上傳的事件, 可以更方便的和自己的后端 API 進行對接

     1  const uploadAvatar = async ({ onSuccess, one rror, file }) => {
     2    const response = await upload('avatar', file);
     3    try {
     4      const {
     5        code,
     6        data: { fid },
     7      } = response;
     8  
     9      onSuccess(response, file);
    10  
    11      dispatch({ type: 'teacher/setAvatarFid', payload: fid });
    12    } catch (e) {
    13      one rror(e);
    14    }
    15  };
    

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/3058.html

標籤:JavaScript

上一篇:小程式 批注/筆跡

下一篇:vue3剖析:回應式原理——effect

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • IEEE1588PTP在數字化變電站時鐘同步方面的應用

    IEEE1588ptp在數字化變電站時鐘同步方面的應用 京準電子科技官微——ahjzsz 一、電力系統時間同步基本概況 隨著對IEC 61850標準研究的不斷深入,國內外學者提出基于IEC61850通信標準體系建設數字化變電站的發展思路。數字化變電站與常規變電站的顯著區別在于程序層傳統的電流/電壓互 ......

    uj5u.com 2020-09-10 03:51:52 more
  • HTTP request smuggling CL.TE

    CL.TE 簡介 前端通過Content-Length處理請求,通過反向代理或者負載均衡將請求轉發到后端,后端Transfer-Encoding優先級較高,以TE處理請求造成安全問題。 檢測 發送如下資料包 POST / HTTP/1.1 Host: ac391f7e1e9af821806e890 ......

    uj5u.com 2020-09-10 03:52:11 more
  • 網路滲透資料大全單——漏洞庫篇

    網路滲透資料大全單——漏洞庫篇漏洞庫 NVD ——美國國家漏洞庫 →http://nvd.nist.gov/。 CERT ——美國國家應急回應中心 →https://www.us-cert.gov/ OSVDB ——開源漏洞庫 →http://osvdb.org Bugtraq ——賽門鐵克 →ht ......

    uj5u.com 2020-09-10 03:52:15 more
  • 京準講述NTP時鐘服務器應用及原理

    京準講述NTP時鐘服務器應用及原理京準講述NTP時鐘服務器應用及原理 安徽京準電子科技官微——ahjzsz 北斗授時原理 授時是指接識訓通過某種方式獲得本地時間與北斗標準時間的鐘差,然后調整本地時鐘使時差控制在一定的精度范圍內。 衛星導航系統通常由三部分組成:導航授時衛星、地面檢測校正維護系統和用戶 ......

    uj5u.com 2020-09-10 03:52:25 more
  • 利用北斗衛星系統設計NTP網路時間服務器

    利用北斗衛星系統設計NTP網路時間服務器 利用北斗衛星系統設計NTP網路時間服務器 安徽京準電子科技官微——ahjzsz 概述 NTP網路時間服務器是一款支持NTP和SNTP網路時間同步協議,高精度、大容量、高品質的高科技時鐘產品。 NTP網路時間服務器設備采用冗余架構設計,高精度時鐘直接來源于北斗 ......

    uj5u.com 2020-09-10 03:52:35 more
  • 詳細解讀電力系統各種對時方式

    詳細解讀電力系統各種對時方式 詳細解讀電力系統各種對時方式 安徽京準電子科技官微——ahjzsz,更多資料請添加VX 衛星同步時鐘是我京準公司開發研制的應用衛星授時時技術的標準時間顯示和發送的裝置,該裝置以M國全球定位系統(GLOBAL POSITIONING SYSTEM,縮寫為GPS)或者我國北 ......

    uj5u.com 2020-09-10 03:52:45 more
  • 如何保證外包團隊接入企業內網安全

    不管企業規模的大小,只要企業想省錢,那么企業的某些服務就一定會采用外包的形式,然而看似美好又經濟的策略,其實也有不好的一面。下面我通過安全的角度來聊聊使用外包團的安全隱患問題。 先看看什么服務會使用外包的,最常見的就是話務/客服這種需要大量重復性、無技術性的服務,或者是一些銷售外包、特殊的職能外包等 ......

    uj5u.com 2020-09-10 03:52:57 more
  • PHP漏洞之【整型數字型SQL注入】

    0x01 什么是SQL注入 SQL是一種注入攻擊,通過前端帶入后端資料庫進行惡意的SQL陳述句查詢。 0x02 SQL整型注入原理 SQL注入一般發生在動態網站URL地址里,當然也會發生在其它地發,如登錄框等等也會存在注入,只要是和資料庫打交道的地方都有可能存在。 如這里http://192.168. ......

    uj5u.com 2020-09-10 03:55:40 more
  • [GXYCTF2019]禁止套娃

    git泄露獲取原始碼 使用GET傳參,引數為exp 經過三層過濾執行 第一層過濾偽協議,第二層過濾帶引數的函式,第三層過濾一些函式 preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'] (?R)參考當前正則運算式,相當于匹配函式里的引數 因此傳遞 ......

    uj5u.com 2020-09-10 03:56:07 more
  • 等保2.0實施流程

    流程 結論 ......

    uj5u.com 2020-09-10 03:56:16 more
最新发布
  • 使用Django Rest framework搭建Blog

    在前面的Blog例子中我們使用的是GraphQL, 雖然GraphQL的使用處于上升趨勢,但是Rest API還是使用的更廣泛一些. 所以還是決定回到傳統的rest api framework上來, Django rest framework的官網上給了一個很好用的QuickStart, 我參考Qu ......

    uj5u.com 2023-04-20 08:17:54 more
  • 記錄-new Date() 我忍你很久了!

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 大家平時在開發的時候有沒被new Date()折磨過?就是它的諸多怪異的設定讓你每每用的時候,都可能不小心踩坑。造成程式意外出錯,卻一下子找不到問題出處,那叫一個煩透了…… 下面,我就列舉它的“四宗罪”及應用思考 可惡的四宗罪 1. Sa ......

    uj5u.com 2023-04-20 08:17:47 more
  • 使用Vue.js實作文字跑馬燈效果

    實作文字跑馬燈效果,首先用到 substring()截取 和 setInterval計時器 clearInterval()清除計時器 效果如下: 實作代碼如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta ......

    uj5u.com 2023-04-20 08:12:31 more
  • JavaScript 運算子

    JavaScript 運算子/運算子 在 JavaScript 中,有一些運算子可以使代碼更簡潔、易讀和高效。以下是一些常見的運算子: 1、可選鏈運算子(optional chaining operator) ?.是可選鏈運算子(optional chaining operator)。?. 可選鏈操 ......

    uj5u.com 2023-04-20 08:02:25 more
  • CSS—相對單位rem

    一、概述 rem是一個相對長度單位,它的單位長度取決于根標簽html的字體尺寸。rem即root em的意思,中文翻譯為根em。瀏覽器的文本尺寸一般默認為16px,即默認情況下: 1rem = 16px rem布局原理:根據CSS媒體查詢功能,更改根標簽的字體尺寸,實作rem單位隨螢屏尺寸的變化,如 ......

    uj5u.com 2023-04-20 08:02:21 more
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 08:01:50 more
  • 如何在 vue3 中使用 jsx/tsx?

    我們都知道,通常情況下我們使用 vue 大多都是用的 SFC(Signle File Component)單檔案組件模式,即一個組件就是一個檔案,但其實 Vue 也是支持使用 JSX 來撰寫組件的。這里不討論 SFC 和 JSX 的好壞,這個仁者見仁智者見智。本篇文章旨在帶領大家快速了解和使用 Vu ......

    uj5u.com 2023-04-20 08:01:37 more
  • 【Vue2.x原始碼系列06】計算屬性computed原理

    本章目標:計算屬性是如何實作的?計算屬性快取原理以及洋蔥模型的應用?在初始化Vue實體時,我們會給每個計算屬性都創建一個對應watcher,我們稱之為計算屬性watcher ......

    uj5u.com 2023-04-20 08:01:31 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:01:10 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:00:32 more