Commit 7837e94687e8713d2e33a898c07fce7e83ed41bb

Authored by 陈洋
1 parent 5f82f835

feat: 添加 排序组件

1 1 import { defineConfig } from 'dumi';
2 2
3 3 export default defineConfig({
  4 + apiParser: {},
4 5 outputPath: 'docs-dist',
5 6 themeConfig: {
6 7 name: '@qx/common ',
7 8 },
  9 + resolve: {
  10 + // 配置入口文件路径,API 解析将从这里开始
  11 + entryFile: './src/index.ts',
  12 + },
8 13 });
... ...
... ... @@ -4,3 +4,4 @@ node_modules
4 4 .dumi/tmp-test
5 5 .dumi/tmp-production
6 6 .DS_Store
  7 +.ieda
... ...
... ... @@ -2,26 +2,25 @@
2 2 "name": "@qx/common",
3 3 "version": "3.0.0",
4 4 "description": "A react library developed with dumi",
  5 + "license": "MIT",
5 6 "module": "dist/index.js",
6 7 "types": "dist/index.d.ts",
  8 + "files": [
  9 + "dist"
  10 + ],
7 11 "scripts": {
8   - "start": "npm run dev",
9   - "dev": "dumi dev",
10 12 "build": "father build",
11 13 "build:watch": "father dev",
  14 + "dev": "dumi dev",
12 15 "docs:build": "dumi build",
13   - "prepare": "husky install && dumi setup",
14 16 "doctor": "father doctor",
15 17 "lint": "npm run lint:es && npm run lint:css",
16 18 "lint:css": "stylelint \"{src,test}/**/*.{css,less}\"",
17 19 "lint:es": "eslint \"{src,test}/**/*.{js,jsx,ts,tsx}\"",
18   - "prepublishOnly": "father doctor && npm run build"
  20 + "prepare": "husky install && dumi setup",
  21 + "prepublishOnly": "father doctor && npm run build",
  22 + "start": "npm run dev"
19 23 },
20   - "authors": [],
21   - "license": "MIT",
22   - "files": [
23   - "dist"
24   - ],
25 24 "commitlint": {
26 25 "extends": [
27 26 "@commitlint/config-conventional"
... ... @@ -44,17 +43,10 @@
44 43 "prettier --parser=typescript --write"
45 44 ]
46 45 },
47   - "publishConfig": {
48   - "access": "public"
49   - },
50   - "peerDependencies": {
51   - "react": ">=16.9.0",
52   - "react-dom": ">=16.9.0",
53   - "antd": ">=5.8.4"
54   - },
55 46 "devDependencies": {
56 47 "@commitlint/cli": "^17.1.2",
57 48 "@commitlint/config-conventional": "^17.1.0",
  49 + "@types/lodash-es": "^4.17.8",
58 50 "@types/react": "^18.0.0",
59 51 "@types/react-dom": "^18.0.0",
60 52 "@umijs/lint": "^4.0.0",
... ... @@ -69,5 +61,16 @@
69 61 "react": "^18.0.0",
70 62 "react-dom": "^18.0.0",
71 63 "stylelint": "^14.9.1"
72   - }
  64 + },
  65 + "peerDependencies": {
  66 + "@ant-design/icons": "^5.2.5",
  67 + "antd": ">=5.8.4",
  68 + "lodash-es": "^4.17.21",
  69 + "react": ">=16.9.0",
  70 + "react-dom": ">=16.9.0"
  71 + },
  72 + "publishConfig": {
  73 + "access": "public"
  74 + },
  75 + "authors": []
73 76 }
... ...
1   -# Foo
2   -
3   -This is an example component.
4   -
5   -```jsx
6   -import { Foo } from '@qx/common';
7   -
8   -export default () => <Foo title="Hello dumi!" />
9   -```
1   -import React, { type FC } from 'react';
2   -
3   -const Foo: FC<{ title: string }> = (props) => <h4>{props.title}</h4>;
4   -
5   -export default Foo;
1   -export { default as Foo } from './Foo';
  1 +export * from './qx-sort-condition';
... ...
  1 +import { createFromIconfontCN } from '@ant-design/icons';
  2 +
  3 +/**
  4 + * 使用:
  5 + * import QxIcon from '@/packages/qx-icon';
  6 + * <QxIcon type="xxx"/>
  7 + * 说明:
  8 + * `xxx`为图标唯一标识,`iconfont.cn`对应图标下的“复制代码”所得,建议命名为“icon-xxx_yyy”,
  9 + * “icon-”为固定前缀,xxx为模块类型,yyy为图标名。公共类的xxx命名为“comm”
  10 + * eg: <QxIcon type="icon-flow_eye"/>
  11 + *
  12 + * @type {React.FC<IconFontProps<string>>}
  13 + */
  14 +const Icon = createFromIconfontCN({
  15 + scriptUrl: [
  16 + 'https://lf1-cdn-tos.bytegoofy.com/obj/iconpark/svg_28096_13.dd706b5c23d28b13b59353264ba38e10.js', // 基础图标
  17 + ],
  18 +});
  19 +export const QxBaseIcon = (props) => {
  20 + const { type, ...rest } = props || {};
  21 +
  22 + return <Icon type={type} {...rest}></Icon>;
  23 +};
... ...
  1 +import { createFromIconfontCN } from '@ant-design/icons';
  2 +
  3 +/**
  4 + * 使用:
  5 + * import QxIcon from '@/packages/qx-icon';
  6 + * <QxIcon type="xxx"/>
  7 + * 说明:
  8 + * `xxx`为图标唯一标识,`iconfont.cn`对应图标下的“复制代码”所得,建议命名为“icon-xxx_yyy”,
  9 + * “icon-”为固定前缀,xxx为模块类型,yyy为图标名。公共类的xxx命名为“comm”
  10 + * eg: <QxIcon type="icon-flow_eye"/>
  11 + *
  12 + * @type {React.FC<IconFontProps<string>>}
  13 + */
  14 +export const QxBaseIcon = createFromIconfontCN({
  15 + scriptUrl: [
  16 + 'https://lf1-cdn-tos.bytegoofy.com/obj/iconpark/svg_28096_13.dd706b5c23d28b13b59353264ba38e10.js', // 基础图标
  17 + ],
  18 +});
... ...
  1 +### 排序规则
  2 +
  3 +```tsx
  4 +import React, { useState } from 'react';
  5 +import { QxSortCondition } from './index';
  6 +
  7 +const options2 = [
  8 + {
  9 + label: '创建时间',
  10 + value: 'created_at',
  11 + },
  12 + {
  13 + label: '记录ID',
  14 + value: 'id',
  15 + icon: '图标',
  16 + },
  17 + {
  18 + label: '标题',
  19 + value: 'wdT8w5ZlCWPhjRzBq8E',
  20 + },
  21 + {
  22 + label: '描述',
  23 + value: 'y8OOH2fDNaFQ7ixioX0',
  24 + },
  25 + {
  26 + label: '文本',
  27 + value: '2rd3ePcrGcQolUbCLWL',
  28 + },
  29 + {
  30 + label: '数值',
  31 + value: '4pQVBruE0gtnjT8eGso',
  32 + },
  33 +];
  34 +
  35 +export default () => {
  36 + const [values, setValues] = useState<
  37 + {
  38 + key: string;
  39 + asc: boolean;
  40 + }[]
  41 + >([
  42 + {
  43 + key: 'created_at',
  44 + asc: false,
  45 + },
  46 + ]);
  47 +
  48 + const props = {
  49 + onChange: (
  50 + _val: {
  51 + key: string;
  52 + asc: boolean;
  53 + }[],
  54 + ) => {
  55 + setValues(_val);
  56 + },
  57 + value: values,
  58 + optionList: options2,
  59 + };
  60 +
  61 + return <QxSortCondition {...props} />;
  62 +};
  63 +// <API id="QxSortCondition"></API>
  64 +```
... ...
  1 +import { Button, Select } from 'antd';
  2 +import { cloneDeep, findIndex, isEqual } from 'lodash-es';
  3 +import React, { useEffect, useState } from 'react';
  4 +
  5 +import { QxBaseIcon } from '../qx-base-icon/index';
  6 +
  7 +const { Option } = Select;
  8 +
  9 +type SortProps = {
  10 + key: string;
  11 + asc: boolean;
  12 +};
  13 +
  14 +interface ItemProps {
  15 + label: string;
  16 + value: string | number;
  17 + icon?: React.ReactNode;
  18 +}
  19 +
  20 +interface QxSortConditionProps {
  21 + /**
  22 + * @description 已选排序字段
  23 + * @default "-"
  24 + */
  25 + value: SortProps[];
  26 + /**
  27 + * @description 字段变化方法
  28 + * @default "-"
  29 + */
  30 + onChange: (val: SortProps[]) => void;
  31 + /**
  32 + * @description 可选字段
  33 + * @default "-"
  34 + */
  35 + optionList: ItemProps[];
  36 + sortList?: ItemProps[];
  37 + extraClassName?: string; // 定义额外 标签类名 覆盖排序原有样式
  38 +}
  39 +
  40 +const defaultSortList = [
  41 + {
  42 + label: 'A → Z',
  43 + value: 'true',
  44 + },
  45 + {
  46 + label: 'Z → A',
  47 + value: 'false',
  48 + },
  49 +];
  50 +
  51 +/**
  52 + * 排序条件设置
  53 + *
  54 + * @param props
  55 + * @constructor
  56 + */
  57 +export const QxSortCondition: React.FC<QxSortConditionProps> = (props) => {
  58 + const { value, onChange, optionList, sortList, extraClassName } = props;
  59 + const [sorts, setSorts] = useState<any[]>([]);
  60 + const [columns, setColumns] = useState<any[]>();
  61 +
  62 + useEffect(() => {
  63 + setColumns(optionList);
  64 + }, [optionList]);
  65 +
  66 + // 根据`code`获取对应列对象
  67 + const getSortItemByDataIndex = (code: string) => {
  68 + if (!columns || columns.length === 0) {
  69 + return {};
  70 + }
  71 + const index = findIndex(columns, (o: ItemProps) => {
  72 + return o.value === code;
  73 + });
  74 + return columns[index];
  75 + };
  76 +
  77 + useEffect(() => {
  78 + if (!columns) {
  79 + return;
  80 + }
  81 + if (!value || value.length === 0) {
  82 + const sortCol: any = getSortItemByDataIndex('created_at');
  83 + const item: SortProps = {
  84 + key: sortCol?.code,
  85 + asc: false,
  86 + };
  87 + setSorts([item]);
  88 + } else {
  89 + setSorts(cloneDeep(value || []));
  90 + }
  91 + }, [columns, value]);
  92 +
  93 + /**
  94 + * 通知到父组件
  95 + */
  96 + useEffect(() => {
  97 + if (!sorts || (Array.isArray(sorts) && sorts.length === 0)) {
  98 + return;
  99 + }
  100 + const isSame = isEqual(value, sorts);
  101 + if (isSame) {
  102 + return;
  103 + }
  104 +
  105 + if (!value && sorts?.length === 0) {
  106 + return;
  107 + }
  108 +
  109 + // 更新父组件`columnsUser`
  110 + onChange(sorts);
  111 + }, [sorts]);
  112 +
  113 + /**
  114 + * 获取未使用的`列`数组
  115 + */
  116 + const getUnSelCols = (cols: ItemProps[], exceptKey: string) => {
  117 + const selectedKeyArr: string[] = [];
  118 + (sorts || []).forEach((v: SortProps) => {
  119 + // 跳过排除项
  120 + if (!(exceptKey && v.key === exceptKey)) {
  121 + selectedKeyArr.push(v.key);
  122 + }
  123 + });
  124 +
  125 + const unSelCols: ItemProps[] = [];
  126 + cols.forEach((v2: ItemProps) => {
  127 + let isIn = false;
  128 + selectedKeyArr.forEach((v3) => {
  129 + if (v2.value === v3) {
  130 + isIn = true;
  131 + }
  132 + });
  133 + if (!isIn) {
  134 + unSelCols.push(v2);
  135 + }
  136 + });
  137 + return unSelCols;
  138 + };
  139 +
  140 + // 新增排序条件
  141 + const addSort = () => {
  142 + const colsTem = getUnSelCols(cloneDeep(columns || []), '') || [];
  143 + const sortsNew = cloneDeep(sorts || []);
  144 + sortsNew.push({
  145 + key: colsTem[0]?.value,
  146 + asc: true,
  147 + });
  148 + setSorts(sortsNew);
  149 + };
  150 +
  151 + // 删除排序条件
  152 + const removeSort = (index: number) => {
  153 + const orderNew = cloneDeep(sorts || []);
  154 + orderNew.splice(index, 1);
  155 + setSorts(orderNew);
  156 + };
  157 +
  158 + return (
  159 + <div
  160 + className={`${extraClassName || ' '} qx-sort-condition-container`}
  161 + style={{ width: '100%' }}
  162 + >
  163 + {(sorts || []).map((v: any, k: any) => (
  164 + <div style={{ marginBottom: '1em' }} key={v.key}>
  165 + {columns && (
  166 + <Select
  167 + defaultValue={v.key}
  168 + getPopupContainer={(triggerNode) => triggerNode}
  169 + value={v.key}
  170 + style={{ width: '54%' }}
  171 + showSearch={true}
  172 + filterOption={(input, option) =>
  173 + option?.children
  174 + ?.toString()
  175 + .toLowerCase()
  176 + .indexOf(input.toLowerCase()) !== -1
  177 + }
  178 + onChange={(val: string) => {
  179 + const item = sorts[k];
  180 + item.key = val;
  181 +
  182 + const sortsNew = cloneDeep(sorts || []);
  183 + sortsNew.splice(k, 1, item);
  184 + setSorts(sortsNew);
  185 + }}
  186 + >
  187 + {getUnSelCols(cloneDeep(columns || []), v.key).map(
  188 + (item: any) => (
  189 + <Option key={item?.value} value={item?.value}>
  190 + {item?.icon} {item?.label}
  191 + </Option>
  192 + ),
  193 + )}
  194 + </Select>
  195 + )}
  196 + &nbsp;
  197 + <Select
  198 + defaultValue={String(v.asc)}
  199 + getPopupContainer={(triggerNode) => triggerNode}
  200 + value={String(v.asc)}
  201 + style={{ minWidth: '20%' }}
  202 + onChange={(val: string) => {
  203 + const item = sorts[k];
  204 + item.asc = val === 'true';
  205 +
  206 + const sortsNew = cloneDeep(sorts || []);
  207 + sortsNew.splice(k, 1, item);
  208 + setSorts(sortsNew);
  209 + }}
  210 + >
  211 + {(sortList || defaultSortList).map((item: ItemProps) => {
  212 + return (
  213 + <Option key={item?.value} value={item.value}>
  214 + {item.label}
  215 + </Option>
  216 + );
  217 + })}
  218 + </Select>
  219 + <span
  220 + className={'qx-sort-condition-container__opt'}
  221 + style={{ margin: '-2px 0 0 .5em' }}
  222 + >
  223 + <Button
  224 + size="small"
  225 + icon={
  226 + <QxBaseIcon style={{ fontSize: 16 }} type={'qx-icon-minus'} />
  227 + }
  228 + disabled={sorts.length <= 1}
  229 + onClick={() => removeSort(k)}
  230 + />
  231 + &nbsp;
  232 + <Button
  233 + size="small"
  234 + icon={
  235 + <QxBaseIcon style={{ fontSize: 16 }} type={'qx-icon-plus'} />
  236 + }
  237 + disabled={
  238 + sorts.length >=
  239 + ((columns || []).length > 3 ? 3 : (columns || []).length)
  240 + }
  241 + onClick={addSort}
  242 + />
  243 + </span>
  244 + </div>
  245 + ))}
  246 + </div>
  247 + );
  248 +};
... ...
  1 +export const options1 = [
  2 + {
  3 + label: '记录ID',
  4 + value: 'id',
  5 + },
  6 + {
  7 + label: '标题',
  8 + value: 'wdT8w5ZlCWPhjRzBq8E',
  9 + },
  10 + {
  11 + label: '描述',
  12 + value: 'y8OOH2fDNaFQ7ixioX0',
  13 + },
  14 + {
  15 + label: '文本',
  16 + value: '2rd3ePcrGcQolUbCLWL',
  17 + },
  18 + {
  19 + label: '数值',
  20 + value: '4pQVBruE0gtnjT8eGso',
  21 + },
  22 + {
  23 + label: '创建时间',
  24 + value: 'created_at',
  25 + },
  26 +];
... ...
... ... @@ -12,5 +12,5 @@
12 12 "@qx/common /*": ["src/*", "*"]
13 13 }
14 14 },
15   - "include": [".dumi/**/*", ".dumirc.ts", "src/**/*"]
  15 + "include": [".dumi/**/*", ".dumirc.ts", "src/**/*", "typings.d.ts"]
16 16 }
... ...
  1 +declare module '*.css';
  2 +declare module '*.less';
  3 +declare module '*.png';
  4 +declare module '*.json';
  5 +declare module '*.svg' {
  6 + export function ReactComponent(
  7 + props: React.SVGProps<SVGSVGElement>,
  8 + ): React.ReactElement;
  9 + const url: string;
  10 + export default url;
  11 +}
... ...