@cainiaofe/cn-ui-m
Version:
357 lines (356 loc) • 18.4 kB
JavaScript
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 };