【Taro】小程序picker動態獲取數據

語言: CN / TW / HK

highlight: arta theme: channing-cyan


在小程序中,picker選擇器提供了幾種基礎的數據格式,如時間,日期,地區等,已經基本滿足絕大多數需求了。

但面對腦洞打開的客户和產品經理,官方也留給了我們個性化的空間,在picker屬性中,將值設定為multiSelector,再傳入一個長度不限二維數組數據,就實現了一個個性化的多項選擇器。

數據量不大的時候,我們可以直接要求後端傳一個長度為3或者更長的二維數組,塞入到range中。但大多聯動選擇器需求都是產品分類,自定義的地區數據等,此類的數據量都非常大,而且是越來越大,一次將數據全部請求過來,可能就不太合適了。

比如一個產品分類數據有一級分類,二級,還不是特別多,但裏面還有三級,四級分類,這數據量就成集合增加,再加上攜帶的相關數據,可能一個接口就輕則100多kb,重則500+kb,這時候一級一級的獲取就顯得很有必要了。

一、先看邏輯

邏輯大概就是以下這樣,黃色方塊上為賦值,下為選擇。

動態picker.jpg

一般進入頁面時會有2種狀態,picker有值或空值,也就是新建表單的時候是空值,修改的時候是有值並需要回顯。

展示

數據接口getRangeData({parentCode:''}: 傳入code獲得下級數據列表,不傳返回頂級數據列表

  • 不管怎麼樣,第一步都要通過後端提供的接口(getRangeData({parentCode:''}))獲取第一列數據c1
  • 如果是新建,直接通過c1的第一個值的code,即將c1[0].code傳入接口獲取二級數據c2,獲取第三列數據也是同樣操作。
  • 如果是修改,這傳進來一個這樣的值,分別對應三列選中的值,將value的第一個值的code傳入接口,即通過selected[0].code獲取第二列數據,第三列也同樣操作。 js const selected = [ {name: 'xxx',code: 0}, {name: 'xxx',code: 01}, {name: 'xxx',code: 012} ]
  • 獲取的數據都為數組,將獲取到的數據組合為一個二維數組並進行賦值,即setRange([c1,c2,c3]),這時候picker的基本功能已經算完成了,可以進行展示了。

操作

能展示的話,用户可以點擊操作了,功能也就完成一半了。當滾動每一列的時候,還需要繼續獲取數據,動態修改下一列的數據,這裏就更簡單的了。

image.png * 通過onColumnChange方法,獲取當前滾動的列(column)和滾動到的索引值(value),則可以通過range[column][value].code獲取並修改下一列的值。 * 點擊確定的時候,出發onChange方法,得到當前三列選中的索引值(value),通過這個索引和range數據組合[range[0][value[0]],range[1][value[1]],range[2][value[2]]],就得到了選中的值。

邏輯整體上就這麼多,最蛋疼的就是各種組合,比較彎彎繞。

二、上代碼

view部分

當值為空的時候,展示請選擇,有值的時候展示選中的值 ```tsx onChange(e)} onColumnChange={e => colChange(e)}

{selected ? ( {selected.map(i => i.name).join()} ) : ( 請選擇 )} ```

js部分

在這裏使用的是taro-react框架來實現這個方法,我用vue也同樣實現了一次,代碼結構基本上大差不差。

初始化數據

```js const pickerValue = useRef([0, 0, 0]); // 選擇器的索引值 const [range, setRange] = useState>([]); // 選擇器的值 const [selected, setSelected] = useState>([]); // 選中的值

useEffect(() => { const init = async () => { const p = await getRangeData({}); if (typeof params.selected === "undefined") { // 當值為空的時候,通過分類第一個值獲取下一列的值 const pp = await getRangeData({parentCode: p.data[0].code}); const ppp = await getRangeData({parentCode: pp.data[0].code}); setRange([p.data, pp.data, ppp.data]); } else { // 當有值的時候,通過選中的值獲取下一列的值 const s = params.selected const pp = await getRangeData({parentCode: s[0].code}); const ppp = await getAddrList({parentCode: c[1].code}); setRange([p.data, pp.data, ppp.data]); setSelected(s); } }; init(); }, []); ```

列選擇

這時候,picker的默認值就成功插進去了,下一步就是切換的時候再動態獲取值 js const colChange = e => { const { column, value } = e.detail; pickerValue.current[column] = value; if (column === 0) { // 滑動列一 let c0 = range[0][value]; // 獲取列一選中的值 const p = await getRangeData({ parentCode: c0.code }); setRange(v => [v[0], p.data, v[2]]); } else if (column === 1) { let c1 = range[1][value]; // 同上 const p = await getRangeData({ parentCode: c1.code }); setRange(v => [v[0], v[1], p.data]); } }; 當pciker切換的時候,獲取滾動的列和位置,再通過索引得到當前列選擇的值獲取下列值

這裏有個點需要注意,我們的range數據是一個數組,屬於引用類型,如果使用下面這種方法修改值 js setRange(v => { v[1] = p.data return v }); 可以成功修改,但無法觸發頁面刷新,即第二列的值不會變,必須使用深拷貝的方法來觸發頁面刷新

確認選擇

點擊確定的是,可以得到三列的索引值,並與現有range數據進行一一對應,就得到了3個選擇的值,講選中的值篩入selected中,即完成了所有功能 js const onChange = e => { let v = e.detail.value; let s0 = range[0][v[0]]; let s1 = range[1][v[1]]; let s2 = range[2][v[2]]; setSelected([ {name: s0.name, code: s0.code} {name: s1.name, code: s1.code} {name: s2.name, code: s2.code} ]); };

總結

功能比較簡單,但第一次寫的話有不少坑,半天才寫好,又優化了半天,在這個小功能上花費了一天時間,後面其他項目有這種動態加載列表的需要,就可以用這一套方法了。