Showing
13 changed files
with
419 additions
and
34 deletions
| 1 | import { defineConfig } from 'dumi'; | 1 | import { defineConfig } from 'dumi'; |
| 2 | 2 | ||
| 3 | export default defineConfig({ | 3 | export default defineConfig({ |
| 4 | + apiParser: {}, | ||
| 4 | outputPath: 'docs-dist', | 5 | outputPath: 'docs-dist', |
| 5 | themeConfig: { | 6 | themeConfig: { |
| 6 | name: '@qx/common ', | 7 | name: '@qx/common ', |
| 7 | }, | 8 | }, |
| 9 | + resolve: { | ||
| 10 | + // 配置入口文件路径,API 解析将从这里开始 | ||
| 11 | + entryFile: './src/index.ts', | ||
| 12 | + }, | ||
| 8 | }); | 13 | }); |
| @@ -2,26 +2,25 @@ | @@ -2,26 +2,25 @@ | ||
| 2 | "name": "@qx/common", | 2 | "name": "@qx/common", |
| 3 | "version": "3.0.0", | 3 | "version": "3.0.0", |
| 4 | "description": "A react library developed with dumi", | 4 | "description": "A react library developed with dumi", |
| 5 | + "license": "MIT", | ||
| 5 | "module": "dist/index.js", | 6 | "module": "dist/index.js", |
| 6 | "types": "dist/index.d.ts", | 7 | "types": "dist/index.d.ts", |
| 8 | + "files": [ | ||
| 9 | + "dist" | ||
| 10 | + ], | ||
| 7 | "scripts": { | 11 | "scripts": { |
| 8 | - "start": "npm run dev", | ||
| 9 | - "dev": "dumi dev", | ||
| 10 | "build": "father build", | 12 | "build": "father build", |
| 11 | "build:watch": "father dev", | 13 | "build:watch": "father dev", |
| 14 | + "dev": "dumi dev", | ||
| 12 | "docs:build": "dumi build", | 15 | "docs:build": "dumi build", |
| 13 | - "prepare": "husky install && dumi setup", | ||
| 14 | "doctor": "father doctor", | 16 | "doctor": "father doctor", |
| 15 | "lint": "npm run lint:es && npm run lint:css", | 17 | "lint": "npm run lint:es && npm run lint:css", |
| 16 | "lint:css": "stylelint \"{src,test}/**/*.{css,less}\"", | 18 | "lint:css": "stylelint \"{src,test}/**/*.{css,less}\"", |
| 17 | "lint:es": "eslint \"{src,test}/**/*.{js,jsx,ts,tsx}\"", | 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 | "commitlint": { | 24 | "commitlint": { |
| 26 | "extends": [ | 25 | "extends": [ |
| 27 | "@commitlint/config-conventional" | 26 | "@commitlint/config-conventional" |
| @@ -44,17 +43,10 @@ | @@ -44,17 +43,10 @@ | ||
| 44 | "prettier --parser=typescript --write" | 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 | "devDependencies": { | 46 | "devDependencies": { |
| 56 | "@commitlint/cli": "^17.1.2", | 47 | "@commitlint/cli": "^17.1.2", |
| 57 | "@commitlint/config-conventional": "^17.1.0", | 48 | "@commitlint/config-conventional": "^17.1.0", |
| 49 | + "@types/lodash-es": "^4.17.8", | ||
| 58 | "@types/react": "^18.0.0", | 50 | "@types/react": "^18.0.0", |
| 59 | "@types/react-dom": "^18.0.0", | 51 | "@types/react-dom": "^18.0.0", |
| 60 | "@umijs/lint": "^4.0.0", | 52 | "@umijs/lint": "^4.0.0", |
| @@ -69,5 +61,16 @@ | @@ -69,5 +61,16 @@ | ||
| 69 | "react": "^18.0.0", | 61 | "react": "^18.0.0", |
| 70 | "react-dom": "^18.0.0", | 62 | "react-dom": "^18.0.0", |
| 71 | "stylelint": "^14.9.1" | 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