利用現(xiàn)有Umi搭建的后臺(tái)管理系統(tǒng)和所遇到的問題

一、用到的技術(shù)

1弛随、技術(shù)棧

react: ^16.14.0

react-dom:^17.0.0

react-quill: ^1.3.5 富文本編輯器

react-router: ^4.3.1

react-dev-inspector:^1.1.1 這個(gè)包允許用戶通過簡(jiǎn)單的點(diǎn)擊直接從瀏覽器 React 組件跳轉(zhuǎn)到本地 IDE 代碼

2、UI組件

Ant Design --------------------------用到版本: antd: 4.15.1

Ant Design Pro Components

二墨林、用法總結(jié)

1敬惦、富文本

這里有個(gè)坑谣膳,如果和自定義下拉選項(xiàng)一起使用,得下拉框渲染完之后才能渲染富文本組件,不然會(huì)報(bào)錯(cuò),加不了必填項(xiàng)

import  ReactQuill,{ Quill }  from 'react-quill';
import QuillEmoji from 'quill-emoji'
import 'quill-emoji/dist/quill-emoji.css'
import 'react-quill/dist/quill.snow.css'

Quill.register({
  'modules/emoji-toolbar': QuillEmoji.ToolbarEmoji,
  // 'modules/emoji-textarea': QuillEmoji.TextAreaEmoji,
  'modules/emoji-shortname': QuillEmoji.ShortNameEmoji
})

  const modules={
    toolbar:{
      container:[
        [{ 'size': ['small', false, 'large', 'huge'] }], // custom dropdown
        [{ 'font': [] }],
        [{ 'header': 1 }, { 'header': 2 }],        // custom button values
        ['bold', 'italic', 'underline', 'strike'],    // toggled buttons
        [{ 'align': [] }],
        [{ 'indent': '-1' }, { 'indent': '+1' }],     // outdent/indent
        [{ 'direction': 'rtl' }],             // text direction
        [{ 'script': 'sub' }, { 'script': 'super' }],   // superscript/subscript
        ['blockquote', 'code-block'],
      
        [{ 'list': 'ordered' }, { 'list': 'bullet' }],
        [{ 'color': [] }, { 'background': [] }],
        ['emoji', 'image', 'video', 'link'],
      
        ['clean']
      ],
      handlers: {
        image: ()=>imageHandler()
      }
    },
    'emoji-toolbar': true,
    // 'emoji-textarea': true,
    'emoji-shortname': true,
  }
 //自定義上傳圖片格式
 const imageHandler = () => {
    const quillEditor = ref.current?.getEditor()
    const input = document.createElement('input')
    input.setAttribute('type', 'file')
    input.setAttribute('accept', 'image/*')
    input.click()
    input.onchange = async () => {
      const file = input.files[0]
      const formData = new FormData()
      formData.append('quill-image', file)
      const code=218
      const link=await upload(file,code)//把base64格式轉(zhuǎn)換為正常url路徑格式,自定義方法
      const range = quillEditor?.getSelection()
      quillEditor.insertEmbed(range.index, 'image', link)
    }
  }

{
    onselect.length>0&&//如果和自定義下拉選項(xiàng)一起使用溉委,得下拉框渲染完之后才能渲染富文本組件鹃唯,不然會(huì)報(bào)錯(cuò)
    <div  className={styles.box}>
            <Form.Item
              label="文章詳情"
              name="articleContent"
              readonly={detailData?.id&&detailData?.edtil}
              // rules={[{ required: true, message: '請(qǐng)?jiān)O(shè)置文章詳情!' }]} 
            >
              <ReactQuill  modules={modules} ref={ref}/>
            </Form.Item>
            <div className={styles.mark}>*</div>
     </div>
 }
 

1.1、base64轉(zhuǎn)url路徑upload方法

import OSS from 'ali-oss';
import request from '@/utils/request';

const getConfig = (params = {}, options = {}) => {
  return request('/auth/goods/product/getOssConfig', {
    method: 'POST',
    data: params,
    ...options
  });
}

const getImageSize = (file) => {
  return new Promise((resolve) => {
    const reader = new FileReader()
    reader.readAsDataURL(file)
    reader.onload = (theFile) => {
      const image = new Image()
      image.src = theFile.target.result
      image.onload = function () {
        const { width, height } = this;
        resolve({ width, height })
      }
    }
  });
}

let ossConfig = null;
let codeTemp = null

const upload = async (file, code) => {
  if (code !== codeTemp) {
    ossConfig = null
  }
  codeTemp = code;
  if (!ossConfig) {
    const res = await getConfig({ code });
    ossConfig = res?.data?.records
  }
  const client = new OSS({
    region: `oss-${ossConfig.regionId}`,
    accessKeyId: ossConfig.credentials.accessKeyId,
    accessKeySecret: ossConfig.credentials.accessKeySecret,
    stsToken: ossConfig.credentials.securityToken,
    bucket: ossConfig.uploadInfo.bucket,
  })
  return new Promise((resolve) => {
    client.put(`${ossConfig.uploadInfo.dir}/${file.uid}-y_g-${file.name}`, file).then(res => {
      if (file.type.indexOf('image') !== -1) {
        getImageSize(file).then(size => {
          resolve(`${res.url}?imgHeight=${size.height}&imgWidth=${size.width}`)
        })
      } else {
        resolve(res.url);
      }
    }).catch(err => {
      ossConfig = null;
      // return upload(file, dirName)
      console.log('上傳失敯旰啊:', err)
    })
  })
}

export default upload;

2坡慌、上傳圖片右邊添加圖片描述不影響Form表單提交的方法

企業(yè)微信截圖_16451702601132.png

 const FromWrap = ({ value, onChange, content, right }) => (
    <div style={{ display: 'flex' }}>
      <div>{content(value, onChange)}</div>
      <div style={{ flex: 1, marginLeft: 10, minWidth: 180 }}>{detailData?.id&&detailData?.edtil?null:right(value)}</div>
    </div>
  )

<Form.Item
          label="封面圖片"
          name="coverPicture"
          rules={[{ required: true, message: '請(qǐng)上傳圖片!' }]}
          readonly={detailData?.id&&detailData?.edtil} 
        >
          <FromWrap
          content={(value, onChange) => <Upload multiple value={value} onChange={onChange}   maxCount={1} accept="image/*"  size={(1*1024)/2} />}
          right={(value) => {
            return (
              <dl>
                <dt>圖片要求</dt>
                <dd>1.圖片大小500kb以內(nèi)</dd>
                <dd>2.建議尺寸為 720 x 200</dd>
                <dd>3.圖片格式png/jpg/gif</dd>
              </dl>
            )
          }}
        />
        </Form.Item>

3、省市區(qū)多選級(jí)聯(lián)選擇

企業(yè)微信截圖_16451712318615.png

原級(jí)聯(lián)組件地址:https://rsuitejs.com/components/multi-cascader/

import React, { useState, useEffect, useMemo } from 'react';
import { Button, Form, Input, message, Select, Tag } from 'antd';
import MultiCascader from 'rsuite/lib/MultiCascader';
import 'rsuite/lib/MultiCascader/styles';
import './style.less'

const AddressMultiCascader = ({ value = '', onChange = () => { }, data, pId = 0, ...rest }) => {
  const [areaData, setAreaData] = useState([]);
  const [selectAreaKey, setSelectAreaKey] = useState(value);
   
  const renderMultiCascaderTag = (selectedItems) => {
    const titleArr = [];
    selectedItems.forEach(item => {
      const arr = [];
      let node = item.parent;
      arr.push(item.label)
      while (node) {
        if (node.pvalue !==-1) {
          arr.push(node.label)
        }
        node = node.parent;
      }
      titleArr.push({
        label: arr.reverse().join('-'),
        value: item.value
      })
    })

  const arrayToTree = (list, parId = 0) => {
    const len = list.length
    function loop(pid) {
      const res = [];
      for (let i = 0; i < len; i += 1) {
        const item = list[i]
        if (item&&item.pid === pid) {
          item.children = loop(item.id)
          res.push(item)
        }
      }
      return res.length ? res : null
    }
    return loop(parId)
   }
    return (
      <div style={{ display: 'flex', flexWrap: 'wrap' }}>
        {
          titleArr.map(item => (
            <Tag
              key={item.value}
              closable
              style={{ marginBottom: 10 }}
              onClose={() => {
                setSelectAreaKey(selectAreaKey.filter(it => it !== item.value))
                onChange(selectAreaKey.filter(it => it !== item.value))
              }}
            >
              {item.label}
            </Tag>
          ))
        }
      </div>
    );
  }

  useEffect(() => {
    const arr = arrayToTree(data || window.yeahgo_area || [], pId)
    let str = JSON.stringify(arr)
    str = str.replace(/name/g, 'label').replace(/id/g, 'value')
    setAreaData(JSON.parse(str))
  }, [data])

  useEffect(() => {
    setSelectAreaKey(value)
  }, [value])
  return (
    <MultiCascader
      value={selectAreaKey}
      data={areaData}
      style={{ width: '100%' }}
      placeholder="請(qǐng)選擇"
      renderValue={(a, b) => renderMultiCascaderTag(b)}
      locale={{ searchPlaceholder: '輸入省市區(qū)名稱' }}
      onChange={(v) => { onChange(v); setSelectAreaKey(v) }}
      {...rest}
    />
  )
}

export default AddressMultiCascader;

import AddressMultiCascader from '@/components/address-multi-cascader'

const [selectKeys, setSelectKeys] = useState([]);

   const getAreaData = (v) => {
      const arr = [];
      v?.forEach?.(item => {
        let deep = 0;
        let node = window.yeahgo_area.find(it => it.id === item);
        const nodeIds = [node.id];
        const nodeNames = [node.name]
        while (node.pid) {
          deep += 1;
          node = window.yeahgo_area.find(it => it.id === node.pid);
          nodeIds.push(node.id);
          nodeNames.push(node.name);
        }
        arr.push({
          provinceId: nodeIds[deep],
          cityId: deep > 0 ? nodeIds[deep - 1] : 0,
          districtId: deep > 1 ? nodeIds[deep - 2] : 0,
          areaName: nodeNames.reverse().join('|')
        })
      })
    
      return arr;
    }
    const getAreaDatas = (v) => {
      const arr = [];
      const brr = []
      v?.forEach?.(item => {
        let deep = 0;
        let node = window.yeahgo_area.find(it => it.id === item);
        const nodeIds = [node.id];
        const nodeNames = [node.name]
        if(node.children){
          const toTreeData = (data) => { 
            data?.forEach(item => {
              if(item.deep == 3){
                brr.push(item.id)
              }
                toTreeData(item.children)
            })  
          }
          toTreeData(node?.children)
        }
        while (node.pid) {//找父級(jí)
          deep += 1;
          node = window.yeahgo_area.find(it => it.id === node.pid);
          nodeIds.push(node.id);
          nodeNames.push(node.name);
        }
        arr.push({
          provinceId: nodeIds[deep],
          cityId: deep > 0 ? nodeIds[deep - 1] : 0,
          districtId: deep > 1 ? nodeIds[deep - 2] : 0,
          areaName: nodeNames.reverse().join('|')
        })
      })
    if(brr.length){
      return getAreaData(brr)
    }else{
      return arr;
    }

    }

<AddressMultiCascader
     value={selectKeys}
     placeholder="添加地區(qū)"
     renderValue={() => <span style={{ color: '#8e8e93' }}>添加地區(qū)</span>}
     renderExtraFooter={() =>
     <div style={{ padding: 10, textAlign: 'right' }}>
       <Button type="primary" onClick={() => { setArea() }}>確定</Button>
     </div>}
     onChange={setSelectKeys}
     disabledItemValues={disabledItemValues}
     onClose={() => { setSelectKeys([]) }}
/>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末藻三,一起剝皮案震驚了整個(gè)濱河市洪橘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌棵帽,老刑警劉巖熄求,帶你破解...
    沈念sama閱讀 218,546評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異逗概,居然都是意外死亡弟晚,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門逾苫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來卿城,“玉大人,你說我怎么就攤上這事铅搓≡逖” “怎么了?”我有些...
    開封第一講書人閱讀 164,911評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵狸吞,是天一觀的道長(zhǎng)勉耀。 經(jīng)常有香客問我,道長(zhǎng)蹋偏,這世上最難降的妖魔是什么便斥? 我笑而不...
    開封第一講書人閱讀 58,737評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮威始,結(jié)果婚禮上枢纠,老公的妹妹穿的比我還像新娘。我一直安慰自己黎棠,他們只是感情好晋渺,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著脓斩,像睡著了一般木西。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上随静,一...
    開封第一講書人閱讀 51,598評(píng)論 1 305
  • 那天八千,我揣著相機(jī)與錄音吗讶,去河邊找鬼。 笑死恋捆,一個(gè)胖子當(dāng)著我的面吹牛照皆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播沸停,決...
    沈念sama閱讀 40,338評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼膜毁,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了愤钾?” 一聲冷哼從身側(cè)響起爽茴,我...
    開封第一講書人閱讀 39,249評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎绰垂,沒想到半個(gè)月后室奏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,696評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡劲装,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評(píng)論 3 336
  • 正文 我和宋清朗相戀三年胧沫,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片占业。...
    茶點(diǎn)故事閱讀 40,013評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡绒怨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出谦疾,到底是詐尸還是另有隱情南蹂,我是刑警寧澤,帶...
    沈念sama閱讀 35,731評(píng)論 5 346
  • 正文 年R本政府宣布念恍,位于F島的核電站六剥,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏峰伙。R本人自食惡果不足惜疗疟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瞳氓。 院中可真熱鬧策彤,春花似錦、人聲如沸匣摘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽音榜。三九已至庞瘸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間囊咏,已是汗流浹背恕洲。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留梅割,地道東北人霜第。 一個(gè)月前我還...
    沈念sama閱讀 48,203評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像户辞,于是被迫代替她去往敵國(guó)和親泌类。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容