日常ProComponent(Ant Design Pro)

HappyCodingTop發表於2022-05-14

ProComponent就是算是對antd的又一次整合和封裝,減少了前端對於細節和聯動的處理,總之就是用起來特別爽。
那這裡就不對ProComponent做過多介紹了,我們直奔主題,如何使用,或者說如何更優雅、更方便的使用元件和編寫程式碼一直是任何一位程式設計師的核心追求,我也是PiuPiuPiu~!

columns的配置

treeSelect

request可以用來調介面獲取值
labelInValue:是設定的取到的是這個表單項值的物件,不僅僅包括value,下面的例子取到的就是fieldNames物件

{
            title: '區域',
            dataIndex: 'areaIds',
            hideInTable: true,
            valueType: 'treeSelect',
            request: async () => {
              let res = await areaSituation();
              if (res?.status == '00') {
                return res.data;
              }
            },
            fieldProps: {
              showArrow: false,
              filterTreeNode: true,
              showSearch: true,
              dropdownMatchSelectWidth: false,
              autoClearSearchValue: true,
              treeNodeFilterProp: 'title',
              labelInValue: true,
              fieldNames: {
                label: 'title',
                lvl: 'lvl',
                value: 'nodeId',
                children: 'childrenList',
              },
              onSelect: (_, dataObj) => {
                setAreaIds([]);
                let arr = [];
                let getData = (data) => {
                  arr = [...arr, data?.nodeId];
                  if (data?.childrenList.length > 0) {
                    data?.childrenList?.forEach((item) => {
                      getData(item);
                    });
                  }
                };
                getData(dataObj);
                setAreaIds(arr);
              },
            },
          },
dateTimeRange

ProFormDateTimePicker這個是可取值到年月日時分秒
ProFormDatePicker 是支援到年月日
可用moment設定初始值,
這個預設取得值是一個陣列 如果搜尋後端需要的這個時間欄位不是陣列而是其他的兩個欄位;可用下面的寫法

{
            title: '操作時間',
            dataIndex: 'actTime',
            hideInTable: true,
            valueType: 'dateTimeRange',
            // initialValue: [moment().subtract(30, 'days'), moment()],
            fieldProps: {
              placeholder: ['入庫開始時間', '入庫結束時間'],
            },
            search: {
              transform: (value) => {
                return {
                  startTime: value[0],
                  endTime: value[1],
                };
              },
            },
          },
radio

status可設定渲染到table的狀態
sorter表格的某一項排序

{
                  title: '提成方式',
                  dataIndex: 'commissionMode',
                  sorter: true,
                  formItemProps:{
                    initialValue:1,
                  },
                  valueType: 'radio',
                  valueEnum: { 0: '固定', 1: '比例' },
 },

{
      title: '狀態',
      dataIndex: 'taskState',
      key: 'taskState',
      valueEnum: {
        false: { text: '失敗', status: 'error' },
        true: { text: '成功', status: 'success' },
      },
      hideInSearch: true,
    },
renderFormItem

可用SelectAccount直接渲染元件在單列表框裡面 比如在EditableProTable裡面,
當然這裡是用在搜尋框的(還是提倡用valueType的方式,但是如果在多個地方使用,封裝成元件這樣使用也是上選)

 {
              title: '冷藏費記賬賬戶',
              dataIndex: 'coldStorageFeeAccountId',
              formItemProps: { rules: [{ required: true }] },
              renderFormItem: (_, { type, ...rest }) => (
                <SelectAccount labelInValue params={{ projectId: rest.recordKey }} />
              ),
            },
digit

渲染成只能輸入數字的搜尋框

{
              title: '件數',
              dataIndex: 'quantity',
              valueType: 'digit',
              fieldProps: { precision: 0, min: 1 },
              formItemProps: { rules: [{ required: true }] },
            },

或者可自定義在search和table的title文字不一樣

fieldProps:可設定搜素框的狀態

{
      title: (_, type) => (type === 'table' ? '操作賬號' : ''),
      dataIndex: 'operatorMobile',
      key: 'operatorMobile',
      //fieldProps: { readOnly: true, placeholder: '請先選擇雜費' },
      fieldProps: {
        placeholder: '操作人賬號',
      },
    },

ProForm表單項

ProFormDependency

可用來監聽表單項的某一項改變之後所做的操作,
name為什麼是陣列呢?

  • 是因為在表單提交的時候呢contractType是作為inboundAppointment物件的一個屬性值進行提交的,
  • 這樣寫的好處就是前端在提交的函式中不用再重新組裝一下這個提交引數了
<ProFormSelect
          width="sm"
          name={['inboundAppointment', 'contractType']}
          label="合同型別"
          rules={[{ required: true, message: '請選擇合同型別' }]}
          initialValue={'2'}
          options={[
            // { value: '0', label: '零倉' },
            { value: '1', label: '包倉' },
            { value: '2', label: '無合同' },
          ]}
          placeholder="包倉/零倉"
        />
        <ProFormDependency name={[['inboundAppointment', 'contractType']]}>
          {({ inboundAppointment }) => {
            if (
              inboundAppointment?.contractType == '1' ||
              inboundAppointment?.contractType == '0'
            ) {
              return (
                <ProFormSelect
                  width="sm"
                  name={['inboundAppointment', 'contractNo']}
                  rules={[{ required: true, message: '請選擇合同' }]}
                  label="合同"
                  options={[
                    { value: '>', label: '大於' },
                    { value: '<', label: '小於' },
                  ]}
                  placeholder="請選擇合同"
                />
              );
            }
          }}
        </ProFormDependency>
ProFormFieldSet

上面我們說到name設定成陣列的形式,可以幫我們省略組裝引數的操作。
那這個更厲害了,可以幫我們提前組裝或者拆分提交表單的引數。
那我們看一下官方這裡是怎麼說的:“ProFormFieldSet 可以將內部的多個 children 的值組合並且儲存在 ProForm 中,並且可以通過 transform 在提交時轉化”,
言而簡之,就是我剛說的意思,對吧~
下面的例子也就是將取到的SelectSubject 物件值進行拆分,儲存在ProForm中。

      <ProFormFieldSet
        width="md"
        name={['subject']}
        transform={(v) => ({ subjectName: v[0]?.label, subjectId: v[0]?.value })}
        label="費用型別"
        rules={[{ required: true }]}
      >
        <SelectSubject className="pro-field-md" labelInValue />
      </ProFormFieldSet>
ProFormText ProForm.Group ProFormDateTimePicker

ProFormText一個簡單的表單項
ProFormDateTimePicker:時間選擇器年月日可精確到時分秒
ProForm.Group: 可將表單項在空間範圍內允許的情況下,做一行展示

const [time, setTime] = useState([moment().startOf('year'), moment().endOf('year')])
<ProForm.Group title="運輸車輛">
          <ProFormText
            width="sm"
            name={['outboundAppointment', 'plateNum']}
            label="車牌號"
            placeholder="請輸入車牌號"
          />
          <ProFormDateTimePicker
            width="sm"
            name={['outboundAppointment', 'expectArriveTime']}
            label="預計到場時間"
          />
        </ProForm.Group>
ProFormDigit

一個只能輸入數字的表單項

<ProFormDigit
                                label="預收天數"
                                name="unitTotal"
                                min={1}
                                max={10}
                                placeholder="請輸入預收天數"
                                fieldProps={{ precision: 0 }}
                            />
ProFormGroup

列表項歸類

 <ProFormGroup label="甲方資訊">
              <ProFormText
                width="sm"
                name={'namejia'}
                label="甲方名稱"
                placeholder="請輸入甲方名稱"
              />
              <ProFormText width="sm" name={'namejia'} label="手機號" placeholder="請輸入手機號" />
              <ProFormText
                width="sm"
                name={'namejia'}
                label="身份證號"
                placeholder="請輸入身份證號"
              />
            </ProFormGroup>

DrawerForm

 const [form] = ProForm.useForm();
<DrawerForm
      submitter={{
        render: () => {
          return [
            <Button key="export" type="primary" onClick={async () => {
              await exportColdFeeList(exportData)
            }}>
              匯出
            </Button>,
          ];
        },
      }}
      width={'70%'}
      layout="horizontal"
      form={form}
      title="客戶賬單明細"
      visible={visible}
      onVisibleChange={setVisible}
      drawerProps={{
        destroyOnClose: true,
      }}
    />

ProDescriptions

官方言:高階描述列表元件,提供一個更加方便快速的方案來構建描述列表。
我是用在列表項的詳情裡面的,通table的用法類似。

<ProDescriptions
          columns={[
            { title: '客戶', dataIndex: '' },
            { title: '合同型別', dataIndex: '' },
            { title: '移位日期', dataIndex: '' },
            { title: '操作人', dataIndex: 'operator' },
            { title: '操作時間', dataIndex: '' },
            { title: '確認人', dataIndex: '' },
            { title: '確認時間', dataIndex: '', span: 3 },
            {
              span: 3,
              render: () => (
                <ProTable
                  headerTitle="移位商品"
                  style={{ width: '100%' }}
                  rowKey="id"
                  dataSource={[{ id: 0 }]}
                  options={false}
                  search={false}
                  columns={[
                    { title: '批次號', dataIndex: '' },
                    { title: '商品', dataIndex: '', sorter: true },
                    { title: '移出件數', dataIndex: '' },
                    { title: '移出重量', dataIndex: '', sorter: true },
                    { title: '移出板數', dataIndex: '', sorter: true },
                    { title: '移出庫位', dataIndex: '' },
                    { title: '移入庫位', dataIndex: '', sorter: true },
                    { title: '移入板數', dataIndex: '', sorter: true },
                  ]}
                />
              ),
            },
            {
              span: 3,
              render: () => (
                <ProTable
                  headerTitle="雜費專案"
                  style={{ width: '100%' }}
                  rowKey="id"
                  dataSource={[{ id: 0 }]}
                  options={false}
                  search={false}
                  columns={[
                    { title: '雜費名稱', dataIndex: '' },
                    { title: '收費依據', dataIndex: '', sorter: true },
                    { title: '單價', dataIndex: '' },
                    { title: '件數', dataIndex: '', sorter: true },
                    { title: '小計', dataIndex: '', sorter: true },
                  ]}
                />
              ),
            },
            {
              span: 3,
              render: () => (
                <ProTable
                  headerTitle="作業人員"
                  style={{ width: '100%' }}
                  rowKey="id"
                  dataSource={[{ id: 0 }]}
                  options={false}
                  search={false}
                  columns={[
                    { title: '姓名', dataIndex: '' },
                    { title: '所屬團隊', dataIndex: '' },
                    { title: '作業型別', dataIndex: '' },
                    { title: '雜費名稱', dataIndex: '' },
                    { title: '作業數量', dataIndex: '' },
                    { title: '作業費用', dataIndex: '' },
                  ]}
                />
              ),
            },
            {
              title: '相關附件',
              dataIndex: 'filePath',
              span: 3,
              render: (text) => {
                // let dataArr = text?.includes(',') ? text?.split(',') : [text];
                // return (
                //   <>
                //     {dataArr?.map((item, index) => {
                //       return (
                //         <a key={index} href={item.url}>
                //           {item.name}
                //         </a>
                //       );
                //     })}
                //   </>
                // );
              },
            },
            { title: '備註', dataIndex: 'remark', span: 3 },
          ]}
        />

ProTable EditableProTable

兩種表格, 一種用的最多的普通表格,一種是可編輯的表格
使用相似度很高。

ProTable

這個ProTable的配置有意思的地方有三:

  1. params自帶pageSize,pageNumber
  2. rowSelection可做批量配置操作
  3. 只有單個條件搜尋的時候可search設定為false,options的search單獨設定,那如果你需要多個條件搜尋的時候,可單獨對搜尋框做一些定製化配置
    當然作為一個高階定製化元件,只有這麼些整合肯定是不夠的,

高階配置
tableExtraRender
這是配置table列表上部,搜尋區域的下部的中間區域,當然如果你有把這個移動到最頂部的需求,可利用css樣式去覆蓋,傾囊寫上

<ProTable
  tableExtraRender={() => (
          <Row gutter={16}>
            <Col span={6}>
              <Card>
                <Statistic title="入庫單總數" value={topData?.all?.totalCount} />
              </Card>
            </Col>
            <Col span={6}>
              <Card>
                <Statistic title="入庫噸重" value={topData?.all?.weight} />
              </Card>
            </Col>
            <Col span={6}>
              <Card>
                <Statistic title="今日入庫單數" value={topData?.today?.totalCount} />
              </Card>
            </Col>
            <Col span={6}>
              <Card>
                <Statistic title="今日入庫噸重" value={topData?.today?.weight} />
              </Card>
            </Col>
          </Row>
        )}
    //search={{
          //labelWidth: 0,
         // collapsed: false,
         // collapseRender: false,
       // }}
        rowKey="id"
        scroll={{ x: 960 }}
        columns={[
          { title: '商品名稱', dataIndex: 'goodsName' },
          { title: '所屬分類', dataIndex: 'category' },
        ]}
        params={{ status: 0 }}
        request={async (params = {}) => {
          const json = await getGlobalCfgGoodsPageList(params);
          return {
            data: json.records,
            page: params.current,
            success: true,
            total: json.total,
          };
        }}
        rowSelection={{
              selectedRowKeys,
              onChange: (keys) => setSelectedRowKeys(keys),
         }}
        search={false}
        toolBarRender={false}
        options={{
          search: {
            name: 'goodsName',
            placeholder: '請輸入商品名稱',
          },
        }}
      />

//css樣式
.inListContent {
  .ant-pro-table {
    display: flex;
    flex-direction: column;
  }
  .ant-pro-table-search {
    order: 2;
  }
  .ant-pro-table-extra {
    order: 1;
  }
  .ant-pro-card {
    order: 3;
  }
}
EditableProTable

這個元件的使用場景一般是在修改或新增的彈框中使用居多,不要問我怎麼知道,因為我常用~
1.trigger="onValuesChange":保證了我當前的諸如DrawerForm類似的form元件可實時拿到這個EditableProTable的值,比如下面在onfinsh中拿到的就是inboundGoodsList的值
2.recordCreatorProps:是手動填加列表的一些配置
3.editable:是編輯的一些配置

   <ProForm.Item label="" name="inboundGoodsList" initialValue={[]}      trigger="onValuesChange">
          <EditableProTable
            headerTitle="入庫商品"
            rowKey="id"
            columns={[
              {
                title: '商品',
                dataIndex: 'goodsId',
                formItemProps: { rules: [{ required: true }] },
                valueType: 'select',
                request: async () => {
                  let res = await getBasicGoodsPageList({
                    current: 1,
                    pageSize: 999,
                    projectId: initialState?.project?.projectId,
                  });
                  if (res.status == '00') {
                    return res?.records?.map((item) => {
                      return {
                        label: item.goodsName,
                        value: item.id,
                      };
                    });
                  }
                },
                fieldProps: () => {
                  return {
                    labelInValue: true,
                  };
                },
              },
              {
                title: '件重(件/kg)',
                dataIndex: 'packageUnit',
                formItemProps: { rules: [{ required: true }] },
                valueType: 'digit',
                fieldProps: { precision: 0, min: 1 },
              },
              {
                title: '重量(kg)',
                dataIndex: 'weight',
                editable: false,
                valueType: 'digit',
                renderText: (_, record, index) => {
                  if (record.quantity && record.packageUnit) {
                    return Math.round(record.quantity * record.packageUnit);
                  }
                },
              },
              { title: '生產日期', dataIndex: 'productionDate', valueType: 'date', width: 200 },
              { title: '操作', width: 50, valueType: 'option' },
            ]}
            recordCreatorProps={{
              newRecordType: 'dataSource',
              record: () => ({
                id: Date.now(),
              }),
            }}
            editable={{
              type: 'multiple',
              form: tableForm,
              editableKeys,
              onChange: setEditableRowKeys,
              actionRender: (row, _, dom) => {
                return [dom.delete];
              },
            }}
          />
        </ProForm.Item>

EditableProTable作為這個元件庫我覺得最牛逼的元件,雖然使用起來很爽,但是還是有點點坑的存在的:
聯動效果來說:當我聯動的是一個設定了不能編輯的一項,那這個聯動效果會失效,什麼意思呢,好的,那上程式碼:

{
                title: '雜費',
                dataIndex: 'poundage',
                valueType: "select",
                request: async () => {
                  let res = await getPoundageList({ projectId: initialState?.project?.projectId })
                  if (res.status == "00") {
                    return res?.data?.map(item => {
                      return {
                        label: item.name,
                        value: item.id
                      }
                    })
                  }
                },
                fieldProps: (_, { rowIndex }) => {
                  return {
                    onChange: async (value) => {
                      debugger
                      let res = await getPoundageDetail(value)
                      if (res.status == "00") {
                        let { mode, chargeDescribe } = res?.obj
                        // tableRef?.current?.setRowData(rowIndex,{
                        //   modeDescribe: mode.toString(),
                        //   chargeDescribe: chargeDescribe || ""
                        // })
                        const data = form.getFieldValue('inboundPoundageItemList')
                        data[rowIndex].modeDescribe = mode.toString()
                        data[rowIndex].chargeDescribe = chargeDescribe
                        form.setFieldsValue({
                          inboundPoundageItemList: data,
                        })
                      }
                    }
                  }
                }
              },

一開始我嘗試使用setRowData對當前這一行的其他項做聯動資料修改,但是失效的,
這裡通俗講就是:他的編輯和不能編輯是對應的兩個表單,當我編輯可編輯的這個表單,是設定不了資料到不可編輯的那個表單的,
對,那怎麼辦?
從資料來源處下手唄:form.getFieldValue得到資料修改完之後
form.setFieldsValue重新賦值給這個EditableProTable對應的表單項

ModalForm

這樣使用trigger的好處:是元件的狀態visible不需要從外部傳入了,
只要引入這個元件,這個元件內部維護這個狀態。
submitter:可以自定義這個表單的確認按鈕和取消按鈕的文字

 <ModalForm
      width="400px"
      title="匯入"
      modalProps={{
        maskClosable: false,
      }}
      trigger={<Button key="add">匯入</Button>}
      submitter={{
        searchConfig: {
          submitText: '匯入',
          resetText: '取消',
        },
      }}
      onFinish={async (values) => onSubmit()}
    >

ProList ProFormTreeSelect ProFormRadio.Group

ProFormTreeSelect、ProFormRadio.Group選取值得時候修改不了的情況在這裡出現了
加上value屬性對應修改的值

 const [libraryValue, setLibraryValue] = useState([]);
 const [areaValue, setAreaValue] = useState({});


  /* 頭部元素 */
    const ProListHeader = () => {
        return (
            <Form
                form={form}
            >
                <ProFormTreeSelect
                    label="選擇區域"
                    name="name"
                    placeholder="請選擇區域"
                    value={areaValue}
                    allowClear
                    width={330}
                    secondary
                    request={async () => {
                        let res = await areaSituation({})
                        if (res.status == "00") {
                            return res?.data
                        }
                    }}
                    fieldProps={{
                        showArrow: false,
                        filterTreeNode: true,
                        showSearch: true,
                        dropdownMatchSelectWidth: false,
                        labelInValue: true,
                        autoClearSearchValue: true,
                        // multiple: true,
                        treeNodeFilterProp: 'title',
                        fieldNames: {
                            label: 'title',
                            lvl: 'lvl',
                            value: 'nodeId',
                            children: 'childrenList',
                        },
                        onChange: (item) => {
                            setAreaValue(item)
                            initDataList({
                                limitIdList: [item?.value]
                            })
                            initTotalData({
                                limitIdList: [item?.value]
                            })
                        }
                    }}
                />
                <ProFormRadio.Group
                    width="md"
                    name="libraryValue"
                    value={libraryValue}
                    label=""
                    options={typeData}
                    onChange={(e) => {
                        setLibraryValue(e?.target?.value)
                        initDataList({ resourceType: e?.target?.value })
                    }}
                />
            </Form>
        );
    };


  const ItemColorMap = {
        0: {
            bgColor: 'rgba(0, 153, 255, 1)',
        },
        1: {
            bgColor: 'gray',
        },
        2: {
            bgColor: 'rgba(0, 153, 255, 1)',
        },
        3: {
            bgColor: 'red',
        },
    };


   <ProList
                className="operateContent"
                pagination={{
                    defaultPageSize: 8,
                    showSizeChanger: false,
                }}
                grid={{ gutter: 16, column: 1 }}
                metas={{
                    title: {},
                    subTitle: {},
                    type: {},
                    avatar: {},
                    content: {},
                    actions: {},
                }}
                rowKey="id"
                headerTitle={<ProListHeader />}
                dataSource={
                    listData.map((unit, unitIndex) => (
                        {
                            title: ``,
                            subTitle: false,
                            actions: false,
                            avatar: false,
                            content: (
                                <div key={unit?.id} >
                                    <div className='identification'><span className='lineStyle' />{unit?.floorNumber}樓</div>
                                    <div className="floorBox">
                                        {unit?.list?.map((item, index) => (
                                            <div key={item?.locationId} className="floor"  style={{ backgroundColor: ItemColorMap[item?.type]?.bgColor
                                            }}  onClick={()=>{setItemObj(item),setVisible(true)}}>
                                                <div className='actualAreaBox'>
                                                    <span>{item?.locationName}</span>
                                                    <span className='actualArea'>{item?.actualArea}m²</span>
                                                    <span>{item?.inventory}T</span>
                                                </div>
                                                <div className='actualAreaBox'>
                                                    <span>{item?.type == 1 ? "閒置" : item?.usage}</span>
                                                    {
                                                        item?.type == 2 && <span className='overDay'>即將過期</span>
                                                    }
                                                    {
                                                        item?.type == 3 && <span className='overDay'>已過期</span>
                                                    }
                                                </div>
                                            </div>
                                        ))
                                        }
                                    </div>
                                </div>
                            )
                        }
                    ))
                }
            />

最後這篇文章,如果幫助到你,或者讓你有所領悟,還希望可以不吝 點贊關注~

相關文章