Showing
13 changed files
with
419 additions
and
34 deletions
... | ... | @@ -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 | } | ... | ... |
src/Foo/index.md
deleted
100644 → 0
src/Foo/index.tsx
deleted
100644 → 0
src/qx-base-icon/index.js
0 → 100644
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 | +}; | ... | ... |
src/qx-base-icon/index.tsx
0 → 100644
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 | +}); | ... | ... |
src/qx-sort-condition/index.md
0 → 100644
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 | +``` | ... | ... |
src/qx-sort-condition/index.tsx
0 → 100644
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 | + | |
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 | + | |
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 | +}; | ... | ... |
src/qx-sort-condition/mock.ts
0 → 100644
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 | +]; | ... | ... |
typings.d.ts
0 → 100644
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 | +} | ... | ... |