|
|
@@ -1,10 +1,30 @@
|
|
|
-import { Cascader } from 'antd'
|
|
|
-import React, { forwardRef, useEffect } from 'react'
|
|
|
+import { getAuthCache } from '@/utils/auth'
|
|
|
+import { CASCADER_HISTORY_KEY } from '@/utils/cache/cacheEnum'
|
|
|
+import { Cascader, Divider, Tag } from 'antd'
|
|
|
+import React, { forwardRef, useEffect, useState, useLayoutEffect } from 'react'
|
|
|
import { connect } from 'umi'
|
|
|
import styles from './index.less'
|
|
|
+import { serializeRecord } from './util'
|
|
|
|
|
|
const CLazyCascader = props => {
|
|
|
- const { refinstance, options, dispatch, onChange, changeOnSelect = false, ...resetProps } = props
|
|
|
+ const {
|
|
|
+ refinstance,
|
|
|
+ options,
|
|
|
+ dispatch,
|
|
|
+ onChange,
|
|
|
+ changeOnSelect = false,
|
|
|
+ popupVisible = false,
|
|
|
+ showFooter = false,
|
|
|
+ defaultValue,
|
|
|
+ onBlur,
|
|
|
+ ...resetProps
|
|
|
+ } = props
|
|
|
+ const [visible, setVisible] = useState(popupVisible)
|
|
|
+
|
|
|
+ const [historyRecord, setHistoryRecord] = useState(
|
|
|
+ showFooter ? getAuthCache(CASCADER_HISTORY_KEY) || [] : []
|
|
|
+ )
|
|
|
+ const [cValue, setValue] = useState(defaultValue)
|
|
|
useEffect(() => {
|
|
|
if (!options.length) {
|
|
|
dispatch({
|
|
|
@@ -18,9 +38,23 @@ const CLazyCascader = props => {
|
|
|
onChange(value, local)
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
const handleOnChange = (value, selectedOptions) => {
|
|
|
+ setValue(value)
|
|
|
+ const len = value?.length
|
|
|
+ if (len === 3) {
|
|
|
+ const { name, id } = selectedOptions[2]
|
|
|
+ const htr = serializeRecord(
|
|
|
+ name,
|
|
|
+ id,
|
|
|
+ selectedOptions.map(item => ({ id: item.id, name: item.name }))
|
|
|
+ )
|
|
|
+ if (htr?.length) {
|
|
|
+ setHistoryRecord(htr)
|
|
|
+ }
|
|
|
+ }
|
|
|
const local = selectedOptions.reduce((prev, curr) => {
|
|
|
- return `${prev}${prev ? ',' : ''}${curr.name}`
|
|
|
+ return `${prev}${prev ? ' / ' : ''}${curr.name}`
|
|
|
}, '')
|
|
|
triggerChange(value, local)
|
|
|
}
|
|
|
@@ -36,17 +70,92 @@ const CLazyCascader = props => {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
+ const handleTagClick = (values, selectedOptions) => {
|
|
|
+ // 加载可能dva没有缓存的地区
|
|
|
+ dispatch({
|
|
|
+ type: 'district/fetchWithValues',
|
|
|
+ payload: values
|
|
|
+ })
|
|
|
+ // 触发级联的onChange回调
|
|
|
+ handleOnChange(values, selectedOptions)
|
|
|
+ // 手动将pop浮层隐藏
|
|
|
+ setVisible(false)
|
|
|
+ }
|
|
|
+
|
|
|
+ useLayoutEffect(() => {
|
|
|
+ document.querySelector('.Cascader-Body')?.addEventListener(
|
|
|
+ 'DOMSubtreeModified',
|
|
|
+ () => {
|
|
|
+ let width = 0
|
|
|
+ const menus = document.getElementsByClassName('ant-cascader-menu')
|
|
|
+ // eslint-disable-next-line no-plusplus
|
|
|
+ for (let i = 0; i < menus.length; i++) {
|
|
|
+ const element = menus[i]
|
|
|
+ if (element.clientWidth) {
|
|
|
+ width += element.clientWidth
|
|
|
+ }
|
|
|
+ }
|
|
|
+ const extraEl = document.getElementById('h-address')
|
|
|
+ if (extraEl) {
|
|
|
+ extraEl.style.width = `${width === 0 ? 156 : width}px`
|
|
|
+ }
|
|
|
+ },
|
|
|
+ false
|
|
|
+ )
|
|
|
+ return () => {
|
|
|
+ document.querySelector('.Cascader-Body')?.removeEventListener('DOMSubtreeModified', () => {})
|
|
|
+ }
|
|
|
+ }, [])
|
|
|
+ const dropdownRender = menu => (
|
|
|
+ <>
|
|
|
+ <div id="casaContent">{menu}</div>
|
|
|
+ {showFooter && historyRecord.length ? (
|
|
|
+ <>
|
|
|
+ <Divider style={{ margin: 0 }} />
|
|
|
+ <div id="h-address" className="p-3" style={{ width: 'auto' }}>
|
|
|
+ <div className="font-semibold">最近使用</div>
|
|
|
+ <div className="inline-flex flex-wrap">
|
|
|
+ {historyRecord?.map((item, idx) => (
|
|
|
+ <div
|
|
|
+ key={item.key + idx}
|
|
|
+ className="mt-1 cursor-pointer"
|
|
|
+ onClick={() =>
|
|
|
+ handleTagClick(
|
|
|
+ item.value.map(v => v.id),
|
|
|
+ item.value
|
|
|
+ )
|
|
|
+ }>
|
|
|
+ <Tag>{item.name}</Tag>
|
|
|
+ </div>
|
|
|
+ ))}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </>
|
|
|
+ ) : null}
|
|
|
+ </>
|
|
|
+ )
|
|
|
+
|
|
|
+ const handleOnPopupVisibleChange = e => {
|
|
|
+ setVisible(e)
|
|
|
+ if (!e && cValue === defaultValue) {
|
|
|
+ triggerChange(null)
|
|
|
+ }
|
|
|
+ }
|
|
|
return (
|
|
|
- <div className={styles.cascader}>
|
|
|
+ <div className={[styles.cascader, 'Cascader-Body'].join(' ')}>
|
|
|
<Cascader
|
|
|
+ value={cValue}
|
|
|
getPopupContainer={triggerNode => triggerNode.parentNode}
|
|
|
+ popupVisible={visible}
|
|
|
placeholder="省/市/区"
|
|
|
options={options}
|
|
|
ref={refinstance}
|
|
|
+ dropdownRender={dropdownRender}
|
|
|
onChange={handleOnChange}
|
|
|
loadData={loadData}
|
|
|
fieldNames={{ label: 'name', value: 'id' }}
|
|
|
changeOnSelect={changeOnSelect}
|
|
|
+ onPopupVisibleChange={handleOnPopupVisibleChange}
|
|
|
{...resetProps}
|
|
|
/>
|
|
|
</div>
|