UNPKG

@cainiaofe/cn-ui-m

Version:
357 lines (356 loc) 18.4 kB
import { __assign, __awaiter, __generator, __rest, __spreadArray } from "tslib"; import $i18n from "../../locales/i18n"; import * as React from 'react'; import { forwardRef, useEffect, useState, } from 'react'; import classNames from 'classnames'; import { CnSearch } from "../cn-search"; import { SelectDrawer } from "../select-drawer"; import isFunction from 'lodash/isFunction'; import { isUndef, isValidArray } from "../../utils/func"; import { find, getDataByValues, getFirstValue, getFullPathByValue, getTreeDepth, } from "../../utils/tree"; import { CnTab, CnTabItem } from '../cn-tab'; import { useRequest } from 'ahooks'; import { handleRequestService } from './service'; import { CascaderItemList } from './cascader-item-list'; import { cascaderFilter } from './utils'; import { CnList, CnListItem } from "../cn-list"; import { CnReadOnly } from "../cn-read-only"; var getInitValue = function (props) { var _a = props.dataSource, dataSource = _a === void 0 ? [] : _a, defaultValue = props.defaultValue, value = props.value; var ret = []; if ('value' in props) { if (isValidArray(value)) { // 为了和PC端保持一致,PC端在单选传入了数组的情况下,仅取第一个值 var actualValue = value[0]; ret = getFullPathByValue(dataSource, actualValue); } else if (!Array.isArray(value) && value) { ret = getFullPathByValue(dataSource, value); } } else if (!isUndef(defaultValue)) { if (isValidArray(defaultValue)) { ret = defaultValue; } else if (!Array.isArray(defaultValue) && defaultValue) { ret = getFullPathByValue(dataSource, defaultValue); } } return ret; }; var getInitInternalValue = function (props) { var r = getInitValue(props); var isNormalMode = props.mode !== 'tree'; if (r.length === 0) { if (!isNormalMode) { return getFirstValue(props.dataSource); } else { return []; } } else { return r; } }; // eslint-disable-next-line var defaultRenderContent = function (values, separator) { if (values === void 0) { values = []; } return Array.isArray(values) ? values.map(function (val) { return val.label; }).join(separator) : ''; }; var CascaderSelect = function (props, ref) { var _a = props.prefix, prefix = _a === void 0 ? 'cn-ui-m-' : _a, _b = props.mode, mode = _b === void 0 ? 'cascade' : _b, _c = props.dataSource, propDataSource = _c === void 0 ? [] : _c, _d = props.requestConfig, requestConfig = _d === void 0 ? {} : _d, enableRemoteLazyLoad = props.enableRemoteLazyLoad, _e = props.separator, separator = _e === void 0 ? '/' : _e, value = props.value, defaultValue = props.defaultValue, className = props.className, hasClear = props.hasClear, _f = props.useSimpleValue, useSimpleValue = _f === void 0 ? true : _f, onChange = props.onChange, _g = props.rootTipText, rootTipText = _g === void 0 ? $i18n.get({ id: 'PleaseSelect', dm: '请选择' }) : _g, propLoadData = props.loadData, isPreview = props.isPreview, readOnly = props.readOnly, renderPreview = props.renderPreview, _h = props.size, size = _h === void 0 ? 'medium' : _h, drawerClassName = props.drawerClassName, previewClassNameProp = props.previewClassName, previewStyle = props.previewStyle, showSearch = props.showSearch, filterLocal = props.filterLocal, _j = props.onSelect, onSelect = _j === void 0 ? function () { } : _j, _k = props.onClear, onClear = _k === void 0 ? function () { } : _k, onSearch = props.onSearch, onCancel = props.onCancel, onVisibleChange = props.onVisibleChange, _l = props.renderContent, renderContent = _l === void 0 ? defaultRenderContent : _l, _m = props.changeOnSelect, changeOnSelect = _m === void 0 ? false : _m, others = __rest(props, ["prefix", "mode", "dataSource", "requestConfig", "enableRemoteLazyLoad", "separator", "value", "defaultValue", "className", "hasClear", "useSimpleValue", "onChange", "rootTipText", "loadData", "isPreview", "readOnly", "renderPreview", "size", "drawerClassName", "previewClassName", "previewStyle", "showSearch", "filterLocal", "onSelect", "onClear", "onSearch", "onCancel", "onVisibleChange", "renderContent", "changeOnSelect"]); var clsPrefix = "".concat(prefix, "cascader"); var isControlled = 'value' in props; var isNormalMode = true; var previewClassName = classNames(previewClassNameProp, "".concat(prefix, "select-preview--").concat(size)); // for field show var _o = useState(getInitValue(props)), curVal = _o[0], setVal = _o[1]; // cur selected value, for internal show var _p = useState(getInitInternalValue(props)), internalValue = _p[0], setInternalVal = _p[1]; var _q = useState(null), loadingNode = _q[0], setLoadingNode = _q[1]; var _r = useState(false), drawerVisible = _r[0], setDrawerVisible = _r[1]; var _s = useState(0), tabActiveIndex = _s[0], setTabActiveIndex = _s[1]; var _t = useState(''), searchValue = _t[0], setSearchValue = _t[1]; // 请求服务封装 var requestService = handleRequestService(requestConfig); var isRemoteDataSource = React.useMemo(function () { return !!((requestConfig === null || requestConfig === void 0 ? void 0 : requestConfig.url) || (requestConfig === null || requestConfig === void 0 ? void 0 : requestConfig.service)); }, [requestConfig]); var _u = useRequest(requestService, __assign({ ready: isRemoteDataSource }, requestConfig)), runAsync = _u.runAsync, run = _u.run, _v = _u.data, data = _v === void 0 ? [] : _v; var dataSource = isRemoteDataSource ? data : propDataSource; var loadData = isRemoteDataSource && enableRemoteLazyLoad ? function (item) { var _a; return runAsync((_a = {}, _a[requestConfig.remoteLazyLoadKey || 'value'] = item.value, _a)).catch(function (err) { console.log(err); }); } : propLoadData; var innerSelectedPath = getDataByValues(dataSource, internalValue); // 通过索引缓存每个选项的位置 var itemCache = React.useMemo(function () { var res = {}; if (!showSearch) return res; var loop = function (currentData, currentPrefix) { if (currentPrefix === void 0) { currentPrefix = '0'; } return currentData.forEach(function (item, index) { var children = item.children; var pos = "".concat(currentPrefix, "-").concat(index); res[pos] = __assign(__assign({}, item), { pos: pos }); if (children && children.length) { loop(children, pos); } }); }; loop(dataSource); return res; }, [dataSource, showSearch, filterLocal]); // 处理确定按钮点击 var handleOk = function () { var setValueCallback = function () { if (!isControlled) { setVal(internalValue); } setSearchValue(''); if (onChange) { var selectedPath = getDataByValues(dataSource, internalValue); var lastData = selectedPath[selectedPath.length - 1]; onChange(useSimpleValue ? lastData.value || '' : internalValue, lastData, { selectedPath: selectedPath, }); } }; if (isNormalMode) { if (isValidArray(internalValue)) { var lastVal_1 = internalValue[internalValue.length - 1]; var n = find(dataSource, function (node) { return node.value === lastVal_1; }); if (changeOnSelect === true) { setValueCallback(); return; } if (n && n.children && n.children.length) { handleCancel('invalid-selection'); return; } } else { handleCancel('invalid-selection'); return; } } setValueCallback(); }; // 处理取消 var handleCancel = function (reason) { var nextInternalVal = curVal; // 如果是树形模式, 如果当前值为空,则重置为初始值 if (mode === 'tree' && Array.isArray(curVal) && curVal.length === 0) { var newTreeInternalVal = getInitInternalValue(__assign(__assign({}, props), { dataSource: dataSource })); nextInternalVal = newTreeInternalVal; } setInternalVal(nextInternalVal); setSearchValue(''); if (isFunction(onCancel)) { onCancel(reason); } }; // 处理复杂模式,列表点击 var handleListClick = function (item, dpt) { return __awaiter(void 0, void 0, void 0, function () { var val, newInternalVal; return __generator(this, function (_a) { switch (_a.label) { case 0: val = item.value; newInternalVal = __spreadArray(__spreadArray([], internalValue.slice(0, dpt), true), [val], false); if (loadData && internalValue.indexOf(val) > -1) { newInternalVal = internalValue.slice(0, internalValue.indexOf(val) + 1); } setInternalVal(newInternalVal); if (!(loadData && !item.isLeaf)) return [3 /*break*/, 2]; setLoadingNode(val); return [4 /*yield*/, loadData(item)]; case 1: _a.sent(); setLoadingNode(null); _a.label = 2; case 2: if (onSelect) { onSelect(val, item); } return [2 /*return*/]; } }); }); }; var handleClear = function () { if (!isControlled) { setVal([]); // 如果是树形模式, 则重置为默认展开数据,否则重置为[] if (mode === 'tree') { var newTreeInternalVal = getInitInternalValue(__assign(__assign({}, props), { dataSource: dataSource })); setInternalVal(newTreeInternalVal); } else { setInternalVal([]); } } if (onClear) { onClear(); } setSearchValue(''); if (onChange) { onChange(useSimpleValue ? '' : [], {}, { selectedPath: [], }); } }; var handleSearch = function (v) { var _a; setSearchValue(v); // 本地搜索不做处理 if (filterLocal) return; // 如果配置了onSearch,则执行onSearch if (isFunction(onSearch)) { onSearch(v); return; } // 如果配置了远程数据源,则调用一次run方法,传入搜索的关键词作为参数 if (isRemoteDataSource) { run((_a = {}, _a[requestConfig.searchKey || 'key'] = v, _a)); } }; var handleVisibleChange = function (visible, reason) { setDrawerVisible(visible); if (isFunction(onVisibleChange)) { onVisibleChange(visible, reason); } }; useEffect(function () { setVal(getInitValue(__assign(__assign({}, props), { dataSource: dataSource }))); setInternalVal(getInitInternalValue(__assign(__assign({}, props), { dataSource: dataSource }))); }, [value]); useEffect(function () { if (!drawerVisible && (dataSource === null || dataSource === void 0 ? void 0 : dataSource.length)) { setVal(getInitValue(__assign(__assign({}, props), { dataSource: dataSource }))); setInternalVal(getInitInternalValue(__assign(__assign({}, props), { dataSource: dataSource }))); } }, [dataSource]); var levels = React.useMemo(function () { var ret = []; var currentOptions = dataSource; var reachedEnd = false; if (isValidArray(internalValue)) { var _loop_1 = function (v) { var target = currentOptions.find(function (option) { return option.value === v; }); ret.push({ selected: target, options: currentOptions, }); if (!target || !target.children || !target.children.length) { reachedEnd = true; return "break"; } currentOptions = target.children; }; for (var _i = 0, internalValue_1 = internalValue; _i < internalValue_1.length; _i++) { var v = internalValue_1[_i]; var state_1 = _loop_1(v); if (state_1 === "break") break; } } if (!reachedEnd) { ret.push({ selected: undefined, options: currentOptions, }); } return ret; }, [internalValue, dataSource]); useEffect(function () { setTabActiveIndex(levels.length - 1); }, [internalValue, levels.length]); var filteredDatasource = React.useMemo(function () { if (showSearch) { return cascaderFilter(itemCache, searchValue, !!filterLocal); } return []; }, [itemCache, showSearch, filterLocal, searchValue]); if (isPreview || readOnly) { if (isFunction(renderPreview)) { return (React.createElement("div", { className: previewClassName, style: previewStyle }, renderPreview(innerSelectedPath))); } return (React.createElement(CnReadOnly, { value: innerSelectedPath.map(function (node) { return node.label; }).join(separator) })); } var depth = getTreeDepth(dataSource); var search = showSearch ? (React.createElement("div", { className: "".concat(prefix, "cascader-search-wrapper"), style: { width: '100%', height: '100rpx' } }, React.createElement(CnSearch, { value: searchValue, hasClear: true, onChange: handleSearch, onClear: function () { handleSearch(''); } }))) : null; var content; if (showSearch && searchValue) { content = (React.createElement("div", { className: "".concat(clsPrefix, "-search-body") }, filteredDatasource.length === 0 ? (React.createElement("div", { className: "".concat(clsPrefix, "-not-found") }, $i18n.get({ id: 'NoData', dm: '暂无数据' }))) : (React.createElement(CnList, null, filteredDatasource.map(function (i) { var _a; var optionChecked = internalValue.length === i.length && internalValue.every(function (v, index) { return v === i[index].value; }); var optionValue = i.map(function (v) { return v.value; }); return (React.createElement(CnListItem, { className: classNames((_a = {}, _a["".concat(clsPrefix, "-search-option--selected")] = optionChecked, _a)), key: optionValue.join('/'), title: i.map(function (v) { return v.label; }).join('/'), onClick: function () { setInternalVal(optionValue); }, disabled: i.some(function (v) { return v.disabled; }) })); }))))); } else { content = mode === 'tree' ? (React.createElement("div", { className: "".concat(clsPrefix, "-tree") }, levels.map(function (level, index) { var val = level.selected ? level.selected.value : null; var columnWidth = "".concat(level.options.length === 0 ? 100 : 100 / depth, "%"); var columnStyle = { width: columnWidth }; // 只有2列时,第一列为33.3,第二列为66.7 if (depth === 2 && index === 0) { columnStyle.width = '33.3%'; } if (depth === 2 && index === 1) { columnStyle.width = '66.7%'; columnStyle.backgroundColor = '#ffffff'; } if (level.options.length === 0) { columnStyle.backgroundColor = 'transparent'; } return (React.createElement("div", { className: "".concat(clsPrefix, "-column"), style: columnStyle, key: index }, React.createElement(CascaderItemList, { options: level.options, loadData: loadData, clsPrefix: clsPrefix, val: val, loadingNode: loadingNode, onClick: function (item) { handleListClick(item, index); }, mode: mode }))); }))) : (React.createElement(CnTab, { className: "".concat(clsPrefix, "-nav"), onChange: function (key) { setTabActiveIndex(key); }, activeKey: tabActiveIndex, contentClassName: "".concat(clsPrefix, "-content"), tabAlign: "left" }, levels.map(function (level, index) { var selected = level.selected; var val = level.selected ? level.selected.value : null; return (React.createElement(CnTabItem, { key: index, title: selected ? selected.label : rootTipText }, React.createElement(CascaderItemList, { options: level.options, loadData: loadData, clsPrefix: clsPrefix, val: val, loadingNode: loadingNode, onClick: function (item) { handleListClick(item, index); }, mode: mode }))); }))); } return (React.createElement(SelectDrawer, __assign({}, others, { ref: ref, hasClear: hasClear, size: size, className: classNames(clsPrefix, className), content: renderContent(getDataByValues(dataSource, curVal), separator), drawerClassName: classNames("".concat(clsPrefix, "-container"), drawerClassName), onVisibleChange: handleVisibleChange, onOk: handleOk, onCancel: handleCancel, onClear: handleClear, buttonPosition: "bottom" }), search, content)); }; var CnCascaderSelect = forwardRef(CascaderSelect); CnCascaderSelect.displayName = 'CnCascaderSelect'; CnCascaderSelect.defaultProps = { separator: '/', mode: 'cascade', requestConfig: {}, size: 'medium', useSimpleValue: true, filterLocal: true, }; export { CnCascaderSelect };