├── .babelrc ├── .editorconfig ├── .fatherrc.ts ├── .gitignore ├── .prettierignore ├── .prettierrc.js ├── .umirc.ts ├── 0001-Button-Divider-finish.patch ├── README.md ├── dist ├── index.css ├── my-lib-cjs.js ├── my-lib-esm.js └── my-lib-umd.js ├── docs └── index.md ├── lib ├── index.css └── umd │ ├── index.js │ └── index.js.map ├── package.json ├── rollup.config.js ├── src ├── Affix │ ├── demos │ │ └── index1.tsx │ ├── index.md │ ├── index.module.less │ └── index.tsx ├── Button │ ├── demos │ │ ├── index1.tsx │ │ ├── index2.tsx │ │ ├── index3.tsx │ │ ├── index4.tsx │ │ ├── index5.tsx │ │ └── index6.tsx │ ├── index.md │ ├── index.module.less │ └── index.tsx ├── DatePicker │ ├── demos │ │ ├── index1.tsx │ │ ├── index2.tsx │ │ ├── index3.tsx │ │ ├── index4.tsx │ │ └── index5.tsx │ ├── index.md │ ├── index.module.less │ ├── index.tsx │ └── rangeDatePicker │ │ ├── index.module.less │ │ └── index.tsx ├── Divider │ ├── demos │ │ ├── index1.tsx │ │ ├── index2.tsx │ │ ├── index3.tsx │ │ ├── index4.tsx │ │ └── index5.tsx │ ├── index.md │ ├── index.module.less │ └── index.tsx ├── Input │ ├── demos │ │ ├── index1.tsx │ │ ├── index2.tsx │ │ ├── index3.tsx │ │ ├── index4.tsx │ │ └── index5.tsx │ ├── index.md │ ├── index.module.less │ └── index.tsx ├── Layout │ ├── Content │ │ ├── index.module.less │ │ └── index.tsx │ ├── Footer │ │ ├── index.module.less │ │ └── index.tsx │ ├── Header │ │ ├── index.module.less │ │ └── index.tsx │ ├── Slider │ │ ├── index.module.less │ │ └── index.tsx │ ├── demos │ │ ├── index1.tsx │ │ └── index2.tsx │ ├── index.md │ ├── index.module.less │ └── index.tsx ├── LazyLoad │ ├── demos │ │ └── index1.tsx │ ├── index.md │ └── index.tsx ├── Menu │ ├── demos │ │ ├── index1.tsx │ │ ├── index2.tsx │ │ ├── index3.tsx │ │ ├── index4.tsx │ │ ├── index5.tsx │ │ └── index6.tsx │ ├── index.md │ ├── index.module.less │ └── index.tsx ├── Pagination │ ├── demos │ │ ├── index1.tsx │ │ ├── index2.tsx │ │ ├── index3.tsx │ │ └── index4.tsx │ ├── index.md │ ├── index.module.less │ └── index.tsx ├── Radio │ ├── RadioGroup │ │ ├── index.module.less │ │ └── index.tsx │ ├── demos │ │ ├── index1.tsx │ │ ├── index2.tsx │ │ ├── index3.tsx │ │ └── index4.tsx │ ├── index.md │ └── index.tsx ├── Select │ ├── demos │ │ ├── index1.tsx │ │ ├── index2.tsx │ │ ├── index3.tsx │ │ ├── index4.tsx │ │ └── index5.tsx │ ├── index.md │ ├── index.module.less │ └── index.tsx └── index.ts ├── tsconfig.json └── typings.d.ts /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "modules": false, 7 | "useBuiltIns": "usage", 8 | "corejs": "2.6.10", 9 | "targets": { 10 | "ie": 10 11 | } 12 | } 13 | ] 14 | ], 15 | "plugins": [ 16 | // 解决多个地方使用相同代码导致打包重复的问题 17 | ["@babel/plugin-transform-runtime"] 18 | ], 19 | "ignore": [ 20 | "node_modules/**" 21 | ] 22 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | [Makefile] 16 | indent_style = tab 17 | -------------------------------------------------------------------------------- /.fatherrc.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | esm: 'babel', 3 | }; 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /npm-debug.log* 6 | /yarn-error.log 7 | /yarn.lock 8 | /package-lock.json 9 | 10 | # production 11 | /es 12 | /docs-dist 13 | 14 | # misc 15 | .DS_Store 16 | /coverage 17 | 18 | # umi 19 | .umi 20 | .umi-production 21 | .umi-test 22 | .env.local 23 | 24 | # ide 25 | /.vscode 26 | /.idea 27 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | **/*.svg 2 | **/*.ejs 3 | **/*.html 4 | package.json 5 | .umi 6 | .umi-production 7 | .umi-test 8 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require('@umijs/fabric').prettier; 2 | -------------------------------------------------------------------------------- /.umirc.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'dumi'; 2 | 3 | export default defineConfig({ 4 | title: 'React-View-UI', 5 | favicon: 6 | 'https://user-images.githubusercontent.com/9554297/83762004-a0761b00-a6a9-11ea-83b4-9c8ff721d4b8.png', 7 | logo: 'https://user-images.githubusercontent.com/9554297/83762004-a0761b00-a6a9-11ea-83b4-9c8ff721d4b8.png', 8 | outputPath: 'docs-dist', 9 | mode: 'site', 10 | apiParser: { 11 | // 自定义属性过滤配置,也可以是一个函数,用法参考:https://github.com/styleguidist/react-docgen-typescript/#propfilter 12 | propFilter: { 13 | // 是否忽略从 node_modules 继承的属性,默认值为 false 14 | skipNodeModules: true, 15 | // 需要忽略的属性名列表,默认为空数组 16 | skipPropsWithName: ['autoFocus', 'form', 'formAction', 'formEncType', 'title'], 17 | // 是否忽略没有文档说明的属性,默认值为 false 18 | skipPropsWithoutDoc: false, 19 | }, 20 | }, 21 | history: { 22 | type: 'hash', 23 | }, 24 | navs: [ 25 | null, 26 | { 27 | title: 'GitHub', 28 | path: 'https://github.com/fengxinhhh/React-View-UI-fs', 29 | }, 30 | ], 31 | }); 32 | -------------------------------------------------------------------------------- /0001-Button-Divider-finish.patch: -------------------------------------------------------------------------------- 1 | From 0f26a0f527e0bc98b711765c4620f37b675c008c Mon Sep 17 00:00:00 2001 2 | From: apple 3 | Date: Sat, 26 Mar 2022 00:37:41 +0800 4 | Subject: [PATCH] Button Divider finish 5 | 6 | --- 7 | .umirc.ts | 12 +++- 8 | docs/index.md | 5 +- 9 | package.json | 2 +- 10 | src/Button/demos/index.tsx | 7 --- 11 | src/Button/demos/index1.tsx | 13 +++++ 12 | src/Button/demos/index2.tsx | 18 ++++++ 13 | src/Button/demos/index3.tsx | 21 +++++++ 14 | src/Button/demos/index4.tsx | 21 +++++++ 15 | src/Button/demos/index5.tsx | 21 +++++++ 16 | src/Button/demos/index6.tsx | 21 +++++++ 17 | src/Button/index.md | 53 +++++++++++++---- 18 | src/Button/index.module.less | 83 +++++++++++++++++++++++++++ 19 | src/Button/index.tsx | 103 ++++++++++++++++++++++++++++++++-- 20 | src/Divider/demos/index1.tsx | 12 ++++ 21 | src/Divider/demos/index2.tsx | 20 +++++++ 22 | src/Divider/demos/index3.tsx | 26 +++++++++ 23 | src/Divider/demos/index4.tsx | 24 ++++++++ 24 | src/Divider/demos/index5.tsx | 12 ++++ 25 | src/Divider/index.md | 39 +++++++++++++ 26 | src/Divider/index.module.less | 30 ++++++++++ 27 | src/Divider/index.tsx | 66 ++++++++++++++++++++++ 28 | src/Table/index.md | 8 +-- 29 | src/index.ts | 5 +- 30 | tsconfig.json | 6 +- 31 | 24 files changed, 590 insertions(+), 38 deletions(-) 32 | delete mode 100644 src/Button/demos/index.tsx 33 | create mode 100644 src/Button/demos/index1.tsx 34 | create mode 100644 src/Button/demos/index2.tsx 35 | create mode 100644 src/Button/demos/index3.tsx 36 | create mode 100644 src/Button/demos/index4.tsx 37 | create mode 100644 src/Button/demos/index5.tsx 38 | create mode 100644 src/Button/demos/index6.tsx 39 | create mode 100644 src/Button/index.module.less 40 | create mode 100644 src/Divider/demos/index1.tsx 41 | create mode 100644 src/Divider/demos/index2.tsx 42 | create mode 100644 src/Divider/demos/index3.tsx 43 | create mode 100644 src/Divider/demos/index4.tsx 44 | create mode 100644 src/Divider/demos/index5.tsx 45 | create mode 100644 src/Divider/index.md 46 | create mode 100644 src/Divider/index.module.less 47 | create mode 100644 src/Divider/index.tsx 48 | 49 | diff --git a/.umirc.ts b/.umirc.ts 50 | index 55d6a7e4..182b0b16 100644 51 | --- a/.umirc.ts 52 | +++ b/.umirc.ts 53 | @@ -7,5 +7,15 @@ export default defineConfig({ 54 | logo: 'https://user-images.githubusercontent.com/9554297/83762004-a0761b00-a6a9-11ea-83b4-9c8ff721d4b8.png', 55 | outputPath: 'docs-dist', 56 | mode: 'site', 57 | - // more config: https://d.umijs.org/config 58 | + apiParser: { 59 | + // 自定义属性过滤配置,也可以是一个函数,用法参考:https://github.com/styleguidist/react-docgen-typescript/#propfilter 60 | + propFilter: { 61 | + // 是否忽略从 node_modules 继承的属性,默认值为 false 62 | + skipNodeModules: true, 63 | + // 需要忽略的属性名列表,默认为空数组 64 | + skipPropsWithName: ['autoFocus', 'form', 'formAction', 'formEncType', 'title'], 65 | + // 是否忽略没有文档说明的属性,默认值为 false 66 | + skipPropsWithoutDoc: false, 67 | + }, 68 | + }, 69 | }); 70 | diff --git a/docs/index.md b/docs/index.md 71 | index 8d866060..84082263 100644 72 | --- a/docs/index.md 73 | +++ b/docs/index.md 74 | @@ -1,10 +1,10 @@ 75 | --- 76 | hero: 77 | - title: react-view-ui 78 | + title: React View UI 79 | desc: 一套易用的轻量级的React UI 组件库 80 | actions: 81 | - text: Getting Started 82 | - link: /view/table 83 | + link: /common/button 84 | features: 85 | - icon: https://gw.alipayobjects.com/zos/bmw-prod/881dc458-f20b-407b-947a-95104b5ec82b/k79dm8ih_w144_h144.png 86 | title: 开箱即用 87 | @@ -19,4 +19,5 @@ footer: Open-source MIT Licensed | Copyright © 2020
Powered by [dumi](http 88 | --- 89 | 90 | ## 快速网站成型工具 91 | + 92 | ##### 易用于多端开发 93 | diff --git a/package.json b/package.json 94 | index 0523d98a..8100a804 100644 95 | --- a/package.json 96 | +++ b/package.json 97 | @@ -1,7 +1,7 @@ 98 | { 99 | "private": true, 100 | "name": "react-view-ui", 101 | - "version": "1.0.0", 102 | + "version": "1.0.1", 103 | "scripts": { 104 | "start": "dumi dev", 105 | "docs:build": "dumi build", 106 | diff --git a/src/Button/demos/index.tsx b/src/Button/demos/index.tsx 107 | deleted file mode 100644 108 | index 297c7661..00000000 109 | --- a/src/Button/demos/index.tsx 110 | +++ /dev/null 111 | @@ -1,7 +0,0 @@ 112 | -import React from 'react' 113 | - 114 | -export default function ButtonDemo() { 115 | - return ( 116 | -
123
117 | - ) 118 | -} 119 | diff --git a/src/Button/demos/index1.tsx b/src/Button/demos/index1.tsx 120 | new file mode 100644 121 | index 00000000..8d4c3868 122 | --- /dev/null 123 | +++ b/src/Button/demos/index1.tsx 124 | @@ -0,0 +1,13 @@ 125 | +import React from 'react'; 126 | +import Button from '..'; 127 | + 128 | +export default function ButtonDemo1() { 129 | + return ( 130 | +
131 | + 132 | + 133 | + 134 | + 135 | +
136 | + ); 137 | +} 138 | diff --git a/src/Button/demos/index2.tsx b/src/Button/demos/index2.tsx 139 | new file mode 100644 140 | index 00000000..4bb5b396 141 | --- /dev/null 142 | +++ b/src/Button/demos/index2.tsx 143 | @@ -0,0 +1,18 @@ 144 | +import React from 'react'; 145 | +import Button from '..'; 146 | + 147 | +export default function ButtonDemo2() { 148 | + return ( 149 | +
150 | + 153 | + 156 | + 159 | +
160 | + ); 161 | +} 162 | diff --git a/src/Button/demos/index3.tsx b/src/Button/demos/index3.tsx 163 | new file mode 100644 164 | index 00000000..f52fc2f8 165 | --- /dev/null 166 | +++ b/src/Button/demos/index3.tsx 167 | @@ -0,0 +1,21 @@ 168 | +import React from 'react'; 169 | +import Button from '..'; 170 | + 171 | +export default function ButtonDemo3() { 172 | + return ( 173 | +
174 | + 177 | + 180 | + 183 | + 186 | +
187 | + ); 188 | +} 189 | diff --git a/src/Button/demos/index4.tsx b/src/Button/demos/index4.tsx 190 | new file mode 100644 191 | index 00000000..ad79067b 192 | --- /dev/null 193 | +++ b/src/Button/demos/index4.tsx 194 | @@ -0,0 +1,21 @@ 195 | +import React from 'react'; 196 | +import Button from '..'; 197 | + 198 | +export default function ButtonDemo4() { 199 | + return ( 200 | +
201 | + 204 | + 207 | + 210 | + 213 | +
214 | + ); 215 | +} 216 | diff --git a/src/Button/demos/index5.tsx b/src/Button/demos/index5.tsx 217 | new file mode 100644 218 | index 00000000..bc9010fd 219 | --- /dev/null 220 | +++ b/src/Button/demos/index5.tsx 221 | @@ -0,0 +1,21 @@ 222 | +import React from 'react'; 223 | +import Button from '..'; 224 | + 225 | +export default function ButtonDemo5() { 226 | + return ( 227 | +
228 | + 231 | + 234 | + 237 | + 240 | +
241 | + ); 242 | +} 243 | diff --git a/src/Button/demos/index6.tsx b/src/Button/demos/index6.tsx 244 | new file mode 100644 245 | index 00000000..ffe48e92 246 | --- /dev/null 247 | +++ b/src/Button/demos/index6.tsx 248 | @@ -0,0 +1,21 @@ 249 | +import React from 'react'; 250 | +import Button from '..'; 251 | + 252 | +export default function ButtonDemo6() { 253 | + return ( 254 | +
255 | + 258 | + 261 | + 264 | + 267 | +
268 | + ); 269 | +} 270 | diff --git a/src/Button/index.md b/src/Button/index.md 271 | index e6283cd1..f14d0c1b 100644 272 | --- a/src/Button/index.md 273 | +++ b/src/Button/index.md 274 | @@ -1,22 +1,53 @@ 275 | --- 276 | -title: Button 277 | +title: Button 按钮 278 | nav: 279 | - title: 导航 280 | - path: /navigator 281 | + title: 通用 282 | + path: /common 283 | group: 284 | - path: /navigator 285 | + path: /common 286 | --- 287 | 288 | -# Button 289 | +# Button 按钮 290 | 291 | -排行榜组件用于简易排行榜业务场景。 292 | +按钮用于开始一个即时操作。 293 | 294 | -## 基础使用 295 | +#### 何时使用 296 | 297 | - 298 | +

标记了一个(或封装一组)操作命令,响应用户点击行为,触发相应的业务逻辑。

299 | +

300 | +

在 React View UI 中我们提供了六种按钮。

301 | 302 | - 303 | +- 状态颜色改变的主题按钮 304 | +- 不同尺寸的按钮 305 | +- 圆形字体按钮 306 | +- 禁用状态按钮 307 | +- 虚线按钮 308 | +- 加载状态按钮 309 | + 310 | +## 基本使用 311 | + 312 | + 313 | + 314 | +## 不同尺寸 315 | + 316 | + 317 | + 318 | +## 字体按钮 319 | + 320 | + 321 | 322 | -## 复杂用法 323 | +## 禁用按钮 324 | 325 | - 326 | \ No newline at end of file 327 | + 328 | + 329 | +## 虚线按钮 330 | + 331 | +

只有文本按钮(type=text)支持虚线样式

332 | + 333 | + 334 | + 335 | +## 加载状态 336 | + 337 | + 338 | + 339 | + 340 | diff --git a/src/Button/index.module.less b/src/Button/index.module.less 341 | new file mode 100644 342 | index 00000000..aa698fe7 343 | --- /dev/null 344 | +++ b/src/Button/index.module.less 345 | @@ -0,0 +1,83 @@ 346 | +.button { 347 | + .primary { 348 | + display: flex; 349 | + align-items: center; 350 | + justify-content: center; 351 | + color: #fff; 352 | + background-color: #1890ff; 353 | + border: none; 354 | + border-radius: 10px; 355 | + cursor: pointer; 356 | + transition: 0.2s linear; 357 | + &:hover { 358 | + opacity: 0.7; 359 | + } 360 | + } 361 | + .danger { 362 | + display: flex; 363 | + align-items: center; 364 | + justify-content: center; 365 | + color: #fff; 366 | + background-color: brown; 367 | + border: none; 368 | + border-radius: 10px; 369 | + cursor: pointer; 370 | + transition: 0.2s linear; 371 | + &:hover { 372 | + opacity: 0.7; 373 | + } 374 | + } 375 | + .warning { 376 | + display: flex; 377 | + align-items: center; 378 | + justify-content: center; 379 | + color: #fff; 380 | + background-color: rgb(224, 197, 41); 381 | + border: none; 382 | + border-radius: 10px; 383 | + cursor: pointer; 384 | + transition: 0.2s linear; 385 | + &:hover { 386 | + opacity: 0.7; 387 | + } 388 | + } 389 | + .text { 390 | + display: flex; 391 | + align-items: center; 392 | + justify-content: center; 393 | + color: #000000; 394 | + background-color: #fff; 395 | + border: none; 396 | + border: 1px solid #ccc; 397 | + border-radius: 10px; 398 | + cursor: pointer; 399 | + transition: 0.2s linear; 400 | + &:hover { 401 | + color: #1890ff; 402 | + border-color: #1890ff !important; 403 | + } 404 | + } 405 | + .loading1 { 406 | + width: 10px; 407 | + height: 10px; 408 | + margin-right: 10px; 409 | + border: 2px solid #ccc; 410 | + border-color: #ccc #eee rgb(226, 226, 226) rgb(109, 108, 108); 411 | + border-radius: 50%; 412 | + animation: 1s loadingWatch infinite linear; 413 | + } 414 | + @keyframes loadingWatch { 415 | + 0% { 416 | + transform: rotate(0deg); 417 | + } 418 | + 50% { 419 | + transform: rotate(100deg); 420 | + } 421 | + 75% { 422 | + transform: rotate(200deg); 423 | + } 424 | + 100% { 425 | + transform: rotate(360deg); 426 | + } 427 | + } 428 | +} 429 | diff --git a/src/Button/index.tsx b/src/Button/index.tsx 430 | index 77478a60..43fab264 100644 431 | --- a/src/Button/index.tsx 432 | +++ b/src/Button/index.tsx 433 | @@ -1,7 +1,100 @@ 434 | -import React from 'react' 435 | +import React, { useEffect, useMemo, FC, memo } from 'react'; 436 | +import './index.module.less'; 437 | 438 | -export default function Button() { 439 | - return ( 440 | -
按钮组件
441 | - ) 442 | +interface ButtonProps { 443 | + //自定义button接口 444 | + /** 445 | + * @description 按钮主题 446 | + * @default primary 447 | + */ 448 | + type?: String; 449 | + /** 450 | + * @description 宽度 451 | + */ 452 | + width?: Number; 453 | + /** 454 | + * @description 高度 455 | + */ 456 | + height?: Number; 457 | + /** 458 | + * @description 禁用状态 459 | + * @default false 460 | + */ 461 | + disabled?: Boolean; 462 | + /** 463 | + * @description 字体按钮 464 | + * @default false 465 | + */ 466 | + circle?: Boolean; 467 | + /** 468 | + * @description 按钮边框为虚线 469 | + * @default false 470 | + */ 471 | + dashed?: Boolean; 472 | + /** 473 | + * @description 加载状态 474 | + * @default false 475 | + */ 476 | + loading?: Boolean; 477 | + /** 478 | + * @description 按钮点击回调事件 479 | + */ 480 | + handleClick?: Function | undefined; 481 | +} 482 | +interface ButtonStyle { 483 | + //button样式接口 484 | + width?: String; 485 | + height?: String; 486 | + borderRadius?: String; 487 | + border?: String; 488 | + cursor?: String; 489 | } 490 | +type NativeButtonProps = Omit, 'type'>; //原生button接口 491 | + 492 | +const Button: FC = memo((props) => { 493 | + const { type, width, height, disabled, circle, dashed, loading, handleClick, children } = props; 494 | + 495 | + const buttonStyle = useMemo(() => { 496 | + if (!type && type !== 'danger' && type !== 'warning' && type !== 'warning' && type !== 'text') { 497 | + return 'primary'; 498 | + } 499 | + return type as any; 500 | + }, [type]); 501 | + const buttonSize = useMemo(() => { 502 | + var size: ButtonStyle = { 503 | + width: '100px', 504 | + height: '40px', 505 | + }; 506 | + if (width) { 507 | + size.width = width + 'px'; 508 | + } 509 | + if (height) { 510 | + size.height = height + 'px'; 511 | + } 512 | + if (circle) { 513 | + size = { ...size, borderRadius: '50%' }; 514 | + } 515 | + if (dashed && type === 'text') { 516 | + size = { ...size, border: '1px dashed #ccc' }; 517 | + } 518 | + if (disabled) { 519 | + size = { ...size, cursor: 'not-allowed' }; 520 | + } 521 | + return size; 522 | + }, [width, height, circle, dashed]); 523 | + return ( 524 | +
525 | + 534 | +
535 | + ); 536 | +}); 537 | + 538 | +export default Button; 539 | diff --git a/src/Divider/demos/index1.tsx b/src/Divider/demos/index1.tsx 540 | new file mode 100644 541 | index 00000000..68d11dfa 542 | --- /dev/null 543 | +++ b/src/Divider/demos/index1.tsx 544 | @@ -0,0 +1,12 @@ 545 | +import Divider from '..'; 546 | +import React from 'react'; 547 | +/** 548 | + * transform: true 549 | + */ 550 | +export default function DividerDemo1() { 551 | + return ( 552 | +
553 | + React View UI 554 | +
555 | + ); 556 | +} 557 | diff --git a/src/Divider/demos/index2.tsx b/src/Divider/demos/index2.tsx 558 | new file mode 100644 559 | index 00000000..347195bc 560 | --- /dev/null 561 | +++ b/src/Divider/demos/index2.tsx 562 | @@ -0,0 +1,20 @@ 563 | +import Divider from '..'; 564 | +import React from 'react'; 565 | +/** 566 | + * transform: true 567 | + */ 568 | +export default function DividerDemo2() { 569 | + return ( 570 | +
571 | +
572 | + React View UI 573 | +
574 | +
575 | + React View UI 576 | +
577 | +
578 | + React View UI 579 | +
580 | +
581 | + ); 582 | +} 583 | diff --git a/src/Divider/demos/index3.tsx b/src/Divider/demos/index3.tsx 584 | new file mode 100644 585 | index 00000000..0ceee774 586 | --- /dev/null 587 | +++ b/src/Divider/demos/index3.tsx 588 | @@ -0,0 +1,26 @@ 589 | +import Divider from '..'; 590 | +import React from 'react'; 591 | +/** 592 | + * transform: true 593 | + */ 594 | +export default function DividerDemo3() { 595 | + return ( 596 | +
597 | +
598 | + 599 | + React View UI 600 | + 601 | +
602 | +
603 | + 604 | + React View UI 605 | + 606 | +
607 | +
608 | + 609 | + React View UI 610 | + 611 | +
612 | +
613 | + ); 614 | +} 615 | diff --git a/src/Divider/demos/index4.tsx b/src/Divider/demos/index4.tsx 616 | new file mode 100644 617 | index 00000000..7dfe6aa5 618 | --- /dev/null 619 | +++ b/src/Divider/demos/index4.tsx 620 | @@ -0,0 +1,24 @@ 621 | +import Divider from '..'; 622 | +import React from 'react'; 623 | +/** 624 | + * transform: true 625 | + */ 626 | +export default function DividerDemo3() { 627 | + return ( 628 | +
629 | +
630 | + 631 | + React View UI 632 | + 633 | +
634 | +
635 | + React View UI 636 | +
637 | +
638 | + 639 | + React View UI 640 | + 641 | +
642 | +
643 | + ); 644 | +} 645 | diff --git a/src/Divider/demos/index5.tsx b/src/Divider/demos/index5.tsx 646 | new file mode 100644 647 | index 00000000..73e369ae 648 | --- /dev/null 649 | +++ b/src/Divider/demos/index5.tsx 650 | @@ -0,0 +1,12 @@ 651 | +import Divider from '..'; 652 | +import React from 'react'; 653 | +/** 654 | + * transform: true 655 | + */ 656 | +export default function DividerDemo5() { 657 | + return ( 658 | +
659 | + React View UI 660 | +
661 | + ); 662 | +} 663 | diff --git a/src/Divider/index.md b/src/Divider/index.md 664 | new file mode 100644 665 | index 00000000..89787048 666 | --- /dev/null 667 | +++ b/src/Divider/index.md 668 | @@ -0,0 +1,39 @@ 669 | +--- 670 | +title: Divider 分割线 671 | +nav: 672 | + title: 视图 673 | + path: /view 674 | +group: 675 | + path: /view 676 | +--- 677 | + 678 | +# Divider 分割线 679 | + 680 | +

分隔内容的文档分割线

681 | + 682 | +#### 何时使用 683 | + 684 | +- 对不同章节的文本段落进行分割。 685 | +- 对行内文字/链接进行分割,例如表格的操作列。 686 | + 687 | +## 基本用法 688 | + 689 | + 690 | + 691 | +## 字体大小 692 | + 693 | + 694 | + 695 | +## 分割线色彩 696 | + 697 | + 698 | + 699 | +## 对齐方式 700 | + 701 | + 702 | + 703 | +## 虚线 704 | + 705 | + 706 | + 707 | + 708 | diff --git a/src/Divider/index.module.less b/src/Divider/index.module.less 709 | new file mode 100644 710 | index 00000000..2bbf74e0 711 | --- /dev/null 712 | +++ b/src/Divider/index.module.less 713 | @@ -0,0 +1,30 @@ 714 | +.divider { 715 | + .line { 716 | + position: relative; 717 | + display: flex; 718 | + align-items: center; 719 | + justify-content: center; 720 | + width: 100%; 721 | + height: 1px; 722 | + border-top: 1px solid #ccc; 723 | + .line-text { 724 | + position: absolute; 725 | + padding: 0 10px; 726 | + background-color: #fff; 727 | + } 728 | + } 729 | + .dashed { 730 | + position: relative; 731 | + display: flex; 732 | + align-items: center; 733 | + justify-content: center; 734 | + width: 100%; 735 | + height: 1px; 736 | + border-top: 1px dashed #ccc; 737 | + .line-text { 738 | + position: absolute; 739 | + padding: 0 10px; 740 | + background-color: #fff; 741 | + } 742 | + } 743 | +} 744 | diff --git a/src/Divider/index.tsx b/src/Divider/index.tsx 745 | new file mode 100644 746 | index 00000000..4db85911 747 | --- /dev/null 748 | +++ b/src/Divider/index.tsx 749 | @@ -0,0 +1,66 @@ 750 | +import React, { FC, useMemo, memo } from 'react'; 751 | +import './index.module.less'; 752 | + 753 | +interface dividerProps { 754 | + /** 755 | + * @description 字体大小 756 | + */ 757 | + fontSize?: Number; 758 | + /** 759 | + * @description 分割线颜色 760 | + * @default #cccccc 761 | + */ 762 | + borderColor?: String; 763 | + /** 764 | + * @description 对齐方式 765 | + * @default center 766 | + */ 767 | + align?: String; 768 | + /** 769 | + * @description 分割线类型 770 | + * @default border 771 | + */ 772 | + dashed?: Boolean; 773 | +} 774 | +const Divider: FC = memo((props) => { 775 | + const { children, fontSize, borderColor, align, dashed } = props; 776 | + const lineAlign = useMemo(() => { 777 | + if (align === 'left') { 778 | + return { 779 | + justifyContent: 'left', 780 | + }; 781 | + } else if (align === 'right') { 782 | + return { 783 | + justifyContent: 'right', 784 | + }; 785 | + } 786 | + return {}; 787 | + }, [align]); 788 | + const lineColor = useMemo((): Object => { 789 | + if (borderColor) { 790 | + return { 791 | + borderColor: borderColor, 792 | + }; 793 | + } 794 | + return {}; 795 | + }, [borderColor]); 796 | + const textStyle = useMemo(() => { 797 | + if (fontSize) { 798 | + return { 799 | + fontSize: `${fontSize}px`, 800 | + }; 801 | + } 802 | + }, [fontSize]); 803 | + return ( 804 | +
805 | +
806 | + {children && ( 807 | + 808 | + {children} 809 | + 810 | + )} 811 | +
812 | +
813 | + ); 814 | +}); 815 | +export default Divider; 816 | diff --git a/src/Table/index.md b/src/Table/index.md 817 | index 2c290024..2dbe71fe 100644 818 | --- a/src/Table/index.md 819 | +++ b/src/Table/index.md 820 | @@ -1,5 +1,5 @@ 821 | --- 822 | -title: Table 823 | +title: Table 表格 824 | nav: 825 | title: 视图 826 | path: /view 827 | @@ -15,8 +15,8 @@ group: 828 | 829 | 830 | 831 | - 832 | - 833 | ## 复杂用法 834 | 835 | - 836 | \ No newline at end of file 837 | + 838 | + 839 | + 840 | diff --git a/src/index.ts b/src/index.ts 841 | index c3a015ce..81fbd4b5 100644 842 | --- a/src/index.ts 843 | +++ b/src/index.ts 844 | @@ -1,2 +1,3 @@ 845 | -export { default as Button} from './Button'; 846 | -export { default as Table} from './Table'; 847 | \ No newline at end of file 848 | +export { default as Button } from './Button'; 849 | +export { default as Table } from './Table'; 850 | +export { default as Divider } from './Divider'; 851 | diff --git a/tsconfig.json b/tsconfig.json 852 | index 8d4a25aa..10c03be0 100644 853 | --- a/tsconfig.json 854 | +++ b/tsconfig.json 855 | @@ -9,10 +9,6 @@ 856 | "strict": true, 857 | "skipLibCheck": true, 858 | "declaration": true, 859 | - "paths": { 860 | - "@/*": ["src/*"], 861 | - "@@/*": ["src/.umi/*"], 862 | - "component-lib-demo": ["src/index.ts"] 863 | - }, 864 | + "baseUrl": "./" 865 | } 866 | } 867 | -- 868 | 2.32.0 (Apple Git-132) 869 | 870 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-view-ui 2 | 3 | ## Getting Started 4 | 5 | Install dependencies, 6 | 7 | ```bash 8 | $ npm i 9 | ``` 10 | 11 | Start the dev server, 12 | 13 | ```bash 14 | $ npm start 15 | ``` 16 | 17 | Build documentation, 18 | 19 | ```bash 20 | $ npm run docs:build 21 | ``` 22 | 23 | Run test, 24 | 25 | ```bash 26 | $ npm test 27 | ``` 28 | 29 | Build library via `father-build`, 30 | 31 | ```bash 32 | $ npm run build 33 | ``` 34 | -------------------------------------------------------------------------------- /dist/index.css: -------------------------------------------------------------------------------- 1 | .button .primary { 2 | display: flex; 3 | align-items: center; 4 | justify-content: center; 5 | color: #fff; 6 | background-color: #1890ff; 7 | border: none; 8 | border-radius: 10px; 9 | cursor: pointer; 10 | transition: 0.2s linear; 11 | } 12 | .button .primary:hover { 13 | opacity: 0.7; 14 | } 15 | .button .danger { 16 | display: flex; 17 | align-items: center; 18 | justify-content: center; 19 | color: #fff; 20 | background-color: brown; 21 | border: none; 22 | border-radius: 10px; 23 | cursor: pointer; 24 | transition: 0.2s linear; 25 | } 26 | .button .danger:hover { 27 | opacity: 0.7; 28 | } 29 | .button .warning { 30 | display: flex; 31 | align-items: center; 32 | justify-content: center; 33 | color: #fff; 34 | background-color: #e0c529; 35 | border: none; 36 | border-radius: 10px; 37 | cursor: pointer; 38 | transition: 0.2s linear; 39 | } 40 | .button .warning:hover { 41 | opacity: 0.7; 42 | } 43 | .button .text { 44 | display: flex; 45 | align-items: center; 46 | justify-content: center; 47 | color: #000000; 48 | background-color: #fff; 49 | border: none; 50 | border: 1px solid #ccc; 51 | border-radius: 10px; 52 | cursor: pointer; 53 | transition: 0.2s linear; 54 | } 55 | .button .text:hover { 56 | color: #1890ff; 57 | border-color: #1890ff !important; 58 | } 59 | .button .loading1 { 60 | width: 10px; 61 | height: 10px; 62 | margin-right: 10px; 63 | border: 2px solid #ccc; 64 | border-color: #ccc #eee #e2e2e2 #6d6c6c; 65 | border-radius: 50%; 66 | animation: 1s loadingWatch infinite linear; 67 | } 68 | @keyframes loadingWatch { 69 | 0% { 70 | transform: rotate(0deg); 71 | } 72 | 50% { 73 | transform: rotate(100deg); 74 | } 75 | 75% { 76 | transform: rotate(200deg); 77 | } 78 | 100% { 79 | transform: rotate(360deg); 80 | } 81 | } 82 | .layout { 83 | width: 100%; 84 | display: flex; 85 | flex-wrap: wrap; 86 | justify-content: space-between; 87 | } 88 | .divider .line { 89 | position: relative; 90 | display: flex; 91 | align-items: center; 92 | justify-content: center; 93 | width: 100%; 94 | height: 1px; 95 | border-top: 1px solid #ccc; 96 | } 97 | .divider .line .line-text { 98 | position: absolute; 99 | padding: 0 10px; 100 | background-color: #fff; 101 | } 102 | .divider .dashed { 103 | position: relative; 104 | display: flex; 105 | align-items: center; 106 | justify-content: center; 107 | width: 100%; 108 | height: 1px; 109 | border-top: 1px dashed #ccc; 110 | } 111 | .divider .dashed .line-text { 112 | position: absolute; 113 | padding: 0 10px; 114 | background-color: #fff; 115 | } 116 | .select { 117 | position: relative; 118 | width: 80px; 119 | height: 30px; 120 | margin-left: 10px; 121 | padding: 0 2px; 122 | font-size: 14px; 123 | line-height: 30px; 124 | text-align: center; 125 | border: 1px solid #ccc; 126 | cursor: pointer; 127 | transition: 0.2s linear; 128 | -moz-user-select: none; 129 | -o-user-select: none; 130 | -khtml-user-select: none; 131 | -webkit-user-select: none; 132 | -ms-user-select: none; 133 | user-select: none; 134 | } 135 | .select .selected { 136 | display: flex; 137 | align-items: center; 138 | justify-content: space-between; 139 | height: 100%; 140 | padding: 0 5px; 141 | } 142 | .select .selected .size { 143 | margin-right: 5px; 144 | } 145 | .select .selected .placeholder { 146 | margin-right: 5px; 147 | color: #ccc; 148 | } 149 | .select .selected:focus { 150 | border-color: #1890ff; 151 | } 152 | .select:hover { 153 | border-color: #1890ff; 154 | } 155 | .select input { 156 | border: none; 157 | outline: none; 158 | } 159 | .select .selectOptions { 160 | position: absolute; 161 | top: 35px; 162 | z-index: 500; 163 | display: flex; 164 | flex-direction: column; 165 | width: 80px; 166 | height: 0px; 167 | overflow: hidden; 168 | box-shadow: 0 3px 10px #8c8c8c; 169 | transition: 0.1s linear; 170 | } 171 | .select .selectOptions .option { 172 | font-size: 14px; 173 | line-height: 2em; 174 | text-align: center; 175 | cursor: pointer; 176 | } 177 | .select .selectOptions .option:hover { 178 | color: #1890ff; 179 | } 180 | .selectOptions { 181 | position: absolute; 182 | top: 35px; 183 | z-index: 500; 184 | display: flex; 185 | flex-direction: column; 186 | width: 80px; 187 | height: 0px; 188 | overflow: hidden; 189 | box-shadow: 0 3px 10px #8c8c8c; 190 | transition: 0.1s linear; 191 | } 192 | .selectOptions .option { 193 | font-size: 14px; 194 | line-height: 2em; 195 | text-align: center; 196 | cursor: pointer; 197 | } 198 | .selectOptions .option:hover { 199 | color: #1890ff; 200 | } 201 | .menu { 202 | width: 220px; 203 | padding: 2px 0px; 204 | color: black; 205 | -moz-user-select: none; 206 | -webkit-user-select: none; 207 | user-select: none; 208 | } 209 | .menu .menuOptions { 210 | padding: 0 10px; 211 | overflow: hidden; 212 | font-size: 14px; 213 | line-height: 50px; 214 | cursor: pointer; 215 | transition: 0.2s linear; 216 | } 217 | .menu .menuOptions .fatherTitle { 218 | display: flex; 219 | align-items: center; 220 | justify-content: space-between; 221 | height: 50px; 222 | } 223 | .menu .menuOptions .fatherTitle:hover { 224 | color: #1890ff; 225 | } 226 | .menu .menuOptions .activeFatherTitle { 227 | display: flex; 228 | align-items: center; 229 | justify-content: space-between; 230 | height: 50px; 231 | color: #1890ff; 232 | } 233 | .menu .menuOptions .activeFatherTitle:hover { 234 | color: #1890ff; 235 | } 236 | .menu .menuOptions .left i { 237 | margin-right: 10px; 238 | } 239 | .menu .activeMenuOptions { 240 | display: flex; 241 | flex-direction: column; 242 | box-sizing: border-box; 243 | padding-left: 5px; 244 | overflow: hidden; 245 | color: #1890ff; 246 | font-size: 14px; 247 | line-height: 50px; 248 | background-color: #e6f7ff; 249 | cursor: pointer; 250 | transition: 0.2s linear; 251 | } 252 | .menu .activeMenuOptions :after { 253 | width: 3px; 254 | height: 50px; 255 | background-color: #1890ff; 256 | content: ''; 257 | } 258 | .menu .childMenuOptions { 259 | display: flex; 260 | flex-direction: column; 261 | box-sizing: border-box; 262 | padding-left: 5px; 263 | overflow: hidden; 264 | font-size: 14px; 265 | line-height: 50px; 266 | cursor: pointer; 267 | transition: 0.2s linear; 268 | } 269 | .darkMenu { 270 | box-sizing: border-box; 271 | width: 200px; 272 | padding: 2px 0px; 273 | color: #ffffffa6; 274 | background-color: #001529; 275 | -moz-user-select: none; 276 | -webkit-user-select: none; 277 | user-select: none; 278 | } 279 | .darkMenu .menuOptions { 280 | padding: 0 10px; 281 | overflow: hidden; 282 | font-size: 14px; 283 | line-height: 50px; 284 | cursor: pointer; 285 | transition: 0.2s linear; 286 | } 287 | .darkMenu .menuOptions .fatherTitle { 288 | display: flex; 289 | align-items: center; 290 | justify-content: space-between; 291 | height: 50px; 292 | } 293 | .darkMenu .menuOptions .fatherTitle:hover { 294 | color: #fff; 295 | } 296 | .darkMenu .menuOptions .activeFatherTitle { 297 | display: flex; 298 | align-items: center; 299 | justify-content: space-between; 300 | box-sizing: border-box; 301 | height: 50px; 302 | color: #fff; 303 | } 304 | .darkMenu .menuOptions .activeFatherTitle:hover { 305 | color: #fff; 306 | } 307 | .darkMenu .menuOptions .left i { 308 | margin-right: 10px; 309 | } 310 | .darkMenu .activeMenuOptions { 311 | position: relative; 312 | display: flex; 313 | flex-direction: column; 314 | box-sizing: border-box; 315 | padding-left: 5px; 316 | overflow: hidden; 317 | color: #fff; 318 | font-size: 14px; 319 | line-height: 50px; 320 | background-color: #1890ff; 321 | cursor: pointer; 322 | transition: 0.2s linear; 323 | } 324 | .darkMenu .childMenuOptions { 325 | position: relative; 326 | display: flex; 327 | flex-direction: column; 328 | box-sizing: border-box; 329 | padding-left: 5px; 330 | overflow: hidden; 331 | font-size: 14px; 332 | line-height: 50px; 333 | cursor: pointer; 334 | transition: 0.2s linear; 335 | } 336 | .affix { 337 | position: relative; 338 | z-index: 99; 339 | display: inline-block; 340 | color: #fff; 341 | background-color: #1890ff; 342 | } 343 | .time-picker { 344 | position: relative; 345 | } 346 | .time-picker .result { 347 | color: #000000; 348 | font-weight: 700; 349 | letter-spacing: 1px; 350 | transition: 0.3s linear; 351 | cursor: pointer; 352 | display: flex; 353 | width: 150px; 354 | justify-content: center; 355 | } 356 | .time-picker .result:hover { 357 | letter-spacing: normal; 358 | color: #1890FF; 359 | width: 120px; 360 | } 361 | .time-picker .result:hover .icon { 362 | opacity: 1; 363 | margin-left: 5px; 364 | } 365 | .time-picker .result:active { 366 | letter-spacing: normal; 367 | color: #1890FF; 368 | } 369 | .time-picker .result:active .icon { 370 | opacity: 1; 371 | margin-left: 5px; 372 | } 373 | .time-picker .result .icon { 374 | opacity: 0; 375 | transition: 0.2s linear; 376 | } 377 | .time-picker .input { 378 | padding: 4px 11px; 379 | color: #000000d9; 380 | font-size: 14px; 381 | line-height: 1.5715; 382 | background-color: #fff; 383 | border: 1px solid #d9d9d9; 384 | border-radius: 2px; 385 | outline: none; 386 | transition: all 0.3s; 387 | } 388 | .time-picker .input:hover { 389 | border-color: #8fb6d8; 390 | } 391 | .time-picker .input:focus { 392 | border-color: #8fb6d8; 393 | box-shadow: 0px 0px 10px 1px #aed7ec; 394 | } 395 | .time-picker .check-box { 396 | width: 220px; 397 | height: 300px; 398 | background-color: #fff; 399 | border-radius: 5px; 400 | position: absolute; 401 | transition: 0.2s linear; 402 | opacity: 0; 403 | z-index: 999; 404 | } 405 | .time-picker .check-box .top-bar { 406 | display: flex; 407 | justify-content: space-between; 408 | align-items: center; 409 | padding: 0 0 0 5px; 410 | } 411 | .time-picker .check-box .top-bar .month, 412 | .time-picker .check-box .top-bar .year { 413 | cursor: pointer; 414 | } 415 | .time-picker .check-box .close-icon { 416 | font-size: 22px; 417 | color: #1890FF; 418 | cursor: pointer; 419 | } 420 | .time-picker .check-box .date-content .week { 421 | margin-top: 5px; 422 | font-weight: 500; 423 | display: flex; 424 | justify-content: space-between; 425 | padding: 0 5px; 426 | } 427 | .time-picker .check-box .date-content .day-list { 428 | display: flex; 429 | flex-wrap: wrap; 430 | line-height: 35px; 431 | font-size: 13px; 432 | } 433 | .time-picker .check-box .date-content .day-list .day { 434 | width: 31.4px; 435 | text-align: center; 436 | cursor: pointer; 437 | transition: 0.3s linear; 438 | } 439 | .time-picker .check-box .date-content .day-list .day:hover { 440 | background-color: #1890FF; 441 | color: #fff; 442 | font-weight: bold; 443 | border-radius: 5px; 444 | } 445 | .time-picker .check-box .date-content .day-list .white { 446 | width: 31.4px; 447 | text-align: center; 448 | cursor: pointer; 449 | transition: 0.3s linear; 450 | } 451 | .time-picker .check-box .date-content .month-toggle-box { 452 | display: flex; 453 | flex-wrap: wrap; 454 | } 455 | .time-picker .check-box .date-content .month-toggle-box .month { 456 | width: 33%; 457 | height: 55px; 458 | text-align: center; 459 | line-height: 55px; 460 | transition: 0.3s linear; 461 | cursor: pointer; 462 | } 463 | .time-picker .check-box .date-content .month-toggle-box .month:hover { 464 | background-color: #1890FF; 465 | color: #fff; 466 | border-radius: 5px; 467 | } 468 | .time-picker .check-box .date-content .year-toggle-box { 469 | font-size: 14px; 470 | } 471 | .time-picker .check-box .date-content .year-toggle-box .toggle-bar { 472 | display: flex; 473 | justify-content: space-between; 474 | margin-top: 10px; 475 | } 476 | .time-picker .check-box .date-content .year-toggle-box .list { 477 | display: flex; 478 | flex-wrap: wrap; 479 | margin-top: 10px; 480 | } 481 | .time-picker .check-box .date-content .year-toggle-box .list .year { 482 | width: 33%; 483 | height: 60px; 484 | text-align: center; 485 | line-height: 60px; 486 | cursor: pointer; 487 | transition: 0.3s linear; 488 | } 489 | .time-picker .check-box .date-content .year-toggle-box .list .year:hover { 490 | background-color: #1890FF; 491 | color: #fff; 492 | border-radius: 5px; 493 | } 494 | .time-picker .check-box .time-footer { 495 | display: flex; 496 | justify-content: space-between; 497 | padding-right: 10px; 498 | position: absolute; 499 | bottom: 20px; 500 | left: 10px; 501 | width: 90%; 502 | } 503 | .time-picker .check-box .time-footer .today { 504 | color: #1890FF; 505 | font-size: 15px; 506 | cursor: pointer; 507 | } 508 | .time-picker .check-box .time-footer .today span { 509 | margin-right: 5px; 510 | } 511 | .time-picker .check-box .time-footer .toggle-month { 512 | color: #1890FF; 513 | font-size: 15px; 514 | cursor: pointer; 515 | } 516 | .time-picker .check-box .time-footer .go-back-icon { 517 | cursor: pointer; 518 | } 519 | .box { 520 | position: relative; 521 | display: flex; 522 | align-items: center; 523 | justify-content: space-between; 524 | background-color: #fff; 525 | } 526 | .box .input { 527 | padding: 4px 11px; 528 | color: #000000d9; 529 | font-size: 14px; 530 | line-height: 1.5715; 531 | background-color: #fff; 532 | border: 1px solid #d9d9d9; 533 | border-radius: 2px; 534 | outline: none; 535 | transition: all 0.3s; 536 | } 537 | .box .input:hover { 538 | border-color: #8fb6d8; 539 | } 540 | .box .input:focus { 541 | border-color: #8fb6d8; 542 | box-shadow: 0px 0px 10px 1px #aed7ec; 543 | } 544 | .box .input .closeIcon { 545 | position: absolute; 546 | } 547 | .box .numTags { 548 | position: absolute; 549 | right: 5px; 550 | display: flex; 551 | flex-direction: column; 552 | color: #bebdbd; 553 | } 554 | .pagination { 555 | display: flex; 556 | } 557 | .prev, 558 | .next, 559 | .numberBox { 560 | min-width: 30px; 561 | height: 30px; 562 | margin: 0 5px; 563 | font-size: 14px; 564 | line-height: 30px; 565 | text-align: center; 566 | border: 1px solid #ccc; 567 | border-radius: 5px; 568 | cursor: pointer; 569 | -moz-user-select: none; 570 | -o-user-select: none; 571 | -khtml-user-select: none; 572 | -webkit-user-select: none; 573 | -ms-user-select: none; 574 | user-select: none; 575 | } 576 | .numberBox:hover { 577 | color: #1890ff; 578 | border: 1px solid #1890ff; 579 | cursor: pointer; 580 | } 581 | .disabled { 582 | color: #c0c4cc; 583 | } 584 | .actived { 585 | color: #1890ff; 586 | border-color: #1890ff; 587 | } 588 | .pageSizeSelect { 589 | width: 80px; 590 | height: 30px; 591 | margin-left: 10px; 592 | padding: 0 5px; 593 | font-size: 14px; 594 | line-height: 30px; 595 | text-align: center; 596 | border: 1px solid #ccc; 597 | cursor: pointer; 598 | transition: 0.2s linear; 599 | -moz-user-select: none; 600 | -o-user-select: none; 601 | -khtml-user-select: none; 602 | -webkit-user-select: none; 603 | -ms-user-select: none; 604 | user-select: none; 605 | } 606 | .pageSizeSelect .size { 607 | margin-right: 5px; 608 | } 609 | .pageSizeSelect .options { 610 | position: relative; 611 | top: 5px; 612 | display: flex; 613 | flex-direction: column; 614 | width: 80px; 615 | box-shadow: 0 0 20px #dadada; 616 | transition: 0.2s linear; 617 | } 618 | .pageSizeSelect .options .option { 619 | font-size: 14px; 620 | line-height: 2em; 621 | text-align: center; 622 | } 623 | .pageSizeSelect .options .option:hover { 624 | color: #1890ff; 625 | } 626 | .pageSizeSelect:hover { 627 | border: 1px solid #1890ff; 628 | } 629 | .jumpBox { 630 | margin-left: 10px; 631 | } 632 | .jumpBox .jump { 633 | width: 40px; 634 | height: 25px; 635 | margin: 0 5px; 636 | border: 1px solid #ccc; 637 | border-radius: 5px; 638 | outline-color: #1890ff; 639 | transition: 0.2s linear; 640 | } 641 | .jumpBox .jump:hover { 642 | border-color: #1890ff; 643 | } 644 | .jumpBox .jump:active { 645 | border-color: #1890ff; 646 | } 647 | .slider { 648 | width: 30%; 649 | background-color: #3ba0e9; 650 | color: #fff; 651 | text-align: center; 652 | padding: 50px 0; 653 | } 654 | .header { 655 | width: 100%; 656 | background-color: #7dbcea; 657 | color: #fff; 658 | text-align: center; 659 | padding: 50px 0; 660 | } 661 | .content { 662 | width: 70%; 663 | background-color: #108EE9; 664 | color: #fff; 665 | text-align: center; 666 | padding: 50px 0; 667 | } 668 | .footer { 669 | width: 100%; 670 | background-color: #7dbcea; 671 | color: #fff; 672 | text-align: center; 673 | padding: 50px 0; 674 | } 675 | .radioGroup { 676 | display: flex; 677 | align-items: center; 678 | height: 32px; 679 | } 680 | .radioGroup .radioBox { 681 | display: inline-block; 682 | height: 32px; 683 | margin: 0 5px; 684 | cursor: pointer; 685 | } 686 | .radioGroup .radioBox .radio { 687 | cursor: pointer !important; 688 | } 689 | .radioGroup .radioBox .disabledRadio { 690 | cursor: not-allowed; 691 | } 692 | .radioGroup .radioBox .radioLabel { 693 | font-size: 14px; 694 | } 695 | .radioGroup .radioBox .disabledLabel { 696 | color: #00000040; 697 | font-size: 14px; 698 | } 699 | .radioGroup .addOption { 700 | display: flex; 701 | flex-direction: row; 702 | } 703 | .radioGroup .groupDisabledStyle { 704 | display: inline-block; 705 | height: 32px; 706 | margin: 0; 707 | padding: 0 20px; 708 | color: #000000d9; 709 | font-size: 14px; 710 | line-height: 30px; 711 | background: #00000040; 712 | border: 1px solid #d9d9d9; 713 | cursor: pointer; 714 | opacity: 0.5; 715 | } 716 | .radioGroup .groupStyle { 717 | display: inline-block; 718 | height: 32px; 719 | margin: 0; 720 | padding: 0 20px; 721 | color: #000000d9; 722 | font-size: 14px; 723 | line-height: 30px; 724 | background: #fff; 725 | border: 1px solid #d9d9d9; 726 | cursor: pointer; 727 | transition: 0.4s linear; 728 | } 729 | .radioGroup .groupStyle:hover { 730 | color: #1890ff; 731 | } 732 | .radioGroup .groupActive { 733 | display: inline-block; 734 | height: 32px; 735 | margin: 0; 736 | padding: 0 20px; 737 | color: #fff; 738 | font-size: 14px; 739 | line-height: 30px; 740 | background: #1890ff; 741 | border: 1px solid #d9d9d9; 742 | cursor: pointer; 743 | transition: 0.4s linear; 744 | } 745 | .radioGroup .groupActive:hover { 746 | opacity: 0.7; 747 | } 748 | .range { 749 | position: relative; 750 | user-select: none; 751 | } 752 | .range .rangePicker { 753 | display: flex; 754 | position: relative; 755 | align-items: center; 756 | } 757 | .range .rangePicker .activeBorder { 758 | position: absolute; 759 | bottom: -2px; 760 | width: 100px; 761 | height: 3px; 762 | background-color: #1890FF; 763 | z-index: 99; 764 | left: 0; 765 | transition: 0.3s linear; 766 | } 767 | .range .date-box { 768 | width: 550px; 769 | height: 300px; 770 | background-color: #fff; 771 | box-shadow: 0px 0px 5px 2px #d0d0d0; 772 | display: flex; 773 | padding-top: 20px; 774 | position: absolute; 775 | opacity: 0; 776 | transition: 0.2s linear; 777 | z-index: 999; 778 | } 779 | .range .date-box .left { 780 | width: 50%; 781 | font-size: 14px; 782 | padding: 0 35px 0 5px; 783 | } 784 | .range .date-box .left .top-bar { 785 | display: flex; 786 | justify-content: space-between; 787 | } 788 | .range .date-box .left .week { 789 | display: flex; 790 | justify-content: space-between; 791 | margin-top: 20px; 792 | padding: 0 25px 0 12px; 793 | } 794 | .range .date-box .left .day-list { 795 | display: flex; 796 | flex-wrap: wrap; 797 | } 798 | .range .date-box .left .day-list .box-list { 799 | width: 35.7px; 800 | height: 40px; 801 | text-align: center; 802 | cursor: pointer; 803 | line-height: 40px; 804 | } 805 | .range .date-box .left .day-list .box-list:hover { 806 | background-color: #1890FF; 807 | color: #fff; 808 | border-radius: 5px; 809 | } 810 | .range .date-box .left .day-list .white { 811 | width: 35.7px; 812 | height: 40px; 813 | text-align: center; 814 | line-height: 40px; 815 | } 816 | .range .date-box .right { 817 | width: 50%; 818 | font-size: 14px; 819 | padding: 0 5px; 820 | } 821 | .range .date-box .right .top-bar { 822 | display: flex; 823 | justify-content: space-between; 824 | } 825 | .range .date-box .right .week { 826 | display: flex; 827 | justify-content: space-between; 828 | margin-top: 20px; 829 | padding: 0 25px 0 12px; 830 | } 831 | .range .date-box .right .day-list { 832 | display: flex; 833 | flex-wrap: wrap; 834 | } 835 | .range .date-box .right .day-list .day-box { 836 | width: 35.7px; 837 | height: 40px; 838 | text-align: center; 839 | cursor: pointer; 840 | line-height: 40px; 841 | } 842 | .range .date-box .right .day-list .day-box:hover { 843 | background-color: #1890FF; 844 | color: #fff; 845 | border-radius: 5px; 846 | } 847 | .range .date-box .right .day-list .disabled-day { 848 | width: 35.7px; 849 | height: 40px; 850 | text-align: center; 851 | line-height: 40px; 852 | color: #ccc; 853 | cursor: not-allowed; 854 | } 855 | .range .date-box .right .day-list .white { 856 | width: 35.7px; 857 | height: 40px; 858 | text-align: center; 859 | line-height: 40px; 860 | } 861 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | hero: 3 | title: React View UI 4 | desc: 一套易用的轻量级的React UI 组件库 5 | actions: 6 | - text: Getting Started 7 | link: /common/button 8 | features: 9 | - icon: https://gw.alipayobjects.com/zos/bmw-prod/881dc458-f20b-407b-947a-95104b5ec82b/k79dm8ih_w144_h144.png 10 | title: 开箱即用 11 | desc: 简单易用,降低使用者的代码量 12 | - icon: https://gw.alipayobjects.com/zos/bmw-prod/d60657df-0822-4631-9d7c-e7a869c2f21c/k79dmz3q_w126_h126.png 13 | title: 为组件化开发而生 14 | desc: 结合MVVM组件化开发思想 15 | - icon: https://gw.alipayobjects.com/zos/bmw-prod/d1ee0c6f-5aed-4a45-a507-339a4bfe076c/k7bjsocq_w144_h144.png 16 | title: 多方支持 17 | desc: 支持React / React Native 18 | footer: Open-source MIT Licensed | Copyright © 2020
Powered by [Xin Feng](https://github.com/fengxinhhh/React-View-UI-fs) 19 | --- 20 | 21 | ## 快速网站成型工具 22 | 23 | ##### 易用于多端开发 24 | -------------------------------------------------------------------------------- /lib/index.css: -------------------------------------------------------------------------------- 1 | .button .primary { 2 | display: flex; 3 | align-items: center; 4 | justify-content: center; 5 | color: #fff; 6 | background-color: #1890ff; 7 | border: none; 8 | border-radius: 10px; 9 | cursor: pointer; 10 | transition: 0.2s linear; 11 | } 12 | .button .primary:hover { 13 | opacity: 0.7; 14 | } 15 | .button .danger { 16 | display: flex; 17 | align-items: center; 18 | justify-content: center; 19 | color: #fff; 20 | background-color: brown; 21 | border: none; 22 | border-radius: 10px; 23 | cursor: pointer; 24 | transition: 0.2s linear; 25 | } 26 | .button .danger:hover { 27 | opacity: 0.7; 28 | } 29 | .button .warning { 30 | display: flex; 31 | align-items: center; 32 | justify-content: center; 33 | color: #fff; 34 | background-color: #e0c529; 35 | border: none; 36 | border-radius: 10px; 37 | cursor: pointer; 38 | transition: 0.2s linear; 39 | } 40 | .button .warning:hover { 41 | opacity: 0.7; 42 | } 43 | .button .text { 44 | display: flex; 45 | align-items: center; 46 | justify-content: center; 47 | color: #000000; 48 | background-color: #fff; 49 | border: none; 50 | border: 1px solid #ccc; 51 | border-radius: 10px; 52 | cursor: pointer; 53 | transition: 0.2s linear; 54 | } 55 | .button .text:hover { 56 | color: #1890ff; 57 | border-color: #1890ff !important; 58 | } 59 | .button .loading1 { 60 | width: 10px; 61 | height: 10px; 62 | margin-right: 10px; 63 | border: 2px solid #ccc; 64 | border-color: #ccc #eee #e2e2e2 #6d6c6c; 65 | border-radius: 50%; 66 | animation: 1s loadingWatch infinite linear; 67 | } 68 | @keyframes loadingWatch { 69 | 0% { 70 | transform: rotate(0deg); 71 | } 72 | 50% { 73 | transform: rotate(100deg); 74 | } 75 | 75% { 76 | transform: rotate(200deg); 77 | } 78 | 100% { 79 | transform: rotate(360deg); 80 | } 81 | } 82 | .content { 83 | width: 70%; 84 | background-color: #108EE9; 85 | color: #fff; 86 | text-align: center; 87 | padding: 50px 0; 88 | } 89 | .slider { 90 | width: 30%; 91 | background-color: #3ba0e9; 92 | color: #fff; 93 | text-align: center; 94 | padding: 50px 0; 95 | } 96 | .layout { 97 | width: 100%; 98 | display: flex; 99 | flex-wrap: wrap; 100 | justify-content: space-between; 101 | } 102 | .header { 103 | width: 100%; 104 | background-color: #7dbcea; 105 | color: #fff; 106 | text-align: center; 107 | padding: 50px 0; 108 | } 109 | .divider .line { 110 | position: relative; 111 | display: flex; 112 | align-items: center; 113 | justify-content: center; 114 | width: 100%; 115 | height: 1px; 116 | border-top: 1px solid #ccc; 117 | } 118 | .divider .line .line-text { 119 | position: absolute; 120 | padding: 0 10px; 121 | background-color: #fff; 122 | } 123 | .divider .dashed { 124 | position: relative; 125 | display: flex; 126 | align-items: center; 127 | justify-content: center; 128 | width: 100%; 129 | height: 1px; 130 | border-top: 1px dashed #ccc; 131 | } 132 | .divider .dashed .line-text { 133 | position: absolute; 134 | padding: 0 10px; 135 | background-color: #fff; 136 | } 137 | .footer { 138 | width: 100%; 139 | background-color: #7dbcea; 140 | color: #fff; 141 | text-align: center; 142 | padding: 50px 0; 143 | } 144 | .select { 145 | position: relative; 146 | width: 80px; 147 | height: 30px; 148 | margin-left: 10px; 149 | padding: 0 2px; 150 | font-size: 14px; 151 | line-height: 30px; 152 | text-align: center; 153 | border: 1px solid #ccc; 154 | cursor: pointer; 155 | transition: 0.2s linear; 156 | -moz-user-select: none; 157 | -o-user-select: none; 158 | -khtml-user-select: none; 159 | -webkit-user-select: none; 160 | -ms-user-select: none; 161 | user-select: none; 162 | } 163 | .select .selected { 164 | display: flex; 165 | align-items: center; 166 | justify-content: space-between; 167 | height: 100%; 168 | padding: 0 5px; 169 | } 170 | .select .selected .size { 171 | margin-right: 5px; 172 | } 173 | .select .selected .placeholder { 174 | margin-right: 5px; 175 | color: #ccc; 176 | } 177 | .select .selected:focus { 178 | border-color: #1890ff; 179 | } 180 | .select:hover { 181 | border-color: #1890ff; 182 | } 183 | .select input { 184 | border: none; 185 | outline: none; 186 | } 187 | .select .selectOptions { 188 | position: absolute; 189 | top: 35px; 190 | z-index: 500; 191 | display: flex; 192 | flex-direction: column; 193 | width: 80px; 194 | height: 0px; 195 | overflow: hidden; 196 | box-shadow: 0 3px 10px #8c8c8c; 197 | transition: 0.1s linear; 198 | } 199 | .select .selectOptions .option { 200 | font-size: 14px; 201 | line-height: 2em; 202 | text-align: center; 203 | cursor: pointer; 204 | } 205 | .select .selectOptions .option:hover { 206 | color: #1890ff; 207 | } 208 | .selectOptions { 209 | position: absolute; 210 | top: 35px; 211 | z-index: 500; 212 | display: flex; 213 | flex-direction: column; 214 | width: 80px; 215 | height: 0px; 216 | overflow: hidden; 217 | box-shadow: 0 3px 10px #8c8c8c; 218 | transition: 0.1s linear; 219 | } 220 | .selectOptions .option { 221 | font-size: 14px; 222 | line-height: 2em; 223 | text-align: center; 224 | cursor: pointer; 225 | } 226 | .selectOptions .option:hover { 227 | color: #1890ff; 228 | } 229 | .box { 230 | position: relative; 231 | display: flex; 232 | align-items: center; 233 | justify-content: space-between; 234 | background-color: #fff; 235 | } 236 | .box .input { 237 | padding: 4px 11px; 238 | color: #000000d9; 239 | font-size: 14px; 240 | line-height: 1.5715; 241 | background-color: #fff; 242 | border: 1px solid #d9d9d9; 243 | border-radius: 2px; 244 | outline: none; 245 | transition: all 0.3s; 246 | } 247 | .box .input:hover { 248 | border-color: #8fb6d8; 249 | } 250 | .box .input:focus { 251 | border-color: #8fb6d8; 252 | box-shadow: 0px 0px 10px 1px #aed7ec; 253 | } 254 | .box .input .closeIcon { 255 | position: absolute; 256 | } 257 | .box .numTags { 258 | position: absolute; 259 | right: 5px; 260 | display: flex; 261 | flex-direction: column; 262 | color: #bebdbd; 263 | } 264 | .menu { 265 | width: 220px; 266 | padding: 2px 0px; 267 | color: black; 268 | -moz-user-select: none; 269 | -webkit-user-select: none; 270 | user-select: none; 271 | } 272 | .menu .menuOptions { 273 | padding: 0 10px; 274 | overflow: hidden; 275 | font-size: 14px; 276 | line-height: 50px; 277 | cursor: pointer; 278 | transition: 0.2s linear; 279 | } 280 | .menu .menuOptions .fatherTitle { 281 | display: flex; 282 | align-items: center; 283 | justify-content: space-between; 284 | height: 50px; 285 | } 286 | .menu .menuOptions .fatherTitle:hover { 287 | color: #1890ff; 288 | } 289 | .menu .menuOptions .activeFatherTitle { 290 | display: flex; 291 | align-items: center; 292 | justify-content: space-between; 293 | height: 50px; 294 | color: #1890ff; 295 | } 296 | .menu .menuOptions .activeFatherTitle:hover { 297 | color: #1890ff; 298 | } 299 | .menu .menuOptions .left i { 300 | margin-right: 10px; 301 | } 302 | .menu .activeMenuOptions { 303 | display: flex; 304 | flex-direction: column; 305 | box-sizing: border-box; 306 | padding-left: 5px; 307 | overflow: hidden; 308 | color: #1890ff; 309 | font-size: 14px; 310 | line-height: 50px; 311 | background-color: #e6f7ff; 312 | cursor: pointer; 313 | transition: 0.2s linear; 314 | } 315 | .menu .activeMenuOptions :after { 316 | width: 3px; 317 | height: 50px; 318 | background-color: #1890ff; 319 | content: ''; 320 | } 321 | .menu .childMenuOptions { 322 | display: flex; 323 | flex-direction: column; 324 | box-sizing: border-box; 325 | padding-left: 5px; 326 | overflow: hidden; 327 | font-size: 14px; 328 | line-height: 50px; 329 | cursor: pointer; 330 | transition: 0.2s linear; 331 | } 332 | .darkMenu { 333 | box-sizing: border-box; 334 | width: 200px; 335 | padding: 2px 0px; 336 | color: #ffffffa6; 337 | background-color: #001529; 338 | -moz-user-select: none; 339 | -webkit-user-select: none; 340 | user-select: none; 341 | } 342 | .darkMenu .menuOptions { 343 | padding: 0 10px; 344 | overflow: hidden; 345 | font-size: 14px; 346 | line-height: 50px; 347 | cursor: pointer; 348 | transition: 0.2s linear; 349 | } 350 | .darkMenu .menuOptions .fatherTitle { 351 | display: flex; 352 | align-items: center; 353 | justify-content: space-between; 354 | height: 50px; 355 | } 356 | .darkMenu .menuOptions .fatherTitle:hover { 357 | color: #fff; 358 | } 359 | .darkMenu .menuOptions .activeFatherTitle { 360 | display: flex; 361 | align-items: center; 362 | justify-content: space-between; 363 | box-sizing: border-box; 364 | height: 50px; 365 | color: #fff; 366 | } 367 | .darkMenu .menuOptions .activeFatherTitle:hover { 368 | color: #fff; 369 | } 370 | .darkMenu .menuOptions .left i { 371 | margin-right: 10px; 372 | } 373 | .darkMenu .activeMenuOptions { 374 | position: relative; 375 | display: flex; 376 | flex-direction: column; 377 | box-sizing: border-box; 378 | padding-left: 5px; 379 | overflow: hidden; 380 | color: #fff; 381 | font-size: 14px; 382 | line-height: 50px; 383 | background-color: #1890ff; 384 | cursor: pointer; 385 | transition: 0.2s linear; 386 | } 387 | .darkMenu .childMenuOptions { 388 | position: relative; 389 | display: flex; 390 | flex-direction: column; 391 | box-sizing: border-box; 392 | padding-left: 5px; 393 | overflow: hidden; 394 | font-size: 14px; 395 | line-height: 50px; 396 | cursor: pointer; 397 | transition: 0.2s linear; 398 | } 399 | .pagination { 400 | display: flex; 401 | } 402 | .prev, 403 | .next, 404 | .numberBox { 405 | min-width: 30px; 406 | height: 30px; 407 | margin: 0 5px; 408 | font-size: 14px; 409 | line-height: 30px; 410 | text-align: center; 411 | border: 1px solid #ccc; 412 | border-radius: 5px; 413 | cursor: pointer; 414 | -moz-user-select: none; 415 | -o-user-select: none; 416 | -khtml-user-select: none; 417 | -webkit-user-select: none; 418 | -ms-user-select: none; 419 | user-select: none; 420 | } 421 | .numberBox:hover { 422 | color: #1890ff; 423 | border: 1px solid #1890ff; 424 | cursor: pointer; 425 | } 426 | .disabled { 427 | color: #c0c4cc; 428 | } 429 | .actived { 430 | color: #1890ff; 431 | border-color: #1890ff; 432 | } 433 | .pageSizeSelect { 434 | width: 80px; 435 | height: 30px; 436 | margin-left: 10px; 437 | padding: 0 5px; 438 | font-size: 14px; 439 | line-height: 30px; 440 | text-align: center; 441 | border: 1px solid #ccc; 442 | cursor: pointer; 443 | transition: 0.2s linear; 444 | -moz-user-select: none; 445 | -o-user-select: none; 446 | -khtml-user-select: none; 447 | -webkit-user-select: none; 448 | -ms-user-select: none; 449 | user-select: none; 450 | } 451 | .pageSizeSelect .size { 452 | margin-right: 5px; 453 | } 454 | .pageSizeSelect .options { 455 | position: relative; 456 | top: 5px; 457 | display: flex; 458 | flex-direction: column; 459 | width: 80px; 460 | box-shadow: 0 0 20px #dadada; 461 | transition: 0.2s linear; 462 | } 463 | .pageSizeSelect .options .option { 464 | font-size: 14px; 465 | line-height: 2em; 466 | text-align: center; 467 | } 468 | .pageSizeSelect .options .option:hover { 469 | color: #1890ff; 470 | } 471 | .pageSizeSelect:hover { 472 | border: 1px solid #1890ff; 473 | } 474 | .jumpBox { 475 | margin-left: 10px; 476 | } 477 | .jumpBox .jump { 478 | width: 40px; 479 | height: 25px; 480 | margin: 0 5px; 481 | border: 1px solid #ccc; 482 | border-radius: 5px; 483 | outline-color: #1890ff; 484 | transition: 0.2s linear; 485 | } 486 | .jumpBox .jump:hover { 487 | border-color: #1890ff; 488 | } 489 | .jumpBox .jump:active { 490 | border-color: #1890ff; 491 | } 492 | .radioGroup { 493 | display: flex; 494 | align-items: center; 495 | height: 32px; 496 | } 497 | .radioGroup .radioBox { 498 | display: inline-block; 499 | height: 32px; 500 | margin: 0 5px; 501 | cursor: pointer; 502 | } 503 | .radioGroup .radioBox .radio { 504 | cursor: pointer !important; 505 | } 506 | .radioGroup .radioBox .disabledRadio { 507 | cursor: not-allowed; 508 | } 509 | .radioGroup .radioBox .radioLabel { 510 | font-size: 14px; 511 | } 512 | .radioGroup .radioBox .disabledLabel { 513 | color: #00000040; 514 | font-size: 14px; 515 | } 516 | .radioGroup .addOption { 517 | display: flex; 518 | flex-direction: row; 519 | } 520 | .radioGroup .groupDisabledStyle { 521 | display: inline-block; 522 | height: 32px; 523 | margin: 0; 524 | padding: 0 20px; 525 | color: #000000d9; 526 | font-size: 14px; 527 | line-height: 30px; 528 | background: #00000040; 529 | border: 1px solid #d9d9d9; 530 | cursor: pointer; 531 | opacity: 0.5; 532 | } 533 | .radioGroup .groupStyle { 534 | display: inline-block; 535 | height: 32px; 536 | margin: 0; 537 | padding: 0 20px; 538 | color: #000000d9; 539 | font-size: 14px; 540 | line-height: 30px; 541 | background: #fff; 542 | border: 1px solid #d9d9d9; 543 | cursor: pointer; 544 | transition: 0.4s linear; 545 | } 546 | .radioGroup .groupStyle:hover { 547 | color: #1890ff; 548 | } 549 | .radioGroup .groupActive { 550 | display: inline-block; 551 | height: 32px; 552 | margin: 0; 553 | padding: 0 20px; 554 | color: #fff; 555 | font-size: 14px; 556 | line-height: 30px; 557 | background: #1890ff; 558 | border: 1px solid #d9d9d9; 559 | cursor: pointer; 560 | transition: 0.4s linear; 561 | } 562 | .radioGroup .groupActive:hover { 563 | opacity: 0.7; 564 | } 565 | -------------------------------------------------------------------------------- /lib/umd/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"} -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-view-ui", 3 | "version": "1.3.2", 4 | "scripts": { 5 | "start": "dumi dev", 6 | "docs:build": "dumi build", 7 | "docs:deploy": "gh-pages -d docs-dist", 8 | "build": "father-build", 9 | "prettier": "prettier --write \"**/*.{js,jsx,tsx,ts,less,md,json}\"", 10 | "test": "umi-test", 11 | "test:coverage": "umi-test --coverage", 12 | "prepublishOnly": "npm run haha", 13 | "deploy": "gh-pages -d docs-dist", 14 | "haha": "rollup -c rollup.config.js" 15 | }, 16 | "module": "es/index.js", 17 | "typings": "es/index.d.ts", 18 | "gitHooks": { 19 | "pre-commit": "lint-staged" 20 | }, 21 | "files": [ 22 | "dist" 23 | ], 24 | "lint-staged": { 25 | "*.{js,jsx,less,md,json}": [ 26 | "prettier --write" 27 | ], 28 | "*.ts?(x)": [ 29 | "prettier --parser=typescript --write" 30 | ] 31 | }, 32 | "dependencies": { 33 | "@ant-design/icons": "^4.7.0", 34 | "@babel/plugin-transform-runtime": "^7.17.0", 35 | "@rollup/plugin-commonjs": "^21.1.0", 36 | "@rollup/plugin-node-resolve": "^13.2.1", 37 | "@rollup/plugin-typescript": "^8.3.2", 38 | "antd": "^4.19.5", 39 | "core-js": "^3.22.2", 40 | "lodash": "^4.17.21", 41 | "react": "^16.12.0 || ^17.0.0", 42 | "react-view-ui": "^1.2.16", 43 | "rollup": "^2.70.2", 44 | "rollup-plugin-clear": "^2.0.7", 45 | "rollup-plugin-commonjs": "^10.1.0", 46 | "rollup-plugin-delete": "^2.0.0", 47 | "rollup-plugin-eslint": "^7.0.0", 48 | "rollup-plugin-less": "^1.1.3", 49 | "rollup-plugin-node-resolve": "^5.2.0", 50 | "rollup-plugin-terser": "^7.0.2", 51 | "rollup-plugin-typescript2": "^0.31.2" 52 | }, 53 | "devDependencies": { 54 | "@babel/core": "^7.17.9", 55 | "@babel/preset-env": "^7.16.11", 56 | "@rollup/plugin-json": "^4.1.0", 57 | "@testing-library/jest-dom": "^5.15.1", 58 | "@testing-library/react": "^12.1.2", 59 | "@types/jest": "^27.0.3", 60 | "@umijs/fabric": "^2.8.1", 61 | "@umijs/test": "^3.0.5", 62 | "dumi": "^1.1.40", 63 | "father-build": "^1.17.2", 64 | "gh-pages": "^3.2.3", 65 | "lint-staged": "^10.0.7", 66 | "prettier": "^2.2.1", 67 | "rollup-plugin-babel": "^4.4.0", 68 | "rollup-plugin-postcss": "^4.0.2", 69 | "yorkie": "^2.0.0" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import typescript from 'rollup-plugin-typescript2'; 2 | import less from 'rollup-plugin-less'; 3 | import clear from 'rollup-plugin-clear'; 4 | import resolve from 'rollup-plugin-node-resolve'; 5 | import commonjs from 'rollup-plugin-commonjs'; 6 | import babel from 'rollup-plugin-babel'; 7 | import { terser } from 'rollup-plugin-terser'; 8 | 9 | export default { 10 | input: ['./src/index.ts'], 11 | output: [ 12 | { 13 | file: 'dist/my-lib-cjs.js', 14 | format: 'cjs', 15 | name: 'my-lib-cjs.js', 16 | }, 17 | { 18 | file: 'dist/my-lib-umd.js', 19 | format: 'umd', 20 | name: 'my-lib-umd.js', 21 | }, 22 | { 23 | file: 'dist/my-lib-esm.js', 24 | format: 'es', 25 | name: 'my-lib-esm.js', 26 | }, 27 | ], 28 | plugins: [ 29 | typescript(), // 会自动读取 文件tsconfig.json配置 30 | less({ output: './dist/index.css' }), 31 | clear({ 32 | targets: ['dist'], 33 | }), 34 | resolve(), // 这样 Rollup 能找到 `ms` 35 | commonjs(), // 这样 Rollup 能转换 `ms` 为一个ES模块 36 | babel({ 37 | exclude: 'node_modules/**', // 防止打包node_modules下的文件 38 | runtimeHelpers: true, // 使plugin-transform-runtime生效 39 | }), 40 | terser(), 41 | ], 42 | external: ['react', 'react-dom'], 43 | }; 44 | -------------------------------------------------------------------------------- /src/Affix/demos/index1.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import Affix from '../index'; 3 | import Button from '../../Button'; 4 | 5 | export default function AffixDemo1() { 6 | const [align, setAlign] = useState('top'); 7 | return ( 8 |
9 | {align == 'top' && ( 10 | 11 |
top Affix
12 |
13 | )} 14 | {align == 'bottom' && ( 15 | 21 |
bottom Affix
22 |
23 | )} 24 |
25 | 28 | 29 |

Affix

30 |

Affix

31 |

Affix

32 |

Affix

33 |

Affix

34 |

Affix

35 |

Affix

36 |

Affix

37 |

Affix

38 |

Affix

39 |

Affix

40 |

Affix

41 |

Affix

42 |

Affix

43 |

Affix

44 |

Affix

45 |

Affix

46 |

Affix

47 |

Affix

48 |

Affix

49 |

Affix

50 |

Affix

51 |

Affix

52 |

Affix

53 |
54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /src/Affix/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Affix 固钉 3 | nav: 4 | title: 通用 5 | path: /common 6 | group: 7 | path: /common 8 | --- 9 | 10 | # Affix 固钉 11 | 12 | 将页面元素钉在可视范围。 13 | 14 | ## 何时使用 15 | 16 | - 当内容区域比较长,需要滚动页面时,这部分内容对应的操作或者导航需要在滚动范围内始终展现。常用于侧边菜单和按钮组合。 17 | 18 | - 页面可视范围过小时,慎用此功能以免遮挡页面内容。 19 | 20 | ## 滚动容器 21 | 22 | - 在首屏时保持元素位置,在滚动后脱离文档流至指定位置 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/Affix/index.module.less: -------------------------------------------------------------------------------- 1 | .affix { 2 | position: relative; 3 | z-index: 99; 4 | display: inline-block; 5 | color: #fff; 6 | background-color: #1890ff; 7 | } 8 | -------------------------------------------------------------------------------- /src/Affix/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, memo, useEffect, useState } from 'react'; 2 | import './index.module.less'; 3 | 4 | interface AffixProps { 5 | /** 6 | * @description 类型 scroll表示滚动容器 7 | * @default '' 8 | */ 9 | affixType?: string; 10 | /** 11 | * @description 插槽 12 | */ 13 | children?: any; 14 | /** 15 | * @description 顶部距离 16 | * @default 0 17 | */ 18 | offsetTop?: number | string; 19 | /** 20 | * @description 左侧距离 21 | * @default 0 22 | */ 23 | offsetLeft?: number | string; 24 | /** 25 | * @description 底部距离 26 | * @default 0 27 | */ 28 | offsetBottom?: number | string; 29 | /** 30 | * @description 右侧距离 31 | * @default 0 32 | */ 33 | offsetRight?: number | string; 34 | /** 35 | * @description 插槽样式 36 | * @default {} 37 | */ 38 | style?: Object; 39 | } 40 | interface offsetProps { 41 | left?: number | string; 42 | right?: number | string; 43 | top?: number | string; 44 | bottom?: number | string; 45 | position?: string; 46 | } 47 | const Affix: FC = (props) => { 48 | const { children, affixType, offsetTop, offsetLeft, offsetBottom, offsetRight, style } = props; 49 | 50 | const [affixElOffset, setAffixElOffset] = useState({}); 51 | let io: IntersectionObserver; //观察者 52 | 53 | useEffect(() => { 54 | const el = document.querySelector('.affix') as Element; 55 | io = new IntersectionObserver((entries) => elementObverse(entries)); 56 | io.observe(el); //数据劫持监听 57 | if (affixType == 'scroll') { 58 | window.addEventListener('scroll', screenScroll); 59 | setAffixElOffset((old) => { 60 | old.position = 'relative'; 61 | if (offsetTop && !old.bottom) { 62 | old.top = 0; 63 | } 64 | if (offsetBottom && !old.top) { 65 | old.bottom = 0; 66 | } 67 | if (offsetLeft && !old.right) { 68 | old.left = 0; 69 | } 70 | if (offsetRight && !old.left) { 71 | old.right = 0; 72 | } 73 | return JSON.parse(JSON.stringify(old)); 74 | }); 75 | } else { 76 | setAffixElOffset((old) => { 77 | old.position = 'fixed'; 78 | if (offsetTop && !old.bottom) { 79 | old.top = offsetTop; 80 | } 81 | if (offsetBottom && !old.top) { 82 | old.bottom = offsetBottom; 83 | } 84 | if (offsetLeft && !old.right) { 85 | old.left = offsetLeft; 86 | } 87 | if (offsetRight && !old.left) { 88 | old.right = offsetRight; 89 | } 90 | return JSON.parse(JSON.stringify(old)); 91 | }); 92 | } 93 | return () => { 94 | io.unobserve(el); 95 | }; 96 | }, []); 97 | const screenScroll = () => { 98 | const el: any = document.querySelector('.affix'); 99 | if (window.scrollY < 200) { 100 | //首屏时,无需脱离文档流 101 | setAffixElOffset((old: offsetProps) => { 102 | for (const key in old as offsetProps) { 103 | if (key == 'position') { 104 | old[key] = 'relative'; 105 | } else if (key == 'right' || key == 'left' || key == 'top' || key == 'bottom') { 106 | old[key] = '0'; 107 | } 108 | } 109 | return JSON.parse(JSON.stringify(old)); 110 | }); 111 | } 112 | }; 113 | const elementObverse = (entries: Array) => { 114 | //监听函数 115 | entries.forEach((entry: IntersectionObserverEntry) => { 116 | if (!entry.isIntersecting) { 117 | // 元素未被观测 118 | if (affixElOffset.position == 'relative') { 119 | setAffixElOffset((old) => { 120 | old.position = 'fixed'; 121 | if (offsetTop && offsetTop !== old.top) { 122 | old.top = `${offsetTop}px`; 123 | } 124 | if (offsetBottom && offsetBottom !== old.bottom) { 125 | old.bottom = `${offsetBottom}px`; 126 | } 127 | if (offsetLeft && offsetLeft !== old.left) { 128 | old.left = `${offsetLeft}px`; 129 | } 130 | if (offsetRight && offsetRight !== old.right) { 131 | old.right = `${offsetRight}px`; 132 | } 133 | return JSON.parse(JSON.stringify(old)); 134 | }); 135 | } 136 | } 137 | }); 138 | }; 139 | 140 | return ( 141 |
), ...style }} 144 | > 145 | {children} 146 |
147 | ); 148 | }; 149 | 150 | export default memo(Affix); 151 | -------------------------------------------------------------------------------- /src/Button/demos/index1.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Button from '..'; 3 | 4 | export default function ButtonDemo1() { 5 | return ( 6 |
7 | 8 | 9 | 10 | 11 |
12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /src/Button/demos/index2.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Button from '..'; 3 | 4 | export default function ButtonDemo2() { 5 | return ( 6 |
7 | 10 | 13 | 16 |
17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /src/Button/demos/index3.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Button from '..'; 3 | 4 | export default function ButtonDemo3() { 5 | return ( 6 |
7 | 10 | 13 | 16 | 19 |
20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /src/Button/demos/index4.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Button from '..'; 3 | 4 | export default function ButtonDemo4() { 5 | return ( 6 |
7 | 10 | 13 | 16 | 19 |
20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /src/Button/demos/index5.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Button from '..'; 3 | 4 | export default function ButtonDemo5() { 5 | return ( 6 |
7 | 10 | 13 | 16 | 19 |
20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /src/Button/demos/index6.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Button from '..'; 3 | 4 | export default function ButtonDemo6() { 5 | return ( 6 |
7 | 10 | 13 | 16 | 19 |
20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /src/Button/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Button 按钮 3 | nav: 4 | title: 通用 5 | path: /common 6 | group: 7 | path: /common 8 | --- 9 | 10 | # Button 按钮 11 | 12 | 按钮用于开始一个即时操作。 13 | 14 | #### 何时使用 15 | 16 |

标记了一个(或封装一组)操作命令,响应用户点击行为,触发相应的业务逻辑。

17 |

18 |

在 React View UI 中我们提供了六种按钮。

19 | 20 | - 状态颜色改变的主题按钮 21 | - 不同尺寸的按钮 22 | - 圆形字体按钮 23 | - 禁用状态按钮 24 | - 虚线按钮 25 | - 加载状态按钮 26 | 27 | ## 基本使用 28 | 29 | 30 | 31 | ## 不同尺寸 32 | 33 | 34 | 35 | ## 字体按钮 36 | 37 | 38 | 39 | ## 禁用按钮 40 | 41 | 42 | 43 | ## 虚线按钮 44 | 45 |

只有文本按钮(type=text)支持虚线样式

46 | 47 | 48 | 49 | ## 加载状态 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/Button/index.module.less: -------------------------------------------------------------------------------- 1 | .button { 2 | .primary { 3 | display: flex; 4 | align-items: center; 5 | justify-content: center; 6 | color: #fff; 7 | background-color: #1890ff; 8 | border: none; 9 | border-radius: 10px; 10 | cursor: pointer; 11 | transition: 0.2s linear; 12 | &:hover { 13 | opacity: 0.7; 14 | } 15 | } 16 | .danger { 17 | display: flex; 18 | align-items: center; 19 | justify-content: center; 20 | color: #fff; 21 | background-color: brown; 22 | border: none; 23 | border-radius: 10px; 24 | cursor: pointer; 25 | transition: 0.2s linear; 26 | &:hover { 27 | opacity: 0.7; 28 | } 29 | } 30 | .warning { 31 | display: flex; 32 | align-items: center; 33 | justify-content: center; 34 | color: #fff; 35 | background-color: rgb(224, 197, 41); 36 | border: none; 37 | border-radius: 10px; 38 | cursor: pointer; 39 | transition: 0.2s linear; 40 | &:hover { 41 | opacity: 0.7; 42 | } 43 | } 44 | .text { 45 | display: flex; 46 | align-items: center; 47 | justify-content: center; 48 | color: #000000; 49 | background-color: #fff; 50 | border: none; 51 | border: 1px solid #ccc; 52 | border-radius: 10px; 53 | cursor: pointer; 54 | transition: 0.2s linear; 55 | &:hover { 56 | color: #1890ff; 57 | border-color: #1890ff !important; 58 | } 59 | } 60 | .loading1 { 61 | width: 10px; 62 | height: 10px; 63 | margin-right: 10px; 64 | border: 2px solid #ccc; 65 | border-color: #ccc #eee rgb(226, 226, 226) rgb(109, 108, 108); 66 | border-radius: 50%; 67 | animation: 1s loadingWatch infinite linear; 68 | } 69 | @keyframes loadingWatch { 70 | 0% { 71 | transform: rotate(0deg); 72 | } 73 | 50% { 74 | transform: rotate(100deg); 75 | } 76 | 75% { 77 | transform: rotate(200deg); 78 | } 79 | 100% { 80 | transform: rotate(360deg); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Button/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useMemo, FC, memo } from 'react'; 2 | import './index.module.less'; 3 | 4 | interface ButtonProps { 5 | //自定义button接口 6 | /** 7 | * @description 按钮主题 8 | * @default primary 9 | */ 10 | type?: String; 11 | /** 12 | * @description 宽度 13 | */ 14 | width?: Number; 15 | /** 16 | * @description 高度 17 | */ 18 | height?: Number; 19 | /** 20 | * @description 禁用状态 21 | * @default false 22 | */ 23 | disabled?: Boolean; 24 | /** 25 | * @description 字体按钮 26 | * @default false 27 | */ 28 | circle?: Boolean; 29 | /** 30 | * @description 按钮边框为虚线 31 | * @default false 32 | */ 33 | dashed?: Boolean; 34 | /** 35 | * @description 加载状态 36 | * @default false 37 | */ 38 | loading?: Boolean; 39 | /** 40 | * @description 按钮点击回调事件 41 | */ 42 | handleClick?: Function | undefined; 43 | } 44 | interface ButtonStyle { 45 | //button样式接口 46 | width?: String; 47 | height?: String; 48 | borderRadius?: String; 49 | border?: String; 50 | cursor?: String; 51 | } 52 | type NativeButtonProps = Omit, 'type'>; //原生button接口 53 | 54 | const Button: FC = memo((props) => { 55 | const { type, width, height, disabled, circle, dashed, loading, handleClick, children } = props; 56 | 57 | const buttonStyle = useMemo(() => { 58 | if (!type && type !== 'danger' && type !== 'warning' && type !== 'warning' && type !== 'text') { 59 | return 'primary'; 60 | } 61 | return type as any; 62 | }, [type]); 63 | const buttonSize = useMemo(() => { 64 | var size: ButtonStyle = { 65 | width: '100px', 66 | height: '40px', 67 | }; 68 | if (width) { 69 | size.width = width + 'px'; 70 | } 71 | if (height) { 72 | size.height = height + 'px'; 73 | } 74 | if (circle) { 75 | size = { ...size, borderRadius: '50%' }; 76 | } 77 | if (dashed && type === 'text') { 78 | size = { ...size, border: '1px dashed #ccc' }; 79 | } 80 | if (disabled) { 81 | size = { ...size, cursor: 'not-allowed' }; 82 | } 83 | return size; 84 | }, [width, height, circle, dashed]); 85 | return ( 86 |
87 | 96 |
97 | ); 98 | }); 99 | 100 | export default Button; 101 | -------------------------------------------------------------------------------- /src/DatePicker/demos/index1.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import TimePicker from '..'; 3 | 4 | export default function TimePickerDemo1() { 5 | const handleChange = (date: string) => { 6 | console.log(date); 7 | }; 8 | return ; 9 | } 10 | -------------------------------------------------------------------------------- /src/DatePicker/demos/index2.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import TimePicker from '..'; 3 | 4 | export default function TimePickerDemo2() { 5 | const handleChange = (date: string) => { 6 | console.log(date); 7 | }; 8 | return ; 9 | } 10 | -------------------------------------------------------------------------------- /src/DatePicker/demos/index3.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import TimePicker from '..'; 3 | 4 | export default function TimePickerDemo3() { 5 | const handleChange = (start: string, end: string) => { 6 | console.log(start, end); 7 | }; 8 | return ; 9 | } 10 | -------------------------------------------------------------------------------- /src/DatePicker/demos/index4.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import TimePicker from '..'; 3 | 4 | export default function TimePickerDemo3() { 5 | const handleChange = (start: string, end: string) => { 6 | console.log(start, end); 7 | }; 8 | return ; 9 | } 10 | -------------------------------------------------------------------------------- /src/DatePicker/demos/index5.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import TimePicker from '..'; 3 | 4 | export default function TimePickerDemo5() { 5 | const handleChange = (start: string, end: string) => { 6 | console.log(start, end); 7 | }; 8 | return ( 9 | 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /src/DatePicker/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: DatePicker 日期选择器 3 | nav: 4 | title: 通用 5 | path: /common 6 | group: 7 | path: /common 8 | --- 9 | 10 | # DatePicker 日期选择器 11 | 12 | 输入或选择日期的控件。 13 | 14 | ## 何时使用 15 | 16 | - 当用户需要输入一个日期,可以点击标准输入框,弹出日期面板进行选择。 17 | 18 | ## 基本使用 19 | 20 | 21 | 22 | ## 输入式日期选择器 23 | 24 | 25 | 26 | ## 区间式日期选择器 27 | 28 | 29 | 30 | ## 重置时间 31 | 32 | 33 | 34 | ## 出现方向 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/DatePicker/index.module.less: -------------------------------------------------------------------------------- 1 | .time-picker { 2 | position: relative; 3 | .result { 4 | display: flex; 5 | justify-content: center; 6 | width: 150px; 7 | color: #000000; 8 | font-weight: 700; 9 | letter-spacing: 1px; 10 | cursor: pointer; 11 | transition: 0.3s linear; 12 | &:hover { 13 | width: 120px; 14 | color: #1890ff; 15 | letter-spacing: normal; 16 | .icon { 17 | margin-left: 5px; 18 | opacity: 1; 19 | } 20 | } 21 | &:active { 22 | color: #1890ff; 23 | letter-spacing: normal; 24 | .icon { 25 | margin-left: 5px; 26 | opacity: 1; 27 | } 28 | } 29 | .icon { 30 | opacity: 0; 31 | transition: 0.2s linear; 32 | } 33 | } 34 | .input { 35 | padding: 4px 11px; 36 | color: #000000d9; 37 | font-size: 14px; 38 | line-height: 1.5715; 39 | background-color: #fff; 40 | border: 1px solid #d9d9d9; 41 | border-radius: 2px; 42 | outline: none; 43 | transition: all 0.3s; 44 | &:hover { 45 | border-color: #8fb6d8; 46 | } 47 | &:focus { 48 | border-color: #8fb6d8; 49 | box-shadow: 0px 0px 10px 1px #aed7ec; 50 | } 51 | } 52 | .check-box { 53 | position: absolute; 54 | z-index: 999; 55 | width: 220px; 56 | height: 300px; 57 | background-color: #fff; 58 | border-radius: 5px; 59 | opacity: 0; 60 | transition: 0.2s linear; 61 | .top-bar { 62 | display: flex; 63 | align-items: center; 64 | justify-content: space-between; 65 | padding: 0 0 0 5px; 66 | .month, 67 | .year { 68 | cursor: pointer; 69 | } 70 | } 71 | .close-icon { 72 | color: #1890ff; 73 | font-size: 22px; 74 | cursor: pointer; 75 | } 76 | .date-content { 77 | .week { 78 | display: flex; 79 | justify-content: space-between; 80 | margin-top: 5px; 81 | padding: 0 5px; 82 | font-weight: 500; 83 | } 84 | .day-list { 85 | display: flex; 86 | flex-wrap: wrap; 87 | font-size: 13px; 88 | line-height: 35px; 89 | .day { 90 | width: 31.4px; 91 | text-align: center; 92 | cursor: pointer; 93 | transition: 0.3s linear; 94 | &:hover { 95 | color: #fff; 96 | font-weight: bold; 97 | background-color: #1890ff; 98 | border-radius: 5px; 99 | } 100 | } 101 | .white { 102 | width: 31.4px; 103 | text-align: center; 104 | cursor: pointer; 105 | transition: 0.3s linear; 106 | } 107 | } 108 | .month-toggle-box { 109 | display: flex; 110 | flex-wrap: wrap; 111 | .month { 112 | width: 33%; 113 | height: 55px; 114 | line-height: 55px; 115 | text-align: center; 116 | cursor: pointer; 117 | transition: 0.3s linear; 118 | &:hover { 119 | color: #fff; 120 | background-color: #1890ff; 121 | border-radius: 5px; 122 | } 123 | } 124 | } 125 | .year-toggle-box { 126 | font-size: 14px; 127 | .toggle-bar { 128 | display: flex; 129 | justify-content: space-between; 130 | margin-top: 10px; 131 | } 132 | .list { 133 | display: flex; 134 | flex-wrap: wrap; 135 | margin-top: 10px; 136 | .year { 137 | width: 33%; 138 | height: 60px; 139 | line-height: 60px; 140 | text-align: center; 141 | cursor: pointer; 142 | transition: 0.3s linear; 143 | &:hover { 144 | color: #fff; 145 | background-color: #1890ff; 146 | border-radius: 5px; 147 | } 148 | } 149 | } 150 | } 151 | } 152 | .time-footer { 153 | position: absolute; 154 | bottom: 20px; 155 | left: 10px; 156 | display: flex; 157 | justify-content: space-between; 158 | width: 90%; 159 | padding-right: 10px; 160 | .today { 161 | color: #1890ff; 162 | font-size: 15px; 163 | cursor: pointer; 164 | span { 165 | margin-right: 5px; 166 | } 167 | } 168 | .toggle-month { 169 | color: #1890ff; 170 | font-size: 15px; 171 | cursor: pointer; 172 | } 173 | .go-back-icon { 174 | cursor: pointer; 175 | } 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/DatePicker/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, memo, useState, useEffect, useCallback } from 'react'; 2 | import RangeDatePicker from './rangeDatePicker'; 3 | import { 4 | FieldTimeOutlined, 5 | CloseOutlined, 6 | CheckOutlined, 7 | LeftOutlined, 8 | RightOutlined, 9 | RollbackOutlined, 10 | DoubleLeftOutlined, 11 | DoubleRightOutlined, 12 | } from '@ant-design/icons'; 13 | import './index.module.less'; 14 | 15 | interface DatePickerProps { 16 | /** 17 | * @description 日期选择器类型(primary/input)仅支持非range 18 | * @default primary 19 | */ 20 | type?: string; 21 | /** 22 | * @description 设置日期区间选择器 23 | * @default false 24 | */ 25 | showRange?: Boolean; 26 | /** 27 | * @description 显示日期重置按钮 28 | * @default false 29 | */ 30 | showClear?: Boolean; 31 | /** 32 | * @description 方向 33 | * @default false 34 | */ 35 | align?: string; 36 | /** 37 | * @description 选择完毕后的回调函数 38 | * @default Function 39 | */ 40 | handleChange?: Function; 41 | } 42 | const monthList = [ 43 | '一月', 44 | '二月', 45 | '三月', 46 | '四月', 47 | '五月', 48 | '六月', 49 | '七月', 50 | '八月', 51 | '九月', 52 | '十月', 53 | '十一月', 54 | '十二月', 55 | ]; 56 | 57 | const DatePicker: FC = (props) => { 58 | const { type, showRange, showClear, align, handleChange } = props; 59 | 60 | const [showTimeDialog, setShowTimeDialog] = useState(false); //显示dialog 61 | const [renderShowDialog, setRenderShowDialog] = useState(false); 62 | const [nowDate, setNowDate] = useState({ 63 | //选中的日期 64 | year: new Date().getFullYear(), 65 | month: new Date().getMonth() + 1, 66 | day: new Date().getDate(), 67 | }); 68 | const [thisMonthFirstDay, setThisMonthFirstDay] = useState(0); //本月第一天是周几 69 | const [dayListArray, setDayListArray] = useState>([]); //每月的日历 70 | const [pickStatus, setPickStatus] = useState(0); //timerpick状态,0表示选择日期,1表示改变月份,2表示改变年份 71 | const [iptValue, setIptValue] = useState(null); //文本框输入的值 72 | const [yearList, setYearList] = useState>([ 73 | 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025, 2026, 74 | ]); 75 | 76 | useEffect(() => { 77 | window.addEventListener('click', () => { 78 | setShowTimeDialog(false); 79 | setTimeout(() => { 80 | setRenderShowDialog(false); 81 | }, 300); 82 | }); 83 | }, []); 84 | useEffect(() => { 85 | const { year, month } = nowDate; 86 | const firstDay = new Date(`${year}/${month}/1`).getDay(); 87 | const totalDay = new Date(year, month, 0).getDate(); 88 | const dayList = new Array(firstDay).fill(''); 89 | for (let i = 1; i < totalDay + 1; i++) { 90 | dayList.push(i); 91 | } 92 | setThisMonthFirstDay(firstDay); //重新计算本月第一天为周几 93 | setDayListArray(dayList); //重排本月日历 94 | }, [nowDate.year, nowDate.month]); 95 | const openDialog = (e: any) => { 96 | e.stopPropagation(); 97 | setShowTimeDialog(true); 98 | setRenderShowDialog(true); 99 | }; 100 | const changeDay = (day: number) => { 101 | //改变日期 102 | if (!day) return; 103 | setNowDate((old) => { 104 | old.day = day; 105 | return { ...old }; 106 | }); 107 | handleChange && handleChange(`${nowDate.year}-${nowDate.month}-${nowDate.day}`); 108 | setShowTimeDialog(false); 109 | setTimeout(() => { 110 | setRenderShowDialog(false); 111 | }, 300); 112 | }; 113 | const setToToday = () => { 114 | //改变到今天 115 | const today = new Date(); 116 | setNowDate((old) => { 117 | old.year = today.getFullYear(); 118 | old.month = today.getMonth() + 1; 119 | old.day = today.getDate(); 120 | return { ...old }; 121 | }); 122 | }; 123 | const changeToNextMonth = () => { 124 | //改变到下个月 125 | const renderDate = { ...nowDate }; 126 | if (renderDate.month == 12) { 127 | //12月新年 128 | renderDate.year += 1; 129 | renderDate.month = 1; 130 | } else { 131 | //普通递增 132 | renderDate.month += 1; 133 | } 134 | setNowDate(renderDate); 135 | }; 136 | const changeToPreMonth = () => { 137 | const renderDate = { ...nowDate }; 138 | if (renderDate.month == 1) { 139 | //12月新年 140 | renderDate.year -= 1; 141 | renderDate.month = 12; 142 | } else { 143 | //普通递增 144 | renderDate.month -= 1; 145 | } 146 | setNowDate(renderDate); 147 | }; 148 | const changeMonth = (month: number) => { 149 | //改变月份 150 | setNowDate((old) => { 151 | old.month = month; 152 | return { ...old }; 153 | }); 154 | setPickStatus(0); 155 | }; 156 | const changeYear = (year: number) => { 157 | //改变年份 158 | setNowDate((old) => { 159 | old.year = year; 160 | return { ...old }; 161 | }); 162 | setPickStatus(0); 163 | }; 164 | const bindIptText = (e: any) => { 165 | //绑定文本框 166 | setIptValue(e.target.value); 167 | }; 168 | const enterChangeDate = (e: any) => { 169 | //回车确认更改 170 | if (e.keyCode == 13) { 171 | //回车 172 | if (iptValue !== null) { 173 | if (/^([1-2]\d{3})-(0?[1-9]|1[0-2])-(0?[1-9]|[1-2][0-9]|30|31)$/.test(iptValue)) { 174 | const inputValue = iptValue.split('-'); 175 | setNowDate((old) => { 176 | old.year = Number(inputValue[0]); 177 | old.month = Number(inputValue[1]); 178 | old.day = Number(inputValue[2]); 179 | return { ...old }; 180 | }); 181 | handleChange && 182 | handleChange( 183 | `${Number(inputValue[0])}-${Number(inputValue[1])}-${Number(inputValue[2])}`, 184 | ); 185 | } 186 | } 187 | setIptValue(null); 188 | setShowTimeDialog(false); 189 | setTimeout(() => { 190 | setRenderShowDialog(false); 191 | }, 300); 192 | } 193 | }; 194 | const blurInput = () => { 195 | //文本框失去焦点 196 | if (iptValue !== null) { 197 | if (/^([1-2]\d{3})-(0?[1-9]|1[0-2])-(0?[1-9]|[1-2][0-9]|30|31)$/.test(iptValue)) { 198 | const inputValue = iptValue.split('-'); 199 | setNowDate((old) => { 200 | old.year = Number(inputValue[0]); 201 | old.month = Number(inputValue[1]); 202 | old.day = Number(inputValue[2]); 203 | return { ...old }; 204 | }); 205 | } 206 | } 207 | setIptValue(null); 208 | }; 209 | const setNextGroupYear = () => { 210 | //设定下一组年份 211 | setYearList((old) => { 212 | return [...old.map((y) => y + 9)]; 213 | }); 214 | }; 215 | const setPreGroupYear = () => { 216 | setYearList((old) => { 217 | return [...old.map((y) => y - 9)]; 218 | }); 219 | }; 220 | const clearDate = () => { 221 | //清空 222 | setNowDate((old) => { 223 | (old.year = new Date().getFullYear()), 224 | (old.month = new Date().getMonth() + 1), 225 | (old.day = new Date().getDate()); 226 | return { ...old }; 227 | }); 228 | setIptValue(null); 229 | }; 230 | const rangeDatePickChangeCallback = (start: string, end: string) => { 231 | handleChange && handleChange(start, end); 232 | }; 233 | const activeStyle = { 234 | //选中的所有样式 235 | result: { 236 | letterSpacing: 'normal', 237 | color: '#1890FF', 238 | width: '120px', 239 | }, 240 | icon: { 241 | opacity: 1, 242 | marginLeft: '5px', 243 | }, 244 | checkBox: { 245 | opacity: 1, 246 | }, 247 | dayActive: { 248 | backgroundColor: '#1890FF', 249 | color: '#fff', 250 | fontWeight: 'bold', 251 | borderRadius: '5px', 252 | }, 253 | }; 254 | const alignFn = useCallback(() => { 255 | //对齐方式 256 | if (!align) { 257 | return { 258 | bottom: { 259 | top: '40px', 260 | }, 261 | }; 262 | } 263 | return { 264 | right: { 265 | left: '170px', 266 | bottom: '20px', 267 | }, 268 | left: { 269 | right: '800px', 270 | bottom: '40px', 271 | }, 272 | top: { 273 | bottom: '40px', 274 | }, 275 | bottom: { 276 | top: '40px', 277 | }, 278 | }[align]; 279 | }, [align]); 280 | 281 | return showRange ? ( 282 | 287 | ) : ( 288 |
289 | {(type == 'primary' || !type) && ( 290 |
openDialog(e)} 294 | > 295 | 296 | {nowDate.year}-{nowDate.month}-{nowDate.day} 297 | 298 |
299 | 300 |
301 |
302 | )} 303 | {type == 'input' && ( 304 |
305 | openDialog(e)} 309 | onChange={(e) => bindIptText(e)} 310 | onKeyDown={(e) => enterChangeDate(e)} 311 | onBlur={blurInput} 312 | /> 313 | {showClear && ( 314 | 318 | )} 319 |
320 | )} 321 | {renderShowDialog && ( 322 |
e.stopPropagation()} 326 | > 327 |
328 | setPickStatus(2)}> 329 | {nowDate.year} 330 | 331 | setPickStatus(1)} style={{ marginRight: '20px' }}> 332 | {nowDate.month} 333 | 334 |
{ 337 | setShowTimeDialog(false); 338 | setTimeout(() => { 339 | setRenderShowDialog(false); 340 | }, 300); 341 | }} 342 | > 343 | 344 |
345 |
346 |
347 | {/* 日历 */} 348 | {pickStatus == 0 && ( 349 | <> 350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 | {dayListArray.map((day, index) => { 361 | return ( 362 |
changeDay(day)} 367 | > 368 | {day} 369 |
370 | ); 371 | })} 372 |
373 | 374 | )} 375 | {/* 月份选择框 */} 376 | {pickStatus == 1 && ( 377 |
378 | {monthList.map((m: string, index) => { 379 | return ( 380 |
changeMonth(index + 1)} 385 | > 386 | {m} 387 |
388 | ); 389 | })} 390 |
391 | )} 392 | {/* 年份选择框 */} 393 | {pickStatus == 2 && ( 394 |
395 |
396 | 397 | 398 | {yearList[0]}-{yearList[8]} 399 | 400 | 401 |
402 |
403 | {yearList.map((m: number) => { 404 | return ( 405 |
changeYear(m)} 410 | > 411 | {m} 412 |
413 | ); 414 | })} 415 |
416 |
417 | )} 418 |
419 |
420 | {pickStatus == 0 && ( 421 | <> 422 |
423 | 今天 424 | 425 |
426 |
427 | 428 | 429 |
430 | 431 | )} 432 | {(pickStatus == 1 || pickStatus == 2) && ( 433 | <> 434 |
435 |
setPickStatus(0)}> 436 | 437 |
438 | 439 | )} 440 |
441 |
442 | )} 443 |
444 | ); 445 | }; 446 | 447 | export default memo(DatePicker); 448 | -------------------------------------------------------------------------------- /src/DatePicker/rangeDatePicker/index.module.less: -------------------------------------------------------------------------------- 1 | .range { 2 | position: relative; 3 | user-select: none; 4 | .rangePicker { 5 | position: relative; 6 | display: flex; 7 | align-items: center; 8 | .activeBorder { 9 | position: absolute; 10 | bottom: -2px; 11 | left: 0; 12 | z-index: 99; 13 | width: 100px; 14 | height: 3px; 15 | background-color: #1890ff; 16 | transition: 0.3s linear; 17 | } 18 | } 19 | .date-box { 20 | position: absolute; 21 | z-index: 999; 22 | display: flex; 23 | width: 550px; 24 | height: 300px; 25 | padding-top: 20px; 26 | background-color: #fff; 27 | box-shadow: 0px 0px 5px 2px #d0d0d0; 28 | opacity: 0; 29 | transition: 0.2s linear; 30 | .left { 31 | width: 50%; 32 | padding: 0 35px 0 5px; 33 | font-size: 14px; 34 | .top-bar { 35 | display: flex; 36 | justify-content: space-between; 37 | } 38 | .week { 39 | display: flex; 40 | justify-content: space-between; 41 | margin-top: 20px; 42 | padding: 0 25px 0 12px; 43 | } 44 | .day-list { 45 | display: flex; 46 | flex-wrap: wrap; 47 | .box-list { 48 | width: 35.7px; 49 | height: 40px; 50 | line-height: 40px; 51 | text-align: center; 52 | cursor: pointer; 53 | &:hover { 54 | color: #fff; 55 | background-color: #1890ff; 56 | border-radius: 5px; 57 | } 58 | } 59 | .white { 60 | width: 35.7px; 61 | height: 40px; 62 | line-height: 40px; 63 | text-align: center; 64 | } 65 | } 66 | } 67 | .right { 68 | width: 50%; 69 | padding: 0 5px; 70 | font-size: 14px; 71 | .top-bar { 72 | display: flex; 73 | justify-content: space-between; 74 | } 75 | .week { 76 | display: flex; 77 | justify-content: space-between; 78 | margin-top: 20px; 79 | padding: 0 25px 0 12px; 80 | } 81 | .day-list { 82 | display: flex; 83 | flex-wrap: wrap; 84 | .day-box { 85 | width: 35.7px; 86 | height: 40px; 87 | line-height: 40px; 88 | text-align: center; 89 | cursor: pointer; 90 | &:hover { 91 | color: #fff; 92 | background-color: #1890ff; 93 | border-radius: 5px; 94 | } 95 | } 96 | .disabled-day { 97 | width: 35.7px; 98 | height: 40px; 99 | color: #ccc; 100 | line-height: 40px; 101 | text-align: center; 102 | cursor: not-allowed; 103 | } 104 | .white { 105 | width: 35.7px; 106 | height: 40px; 107 | line-height: 40px; 108 | text-align: center; 109 | } 110 | } 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/DatePicker/rangeDatePicker/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, FC, memo, useState, useCallback } from 'react'; 2 | import { 3 | DoubleLeftOutlined, 4 | LeftOutlined, 5 | DoubleRightOutlined, 6 | RightOutlined, 7 | SwapRightOutlined, 8 | } from '@ant-design/icons'; 9 | import Input from '../../Input'; 10 | import './index.module.less'; 11 | 12 | interface RangeProps { 13 | showClear?: Boolean; 14 | align?: string; 15 | handleChange?: Function; 16 | } 17 | const RangeDatePicker: FC = (props) => { 18 | const { showClear, align, handleChange } = props; 19 | const [startDate, setStartDate] = useState({ 20 | startYear: new Date().getFullYear(), 21 | startMonth: new Date().getMonth() + 1, 22 | startDay: new Date().getDate(), 23 | }); 24 | const [endDate, setEndDate] = useState({ 25 | endYear: new Date().getFullYear(), 26 | endMonth: new Date().getMonth() + 2, 27 | endDay: new Date().getDate(), 28 | }); 29 | const [startTime, setStartTime] = useState(''); 30 | const [endTime, setEndTime] = useState(''); 31 | const [startMonthFirstDay, setStartMonthFirstDay] = useState(0); //本月第一天是周几 32 | const [endMonthFirstDay, setEndMonthFirstDay] = useState(0); //本月第一天是周几 33 | const [startDayListArray, setStartDayListArray] = useState>([]); //start月的日历 34 | const [endDayListArray, setEndDayListArray] = useState>([]); //end月的日历 35 | const [showTimeDiaglog, setShowTimeDialog] = useState(false); //日期选择器dialog show 36 | const [renderShowDialog, setRenderShowDialog] = useState(false); 37 | const [chooseStatus, setChooseStatus] = useState({ 38 | start: false, 39 | end: false, 40 | }); //是否被选择过 41 | 42 | let activeBorderDom: Element | null = document.querySelector('.activeBorder'); 43 | 44 | useEffect(() => { 45 | const { startYear, startMonth } = startDate; 46 | const { endYear, endMonth } = endDate; 47 | const startFirstDay = new Date(`${startYear}/${startMonth}/1`).getDay(); 48 | const endFirstDay = new Date(`${endYear}/${endMonth}/1`).getDay(); 49 | const startTotalDay = new Date(startYear, startMonth, 0).getDate(); 50 | const endTotalDay = new Date(endYear, endMonth, 0).getDate(); 51 | const startDayList = new Array(startFirstDay).fill(''); 52 | const endDayList = new Array(endFirstDay).fill(''); 53 | for (let i = 1; i < startTotalDay + 1; i++) { 54 | startDayList.push(i); 55 | } 56 | for (let i = 1; i < endTotalDay + 1; i++) { 57 | endDayList.push(i); 58 | } 59 | setStartDayListArray(startDayList); 60 | setStartMonthFirstDay(startFirstDay); 61 | setEndDayListArray(endDayList); 62 | setEndMonthFirstDay(endFirstDay); 63 | }, [startDate.startYear, startDate.startMonth, endDate.endYear, endDate.endMonth]); 64 | useEffect(() => { 65 | window.addEventListener('click', () => { 66 | setShowTimeDialog(false); 67 | setTimeout(() => { 68 | setRenderShowDialog(false); 69 | }, 300); 70 | }); 71 | }, []); 72 | useEffect(() => { 73 | if (chooseStatus.start && chooseStatus.end) { 74 | setShowTimeDialog(false); 75 | setTimeout(() => { 76 | setRenderShowDialog(false); 77 | }, 300); 78 | setChooseStatus((old) => { 79 | old.start = false; 80 | old.end = false; 81 | return { ...old }; 82 | }); 83 | handleChange && handleChange(startTime, endTime); 84 | } 85 | }, [chooseStatus]); 86 | 87 | const startIptFocus = () => { 88 | setShowTimeDialog(true); 89 | setRenderShowDialog(true); 90 | (activeBorderDom as any).style.left = '0'; 91 | }; 92 | const endIptFocus = () => { 93 | setShowTimeDialog(true); 94 | setRenderShowDialog(true); 95 | (activeBorderDom as any).style.left = '190px'; 96 | }; 97 | const preYear = (type: string) => { 98 | //切换上一年 99 | if (type == 'start') { 100 | const renderDate = { ...startDate }; 101 | renderDate.startYear -= 1; 102 | setStartDate(renderDate); 103 | } else if (type == 'end') { 104 | if (endDate.endYear > startDate.startYear) { 105 | const renderDate = { ...endDate }; 106 | renderDate.endYear -= 1; 107 | setEndDate(renderDate); 108 | } 109 | } 110 | }; 111 | const nextYear = (type: string) => { 112 | //切换下一年 113 | if (type == 'start') { 114 | if (startDate.startYear < endDate.endYear) { 115 | const renderDate = { ...startDate }; 116 | renderDate.startYear += 1; 117 | setStartDate(renderDate); 118 | } 119 | } else if (type == 'end') { 120 | const renderDate = { ...endDate }; 121 | renderDate.endYear += 1; 122 | setEndDate(renderDate); 123 | } 124 | }; 125 | const preMonth = (type: string) => { 126 | //切换上一个月 127 | if (type == 'start') { 128 | const renderDate = { ...startDate }; 129 | if (renderDate.startMonth == 1) { 130 | renderDate.startMonth = 12; 131 | renderDate.startYear -= 1; 132 | } else { 133 | renderDate.startMonth -= 1; 134 | } 135 | setStartDate(renderDate); 136 | } else if (type == 'end') { 137 | if (endDate.endYear == startDate.startYear && endDate.endMonth == startDate.startMonth) { 138 | return; 139 | } else { 140 | const renderDate = { ...endDate }; 141 | if (renderDate.endMonth == 1) { 142 | renderDate.endMonth = 12; 143 | renderDate.endYear -= 1; 144 | } else { 145 | renderDate.endMonth -= 1; 146 | } 147 | if (renderDate.endDay < startDate.startDay) { 148 | renderDate.endDay = startDate.startDay; 149 | } 150 | setEndDate(renderDate); 151 | } 152 | } 153 | }; 154 | const nextMonth = (type: string) => { 155 | //切换下一个月 156 | if (type == 'start') { 157 | if (endDate.endYear == startDate.startYear && endDate.endMonth == startDate.startMonth) { 158 | return; 159 | } else { 160 | const renderDate = { ...startDate }; 161 | if (renderDate.startMonth == 12) { 162 | renderDate.startMonth = 1; 163 | renderDate.startYear += 1; 164 | } else { 165 | renderDate.startMonth += 1; 166 | } 167 | setStartDate(renderDate); 168 | } 169 | } else if (type == 'end') { 170 | const renderDate = { ...endDate }; 171 | if (renderDate.endMonth == 12) { 172 | renderDate.endMonth = 1; 173 | renderDate.endYear += 1; 174 | } else { 175 | renderDate.endMonth += 1; 176 | } 177 | setEndDate(renderDate); 178 | } 179 | }; 180 | const chooseStartDay = (day: number | string) => { 181 | //选择开始日期 182 | if (day == '') return; 183 | setStartDate((old) => { 184 | old.startDay = day as number; 185 | return { ...old }; 186 | }); 187 | setChooseStatus((old) => { 188 | old.start = true; 189 | return { ...old }; 190 | }); 191 | setStartTime(`${startDate.startYear}-${startDate.startMonth}-${day}`); 192 | if (startDate.startYear == endDate.endYear && startDate.startMonth == endDate.endMonth) { 193 | if (day > endDate.endDay) { 194 | setEndDate((old) => { 195 | old.endDay = day as number; 196 | return { ...old }; 197 | }); 198 | } 199 | } 200 | }; 201 | const chooseEndDay = (day: number | string) => { 202 | //选择结束日期 203 | if (startDate.startYear == endDate.endYear && startDate.startMonth == endDate.endMonth) { 204 | if (day < startDate.startDay) { 205 | return; 206 | } 207 | } 208 | setChooseStatus((old) => { 209 | old.end = true; 210 | return { ...old }; 211 | }); 212 | setEndDate((old) => { 213 | old.endDay = day as number; 214 | return { ...old }; 215 | }); 216 | setEndTime(`${endDate.endYear}-${endDate.endMonth}-${day}`); 217 | }; 218 | const enterChangeStartTime = (e: any) => { 219 | //回车改变 220 | if (e.keyCode == 13) { 221 | if (/^([1-2]\d{3})-(0?[1-9]|1[0-2])-(0?[1-9]|[1-2][0-9]|30|31)$/.test(startTime)) { 222 | const start = startTime.split('-'); 223 | if ( 224 | Number(start[0]) <= endDate.endYear && 225 | Number(start[1]) <= endDate.endMonth && 226 | Number(start[2]) <= endDate.endDay 227 | ) { 228 | setStartDate((old) => { 229 | old.startYear = Number(start[0]); 230 | old.startMonth = Number(start[1]); 231 | old.startDay = Number(start[2]); 232 | return { ...old }; 233 | }); 234 | setChooseStatus((old) => { 235 | old.start = true; 236 | return { ...old }; 237 | }); 238 | } else { 239 | setStartTime(''); 240 | } 241 | } else { 242 | setStartTime(''); 243 | } 244 | } 245 | }; 246 | const blurStartTime = () => { 247 | //失去焦点 248 | if (!/^([1-2]\d{3})-(0?[1-9]|1[0-2])-(0?[1-9]|[1-2][0-9]|30|31)$/.test(startTime)) { 249 | setStartTime(''); 250 | } 251 | }; 252 | const enterChangeEndTime = (e: any) => { 253 | //回车改变 254 | if (e.keyCode == 13) { 255 | if (/^([1-2]\d{3})-(0?[1-9]|1[0-2])-(0?[1-9]|[1-2][0-9]|30|31)$/.test(endTime)) { 256 | const start = endTime.split('-'); 257 | if ( 258 | Number(start[0]) >= startDate.startYear && 259 | Number(start[1]) >= startDate.startMonth && 260 | Number(start[2]) >= startDate.startDay 261 | ) { 262 | setEndDate((old) => { 263 | old.endYear = Number(start[0]); 264 | old.endMonth = Number(start[1]); 265 | old.endDay = Number(start[2]); 266 | return { ...old }; 267 | }); 268 | setChooseStatus((old) => { 269 | old.end = true; 270 | return { ...old }; 271 | }); 272 | } else { 273 | setEndTime(''); 274 | } 275 | } else { 276 | setEndTime(''); 277 | } 278 | } 279 | }; 280 | const blurEndTime = () => { 281 | //失去焦点 282 | if (!/^([1-2]\d{3})-(0?[1-9]|1[0-2])-(0?[1-9]|[1-2][0-9]|30|31)$/.test(endTime)) { 283 | setEndTime(''); 284 | } 285 | }; 286 | const clearStartTime = () => { 287 | //清空开始时间 288 | setStartTime(''); 289 | setStartDate((old) => { 290 | const now = new Date(); 291 | old.startDay = now.getDate(); 292 | old.startMonth = now.getMonth() + 1; 293 | old.startYear = now.getFullYear(); 294 | return { ...old }; 295 | }); 296 | }; 297 | const clearEndTime = () => { 298 | //清空结束时间 299 | setEndTime(''); 300 | setEndDate((old) => { 301 | const now = new Date(); 302 | old.endDay = now.getDate(); 303 | old.endMonth = now.getMonth() + 1; 304 | old.endYear = now.getFullYear(); 305 | return { ...old }; 306 | }); 307 | }; 308 | const activeStyles = () => { 309 | //选中的样式 310 | return { 311 | activeDay: { 312 | color: '#fff', 313 | background: '#1890FF', 314 | fontWeight: 'bold', 315 | borderRadius: '5px', 316 | }, 317 | showDialog: { 318 | opacity: 1, 319 | }, 320 | }; 321 | }; 322 | const alignFn = useCallback(() => { 323 | if (!align) { 324 | return { 325 | bottom: { 326 | top: '40px', 327 | }, 328 | }; 329 | } 330 | return { 331 | right: { 332 | left: '360px', 333 | bottom: '20px', 334 | }, 335 | left: { 336 | right: '600px', 337 | bottom: '20px', 338 | }, 339 | top: { 340 | bottom: '40px', 341 | }, 342 | bottom: { 343 | top: '40px', 344 | }, 345 | }[align]; 346 | }, [align]); 347 | const disabledClass = useCallback( 348 | (day: number | string) => { 349 | if (day == '') { 350 | return 'white'; 351 | } 352 | if (startDate.startYear == endDate.endYear && startDate.startMonth == endDate.endMonth) { 353 | if (day < startDate.startDay) { 354 | return 'disabled-day'; 355 | } 356 | return 'day-box'; 357 | } 358 | return 'day-box'; 359 | }, 360 | [startDate, endDate], 361 | ); 362 | return ( 363 |
e.stopPropagation()}> 364 |
e.stopPropagation()}> 365 | setStartTime(v)} 373 | handleIptFocus={startIptFocus} 374 | handleKeyDown={(e: any) => enterChangeStartTime(e)} 375 | handleIptBlur={blurStartTime} 376 | clearCallback={clearStartTime} 377 | showClear={showClear as boolean} 378 | /> 379 | 380 | setEndTime(v)} 386 | handleIptFocus={endIptFocus} 387 | handleKeyDown={(e: any) => enterChangeEndTime(e)} 388 | handleIptBlur={blurEndTime} 389 | clearCallback={clearEndTime} 390 | showClear={showClear as boolean} 391 | /> 392 |
393 |
394 | {renderShowDialog && ( 395 |
e.stopPropagation()} 398 | style={{ ...(showTimeDiaglog ? activeStyles().showDialog : {}), ...alignFn() } as any} 399 | > 400 |
401 |
402 |
403 | preYear('start')} 406 | /> 407 | preMonth('start')} 410 | /> 411 |
412 |
413 | {startDate.startYear}年 {startDate.startMonth}月 414 |
415 |
416 | nextMonth('start')} /> 417 | nextYear('start')} 420 | /> 421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 | {startDayListArray.map((i: string | number, index) => { 434 | return ( 435 |
chooseStartDay(Number(i))} 440 | > 441 | {i} 442 |
443 | ); 444 | })} 445 |
446 |
447 |
448 |
449 |
450 | preYear('end')} /> 451 | preMonth('end')} 454 | /> 455 |
456 |
457 | {endDate.endYear}年 {endDate.endMonth}月 458 |
459 | 460 |
461 | nextMonth('end')} /> 462 | nextYear('end')} 465 | /> 466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 | {endDayListArray.map((i: string | number, index) => { 479 | return ( 480 |
chooseEndDay(Number(i))} 485 | > 486 | {i} 487 |
488 | ); 489 | })} 490 |
491 |
492 |
493 | )} 494 |
495 | ); 496 | }; 497 | 498 | export default memo(RangeDatePicker); 499 | -------------------------------------------------------------------------------- /src/Divider/demos/index1.tsx: -------------------------------------------------------------------------------- 1 | import Divider from '..'; 2 | import React from 'react'; 3 | /** 4 | * transform: true 5 | */ 6 | export default function DividerDemo1() { 7 | return ( 8 |
9 | React View UI 10 |
11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /src/Divider/demos/index2.tsx: -------------------------------------------------------------------------------- 1 | import Divider from '..'; 2 | import React from 'react'; 3 | /** 4 | * transform: true 5 | */ 6 | export default function DividerDemo2() { 7 | return ( 8 |
9 |
10 | React View UI 11 |
12 |
13 | React View UI 14 |
15 |
16 | React View UI 17 |
18 |
19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /src/Divider/demos/index3.tsx: -------------------------------------------------------------------------------- 1 | import Divider from '..'; 2 | import React from 'react'; 3 | /** 4 | * transform: true 5 | */ 6 | export default function DividerDemo3() { 7 | return ( 8 |
9 |
10 | 11 | React View UI 12 | 13 |
14 |
15 | 16 | React View UI 17 | 18 |
19 |
20 | 21 | React View UI 22 | 23 |
24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /src/Divider/demos/index4.tsx: -------------------------------------------------------------------------------- 1 | import Divider from '..'; 2 | import React from 'react'; 3 | /** 4 | * transform: true 5 | */ 6 | export default function DividerDemo3() { 7 | return ( 8 |
9 |
10 | 11 | React View UI 12 | 13 |
14 |
15 | React View UI 16 |
17 |
18 | 19 | React View UI 20 | 21 |
22 |
23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /src/Divider/demos/index5.tsx: -------------------------------------------------------------------------------- 1 | import Divider from '..'; 2 | import React from 'react'; 3 | /** 4 | * transform: true 5 | */ 6 | export default function DividerDemo5() { 7 | return ( 8 |
9 | React View UI 10 |
11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /src/Divider/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Divider 分割线 3 | nav: 4 | title: 视图 5 | path: /view 6 | group: 7 | path: /view 8 | --- 9 | 10 | # Divider 分割线 11 | 12 |

分隔内容的文档分割线

13 | 14 | #### 何时使用 15 | 16 | - 对不同章节的文本段落进行分割。 17 | - 对行内文字/链接进行分割,例如表格的操作列。 18 | 19 | ## 基本用法 20 | 21 | 22 | 23 | ## 字体大小 24 | 25 | 26 | 27 | ## 分割线色彩 28 | 29 | 30 | 31 | ## 对齐方式 32 | 33 | 34 | 35 | ## 虚线 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/Divider/index.module.less: -------------------------------------------------------------------------------- 1 | .divider { 2 | .line { 3 | position: relative; 4 | display: flex; 5 | align-items: center; 6 | justify-content: center; 7 | width: 100%; 8 | height: 1px; 9 | border-top: 1px solid #ccc; 10 | .line-text { 11 | position: absolute; 12 | padding: 0 10px; 13 | background-color: #fff; 14 | } 15 | } 16 | .dashed { 17 | position: relative; 18 | display: flex; 19 | align-items: center; 20 | justify-content: center; 21 | width: 100%; 22 | height: 1px; 23 | border-top: 1px dashed #ccc; 24 | .line-text { 25 | position: absolute; 26 | padding: 0 10px; 27 | background-color: #fff; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Divider/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useMemo, memo } from 'react'; 2 | import './index.module.less'; 3 | 4 | interface dividerProps { 5 | children?: any; 6 | /** 7 | * @description 字体大小 8 | */ 9 | fontSize?: Number; 10 | /** 11 | * @description 分割线颜色 12 | * @default #cccccc 13 | */ 14 | borderColor?: String; 15 | /** 16 | * @description 对齐方式 17 | * @default center 18 | */ 19 | align?: String; 20 | /** 21 | * @description 分割线类型 22 | * @default border 23 | */ 24 | dashed?: Boolean; 25 | } 26 | const Divider: FC = memo((props) => { 27 | const { children, fontSize, borderColor, align, dashed } = props; 28 | const lineAlign = useMemo(() => { 29 | if (align === 'left') { 30 | return { 31 | justifyContent: 'left', 32 | }; 33 | } else if (align === 'right') { 34 | return { 35 | justifyContent: 'right', 36 | }; 37 | } 38 | return {}; 39 | }, [align]); 40 | const lineColor = useMemo((): Object => { 41 | if (borderColor) { 42 | return { 43 | borderColor: borderColor, 44 | }; 45 | } 46 | return {}; 47 | }, [borderColor]); 48 | const textStyle = useMemo(() => { 49 | if (fontSize) { 50 | return { 51 | fontSize: `${fontSize}px`, 52 | }; 53 | } 54 | }, [fontSize]); 55 | return ( 56 |
57 |
58 | {children && ( 59 | 60 | {children} 61 | 62 | )} 63 |
64 |
65 | ); 66 | }); 67 | export default Divider; 68 | -------------------------------------------------------------------------------- /src/Input/demos/index1.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Input from '..'; 3 | 4 | export default function InputDemo1() { 5 | const handleIptChange = (h: string) => { 6 | console.log(h); 7 | }; 8 | return ; 9 | } 10 | -------------------------------------------------------------------------------- /src/Input/demos/index2.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Input from '..'; 3 | import Select from '../../Select/index'; 4 | import Button from '../../Button/index'; 5 | 6 | export default function InputDemo2() { 7 | const option = [ 8 | { 9 | label: 'Mucy', 10 | value: 'mucy', 11 | }, 12 | { 13 | label: 'Mike', 14 | value: 'mike', 15 | }, 16 | { 17 | label: 'aMck', 18 | value: 'amck', 19 | }, 20 | ]; 21 | const handleSelectCallback = (h: number) => { 22 | console.log(h); 23 | }; 24 | const handleIptChange = (h: string) => { 25 | console.log(h); 26 | }; 27 | return ( 28 | <> 29 |
30 | 31 |
32 | 42 |
43 | 44 |
45 |
46 | 47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /src/Input/demos/index3.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Input from '..'; 3 | 4 | export default function InputDemo3() { 5 | const handleIptChange = (h: string) => { 6 | console.log(h); 7 | }; 8 | return ; 9 | } 10 | -------------------------------------------------------------------------------- /src/Input/demos/index4.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Input from '..'; 3 | 4 | export default function InputDemo4() { 5 | const handleIptChange = (h: string) => { 6 | console.log(h); 7 | }; 8 | return ( 9 | 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /src/Input/demos/index5.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Input from '..'; 3 | 4 | export default function InputDemo5() { 5 | const handleIptChange = (h: string) => { 6 | console.log(h); 7 | }; 8 | const handleNumChange = (h: string) => { 9 | console.log(h); 10 | }; 11 | return ( 12 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /src/Input/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Input 输入框 3 | nav: 4 | title: 通用 5 | path: /common 6 | group: 7 | path: /common 8 | --- 9 | 10 | # Input 输入框 11 | 12 | 通过鼠标或键盘输入内容,是最基础的表单域的包装。 13 | 14 | ## 何时使用 15 | 16 | - 需要用户输入表单域内容时。 17 | - 提供组合型输入框,带搜索的输入框,还可以进行大小选择 18 | 19 | ## 基本使用 20 | 21 | 22 | 23 | ## 多场景适用 24 | 25 | 26 | 27 | ## 可清空 28 | 29 | 30 | 31 | ## 密码框 32 | 33 | 34 | 35 | ## 数字框 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/Input/index.module.less: -------------------------------------------------------------------------------- 1 | .box { 2 | position: relative; 3 | display: flex; 4 | align-items: center; 5 | justify-content: space-between; 6 | background-color: #fff; 7 | .input { 8 | padding: 4px 11px; 9 | color: #000000d9; 10 | font-size: 14px; 11 | line-height: 1.5715; 12 | background-color: #fff; 13 | border: 1px solid #d9d9d9; 14 | border-radius: 2px; 15 | outline: none; 16 | transition: all 0.3s; 17 | &:hover { 18 | border-color: #8fb6d8; 19 | } 20 | &:focus { 21 | border-color: #8fb6d8; 22 | box-shadow: 0px 0px 10px 1px #aed7ec; 23 | } 24 | .closeIcon { 25 | position: absolute; 26 | } 27 | } 28 | .numTags { 29 | position: absolute; 30 | right: 5px; 31 | display: flex; 32 | flex-direction: column; 33 | color: #bebdbd; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Input/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useState, useMemo, memo } from 'react'; 2 | import { CloseOutlined, EyeOutlined, UpOutlined, DownOutlined } from '@ant-design/icons'; 3 | import './index.module.less'; 4 | 5 | interface InputProps { 6 | /** 7 | * @description 自定义宽度 8 | * @default 170px 9 | */ 10 | width?: string; 11 | /** 12 | * @description 自定义样式 13 | * @default {} 14 | */ 15 | moreStyle?: object; 16 | /** 17 | * @description 输入框类型 18 | * @default text 19 | */ 20 | type?: string; 21 | /** 22 | * @description 提示 23 | * @default '' 24 | */ 25 | placeholder?: string; 26 | /** 27 | * @description 显示清空按钮 28 | * @default false 29 | */ 30 | showClear?: boolean; 31 | /** 32 | * @description 显示密码切换按钮(需同时设置type="password") 33 | * @default false 34 | */ 35 | showTogglePwd?: boolean; 36 | /** 37 | * @description 数字框最小值 38 | * @default '' 39 | */ 40 | min?: number; 41 | /** 42 | * @description 数字框最大值 43 | * @default '' 44 | */ 45 | max?: number; 46 | /** 47 | * @description 数字框递增/减值 48 | * @default 1 49 | */ 50 | step?: number; 51 | /** 52 | * @description 输入框内容改变回调 53 | */ 54 | handleIptChange?: Function; 55 | /** 56 | * @description 输入框聚焦回调 57 | */ 58 | handleIptFocus?: Function; 59 | /** 60 | * @description 输入框失去焦点回调 61 | */ 62 | handleIptBlur?: Function; 63 | /** 64 | * @description 输入框键盘监听 65 | */ 66 | handleKeyDown?: Function; 67 | /** 68 | * @description 数字框内容改变回调 69 | */ 70 | handleNumChange?: Function; 71 | /** 72 | * @description 清空回调 73 | */ 74 | clearCallback?: Function; 75 | /** 76 | * @description 默认内容 77 | * @default '' 78 | */ 79 | defaultValue?: string; 80 | } 81 | 82 | type NativeInputProps = Omit, 'type'>; //原生Input接口 83 | const Input: FC = (props) => { 84 | const { 85 | width, 86 | moreStyle, 87 | type, 88 | placeholder, 89 | showClear, 90 | showTogglePwd, 91 | min, 92 | max, 93 | step, 94 | handleIptChange, 95 | handleKeyDown, 96 | handleIptFocus, 97 | handleIptBlur, 98 | handleNumChange, 99 | clearCallback, 100 | defaultValue, 101 | } = props; 102 | const [iptValue, setIptValue] = useState(defaultValue || ''); 103 | const [pwdIptState, setPwdIptState] = useState(true); //密码框切换状态 104 | const changeIpt = (e: any) => { 105 | //改变文本框 106 | setIptValue(e.target.value); 107 | if (handleIptChange) { 108 | handleIptChange(e.target.value); 109 | } 110 | }; 111 | const blurIpt = (e: any) => { 112 | //失去焦点 113 | if (type === 'num' && Number(iptValue) == NaN) { 114 | setIptValue(''); 115 | } 116 | handleIptBlur && handleIptBlur(); 117 | }; 118 | const focusIpt = () => { 119 | handleIptFocus && handleIptFocus(iptValue); 120 | }; 121 | const addNum = () => { 122 | //加 123 | if (type === 'num' && Number(iptValue) == NaN) { 124 | return setIptValue(''); 125 | } 126 | const stepNum = step || 1; 127 | if (step && max && Number(iptValue) + stepNum > max) { 128 | handleNumChange && handleNumChange(max); 129 | return setIptValue(max); 130 | } 131 | if (step && min && Number(iptValue) + stepNum < min) { 132 | handleNumChange && handleNumChange(min); 133 | return setIptValue(min); 134 | } 135 | handleNumChange && handleNumChange(Number(iptValue) + stepNum); 136 | setIptValue(Number(iptValue) + stepNum); 137 | }; 138 | const lowNum = () => { 139 | //减 140 | if (type === 'num' && Number(iptValue) == NaN) { 141 | return setIptValue(''); 142 | } 143 | const stepNum = step || 1; 144 | if (step && min && Number(iptValue) - stepNum < min) { 145 | handleNumChange && handleNumChange(min); 146 | return setIptValue(min); 147 | } 148 | handleNumChange && handleNumChange(Number(iptValue) - stepNum); 149 | setIptValue(Number(iptValue) - stepNum); 150 | }; 151 | const iptType = useMemo(() => { 152 | if (showTogglePwd && type === 'password') { 153 | return pwdIptState ? 'password' : 'text'; 154 | } 155 | return type || 'text'; 156 | }, [type, showTogglePwd, pwdIptState]); 157 | const exticStyle = useMemo(() => { 158 | let style = { width: '170px' }; 159 | if (width) { 160 | style.width = width + 'px'; 161 | } 162 | return { ...style, ...moreStyle }; 163 | }, [width, moreStyle]); 164 | return ( 165 |
166 | handleKeyDown && handleKeyDown(e)} 176 | /> 177 | { 178 | //可清除 179 | (showClear && ( 180 | { 183 | setIptValue(''); 184 | clearCallback && clearCallback(); 185 | }} 186 | /> 187 | )) || 188 | //密码框 189 | (type === 'password' && showTogglePwd && ( 190 | setPwdIptState(!pwdIptState)} 193 | /> 194 | )) || 195 | //数字框 196 | (type === 'num' && ( 197 |
198 | 199 | 200 |
201 | )) 202 | } 203 |
204 | ); 205 | }; 206 | export default memo(Input); 207 | -------------------------------------------------------------------------------- /src/Layout/Content/index.module.less: -------------------------------------------------------------------------------- 1 | .content { 2 | width: 70%; 3 | background-color: #108EE9; 4 | color: #fff; 5 | text-align: center; 6 | padding: 50px 0; 7 | } -------------------------------------------------------------------------------- /src/Layout/Content/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useMemo, memo } from 'react'; 2 | import './index.module.less'; 3 | 4 | interface ContentProps { 5 | row?: Number; 6 | extraStyle?: Object; 7 | children?: Element | undefined | String | any; 8 | } 9 | const Content: FC = (props) => { 10 | const { children, row, extraStyle } = props; 11 | const contentRow = useMemo(() => { 12 | if (row) { 13 | return { 14 | width: `${row}0%`, 15 | }; 16 | } 17 | return {}; 18 | }, [row]); 19 | const propsStyle = useMemo(() => { 20 | if (extraStyle) { 21 | return extraStyle; 22 | } 23 | return {}; 24 | }, [extraStyle]); 25 | 26 | return ( 27 |
28 | {children} 29 |
30 | ); 31 | }; 32 | export default memo(Content); 33 | -------------------------------------------------------------------------------- /src/Layout/Footer/index.module.less: -------------------------------------------------------------------------------- 1 | .footer { 2 | width: 100%; 3 | background-color: #7dbcea; 4 | color: #fff; 5 | text-align: center; 6 | padding: 50px 0; 7 | } -------------------------------------------------------------------------------- /src/Layout/Footer/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useMemo, memo } from 'react'; 2 | import './index.module.less'; 3 | 4 | interface FooterProps { 5 | extraStyle?: Object; 6 | children?: Element | undefined | String | any; 7 | } 8 | const Footer: FC = (props) => { 9 | const { children, extraStyle } = props; 10 | 11 | const propsStyle = useMemo(() => { 12 | if (extraStyle) { 13 | return extraStyle; 14 | } 15 | return {}; 16 | }, [extraStyle]); 17 | 18 | return ( 19 |
20 | {children} 21 |
22 | ); 23 | }; 24 | export default memo(Footer); 25 | -------------------------------------------------------------------------------- /src/Layout/Header/index.module.less: -------------------------------------------------------------------------------- 1 | .header { 2 | width: 100%; 3 | background-color: #7dbcea; 4 | color: #fff; 5 | text-align: center; 6 | padding: 50px 0; 7 | } -------------------------------------------------------------------------------- /src/Layout/Header/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, memo, useMemo } from 'react'; 2 | import './index.module.less'; 3 | 4 | interface HeaderProps { 5 | extraStyle?: Object; 6 | children?: Element | undefined | String | any; 7 | } 8 | const Header: FC = (props) => { 9 | const { children, extraStyle } = props; 10 | 11 | const propsStyle = useMemo(() => { 12 | if (extraStyle) { 13 | return extraStyle; 14 | } 15 | return {}; 16 | }, [extraStyle]); 17 | 18 | return ( 19 |
20 | {children} 21 |
22 | ); 23 | }; 24 | export default memo(Header); 25 | -------------------------------------------------------------------------------- /src/Layout/Slider/index.module.less: -------------------------------------------------------------------------------- 1 | .slider { 2 | width: 30%; 3 | background-color: #3ba0e9; 4 | color: #fff; 5 | text-align: center; 6 | padding: 50px 0; 7 | } -------------------------------------------------------------------------------- /src/Layout/Slider/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useMemo, memo } from 'react'; 2 | import './index.module.less'; 3 | 4 | interface SliderProps { 5 | row?: Number; 6 | extraStyle?: Object; 7 | children?: Element | undefined | String | any; 8 | } 9 | const Slider: FC = (props) => { 10 | const { row, extraStyle } = props; 11 | 12 | const sliderRow = useMemo(() => { 13 | if (row) { 14 | return { 15 | width: `${row}0%`, 16 | }; 17 | } 18 | return {}; 19 | }, [row]); 20 | const propsStyle = useMemo(() => { 21 | if (extraStyle) { 22 | return extraStyle; 23 | } 24 | return {}; 25 | }, [extraStyle]); 26 | 27 | return ( 28 |
29 | {props.children} 30 |
31 | ); 32 | }; 33 | export default memo(Slider); 34 | -------------------------------------------------------------------------------- /src/Layout/demos/index1.tsx: -------------------------------------------------------------------------------- 1 | import Layout from '..'; 2 | import Header from '../Header/index' 3 | import Content from '../Content'; 4 | import Footer from '../Footer' 5 | import Slider from '../Slider'; 6 | import React from 'react'; 7 | /** 8 | * transform: true 9 | */ 10 | export default function LayoutDemo1() { 11 | return ( 12 | <> 13 |
14 | 15 |
16 | header 17 |
18 | 19 | content 20 | 21 |
22 | footer 23 |
24 |
25 |
26 |
27 |
28 | 29 |
30 | header 31 |
32 | 33 | 34 | slider 35 | 36 | 37 | content 38 | 39 | 40 | 41 |
42 | footer 43 |
44 |
45 |
46 |
47 |
48 | 49 |
50 | header 51 |
52 | 53 | 54 | content 55 | 56 | 57 | slider 58 | 59 | 60 | 61 |
62 | footer 63 |
64 |
65 |
66 |
67 |
68 | 69 |
70 | header 71 |
72 | 73 | 74 | slider 75 | 76 | 77 | content 78 | 79 | 80 | slider 81 | 82 | 83 | 84 |
85 | footer 86 |
87 |
88 |
89 | 90 | 91 | ); 92 | } 93 | -------------------------------------------------------------------------------- /src/Layout/demos/index2.tsx: -------------------------------------------------------------------------------- 1 | import Layout from '..'; 2 | import Header from '../Header/index' 3 | import Content from '../Content'; 4 | import Footer from '../Footer' 5 | import Slider from '../Slider'; 6 | import React from 'react'; 7 | /** 8 | * transform: true 9 | */ 10 | export default function LayoutDemo1() { 11 | return ( 12 | <> 13 |
14 | 15 |
16 | header 17 |
18 | 19 | 20 | slider 21 | 22 | 23 | content 24 | 25 | 26 |
27 | footer 28 |
29 |
30 |
31 | 32 | 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /src/Layout/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Layout 快速布局 3 | nav: 4 | title: 视图 5 | path: /view 6 | group: 7 | path: /view 8 | --- 9 | 10 | # Layout 快速布局 11 | 12 |

快速成型常见网页布局

13 | 14 | #### 组件概述 15 | 16 | - Layout:布局容器,其下可嵌套 Header Sider Content Footer 或 Layout 本身,可以放在任何父容器中 17 | - Header:顶部布局,自带默认样式,其下可嵌套任何元素,只能放在 Layout 中。 18 | - Sider:侧边栏,自带默认样式及基本功能,其下可嵌套任何元素,只能放在 Layout 中。 19 | - Content:内容部分,自带默认样式,其下可嵌套任何元素,只能放在 Layout 中。 20 | - Footer:底部布局,自带默认样式,其下可嵌套任何元素,只能放在 Layout 中。 21 | 22 | ## 经典页面布局 23 | 24 | 25 | 26 | ## 自定义样式 27 | 28 |

根据业务需求选择自身样式

29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/Layout/index.module.less: -------------------------------------------------------------------------------- 1 | .layout { 2 | width: 100%; 3 | display: flex; 4 | flex-wrap: wrap; 5 | justify-content: space-between; 6 | } 7 | -------------------------------------------------------------------------------- /src/Layout/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useMemo, memo, FC } from 'react'; 2 | import './index.module.less'; 3 | 4 | interface layoutProps { 5 | children?: any; 6 | /** 7 | * @description 自定义样式 8 | * @default {} 9 | */ 10 | extraStyle?: Object; 11 | /** 12 | * @description Slider/Content横向栅格比例 13 | * @default Slider-3,Content-7 14 | */ 15 | row?: Number; 16 | } 17 | 18 | const Layout: FC = (props) => { 19 | const { children, extraStyle } = props; 20 | 21 | const propsStyles = useMemo(() => { 22 | if (extraStyle) { 23 | return extraStyle; 24 | } 25 | return {}; 26 | }, [extraStyle]); 27 | 28 | return ( 29 |
30 | {children} 31 |
32 | ); 33 | }; 34 | export default memo(Layout); 35 | -------------------------------------------------------------------------------- /src/LazyLoad/demos/index1.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import LazyLoad from '..'; 3 | 4 | export default function LazyLoadDemo1() { 5 | return ( 6 | <> 7 |

第一页

8 |

1

9 |

1

10 |

1

11 |

1

12 |

1

13 |

1

14 |

1

15 |

1

16 |

1

17 |

1

18 |

1

19 |

1

20 |

1

21 |

1

22 |

1

23 |

1

24 |

1

25 |

1

26 |

1

27 |

1

28 |

1

29 |

1

30 |

1

31 | 32 |
第二页
33 | 34 | 35 | 36 | 37 | 38 | 39 |
40 |

2

41 |

2

42 |

2

43 |

2

44 |

2

45 |

2

46 |

2

47 |

2

48 |

2

49 |

2

50 |

2

51 |

2

52 |

2

53 |

2

54 |

2

55 |

2

56 |

2

57 |

2

58 | 59 |
第三页
60 | 61 | 62 | 63 | 64 | 65 | 66 |
67 | 68 | ); 69 | } 70 | -------------------------------------------------------------------------------- /src/LazyLoad/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: LazyLoad 懒加载 3 | nav: 4 | title: 通用 5 | path: /common 6 | group: 7 | path: /common 8 | --- 9 | 10 | # LazyLoad 懒加载 11 | 12 | 对一些内容进行显示监测,出现时进行静态加载。 13 | 14 | ## 何时使用 15 | 16 | 常用于长列表 SPA 首屏下,对中下部分元素进行懒加载,实现页面加载优化。 17 | 18 | ## 基本使用 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/LazyLoad/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, memo, useEffect, useState, createRef } from 'react'; 2 | 3 | interface LazyLoadProps { 4 | children?: any; 5 | /** 6 | * @description 延迟加载 7 | * @default 0ms 8 | */ 9 | delay?: number; 10 | } 11 | 12 | const LazyLoad: FC = (props) => { 13 | const { children, delay } = props; 14 | const [showEl, setShowEl] = useState(false); 15 | const lazyRef = createRef(); 16 | let io: IntersectionObserver; //观察者 17 | 18 | useEffect(() => { 19 | io = new IntersectionObserver((entries) => elementObverse(entries)); 20 | io.observe(lazyRef.current as Element); 21 | }, []); 22 | 23 | const elementObverse = (entries: Array) => { 24 | //监听函数 25 | entries.forEach((entry: IntersectionObserverEntry) => { 26 | if (entry.isIntersecting) { 27 | if (delay) { 28 | setTimeout(() => { 29 | setShowEl(true); 30 | }, delay); 31 | } else { 32 | setShowEl(true); 33 | } 34 | } 35 | }); 36 | }; 37 | 38 | return ( 39 |
40 | {showEl && children} 41 |
42 | ); 43 | }; 44 | 45 | export default memo(LazyLoad); 46 | -------------------------------------------------------------------------------- /src/Menu/demos/index1.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Menu from '..'; 3 | import { AppstoreOutlined, MailOutlined, SettingOutlined } from '@ant-design/icons'; 4 | 5 | export default function MenuDemos1() { 6 | const getItem = ( 7 | label: string, 8 | key: string | number, 9 | level: string | number, 10 | icon?: JSX.Element | null, 11 | children?: Array | null, 12 | ) => { 13 | return { 14 | label, 15 | key, 16 | level, 17 | icon, 18 | children, 19 | }; 20 | }; 21 | const items = [ 22 | getItem('Navigation One', 'sub1', 1, , [ 23 | getItem('Option 1', '1', 2), 24 | getItem('Option 2', '2', 2), 25 | getItem('Option 3', '3', 2), 26 | getItem('Option 4', '4', 2), 27 | ]), 28 | getItem('Navigation Two', 'sub2', 1, , [ 29 | getItem('Option 5', '5', 2), 30 | getItem('Option 6', '6', 2, null, [ 31 | getItem('Option 13', '13', 3), 32 | getItem('Option 14', '14', 3), 33 | ]), 34 | getItem('Submenu', 'sub3', 2, null, [ 35 | getItem('Option 15', '15', 3), 36 | getItem('Option 16', '16', 3), 37 | ]), 38 | ]), 39 | getItem('Navigation Three', 'sub4', 1, , [ 40 | getItem('Option 9', '9', 2), 41 | getItem('Option 10', '10', 2), 42 | getItem('Option 11', '11', 2), 43 | getItem('Option 12', '12', 2), 44 | ]), 45 | ]; 46 | const handleRouteChange = (page: string) => { 47 | console.log(page); 48 | }; 49 | return ; 50 | } 51 | -------------------------------------------------------------------------------- /src/Menu/demos/index2.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Menu from '..'; 3 | import { AppstoreOutlined, MailOutlined, SettingOutlined } from '@ant-design/icons'; 4 | 5 | export default function MenuDemos1() { 6 | const getItem = ( 7 | label: string, 8 | key: string | number, 9 | level: string | number, 10 | icon?: JSX.Element | null, 11 | children?: Array | null, 12 | ) => { 13 | return { 14 | label, 15 | key, 16 | level, 17 | icon, 18 | children, 19 | }; 20 | }; 21 | const items = [ 22 | getItem('Navigation One', 'sub1', 1, , [ 23 | getItem('Option 1', '1', 2), 24 | getItem('Option 2', '2', 2), 25 | getItem('Option 3', '3', 2), 26 | getItem('Option 4', '4', 2), 27 | ]), 28 | getItem('Navigation Two', 'sub2', 1, , [ 29 | getItem('Option 5', '5', 2), 30 | getItem('Option 6', '6', 2, null, [ 31 | getItem('Option 13', '13', 3), 32 | getItem('Option 14', '14', 3), 33 | ]), 34 | getItem('Submenu', 'sub3', 2, null, [ 35 | getItem('Option 15', '15', 3), 36 | getItem('Option 16', '16', 3), 37 | ]), 38 | ]), 39 | getItem('Navigation Three', 'sub4', 1, , [ 40 | getItem('Option 9', '9', 2), 41 | getItem('Option 10', '10', 2), 42 | getItem('Option 11', '11', 2), 43 | getItem('Option 12', '12', 2), 44 | ]), 45 | ]; 46 | const handleRouteChange = (page: string) => { 47 | console.log(page); 48 | }; 49 | return ; 50 | } 51 | -------------------------------------------------------------------------------- /src/Menu/demos/index3.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Menu from '..'; 3 | import { AppstoreOutlined, MailOutlined, SettingOutlined } from '@ant-design/icons'; 4 | 5 | export default function MenuDemos1() { 6 | const getItem = ( 7 | label: string, 8 | key: string | number, 9 | level: string | number, 10 | icon?: JSX.Element | null, 11 | children?: Array | null, 12 | ) => { 13 | return { 14 | label, 15 | key, 16 | level, 17 | icon, 18 | children, 19 | }; 20 | }; 21 | const items = [ 22 | getItem('Navigation One', 'sub1', 1, , [ 23 | getItem('Option 1', '1', 2), 24 | getItem('Option 2', '2', 2), 25 | getItem('Option 3', '3', 2), 26 | getItem('Option 4', '4', 2), 27 | ]), 28 | getItem('Navigation Two', 'sub2', 1, , [ 29 | getItem('Option 5', '5', 2), 30 | getItem('Option 6', '6', 2, null, [ 31 | getItem('Option 13', '13', 3), 32 | getItem('Option 14', '14', 3), 33 | ]), 34 | getItem('Submenu', 'sub3', 2, null, [ 35 | getItem('Option 15', '15', 3), 36 | getItem('Option 16', '16', 3), 37 | ]), 38 | ]), 39 | getItem('Navigation Three', 'sub4', 1, , [ 40 | getItem('Option 9', '9', 2), 41 | getItem('Option 10', '10', 2), 42 | getItem('Option 11', '11', 2), 43 | getItem('Option 12', '12', 2), 44 | ]), 45 | ]; 46 | const handleRouteChange = (page: string) => { 47 | console.log(page); 48 | }; 49 | return ; 50 | } 51 | -------------------------------------------------------------------------------- /src/Menu/demos/index4.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Menu from '..'; 3 | import { AppstoreOutlined, MailOutlined, SettingOutlined } from '@ant-design/icons'; 4 | 5 | export default function MenuDemos1() { 6 | const getItem = ( 7 | label: string, 8 | key: string | number, 9 | level: string | number, 10 | icon?: JSX.Element | null, 11 | children?: Array | null, 12 | ) => { 13 | return { 14 | label, 15 | key, 16 | level, 17 | icon, 18 | children, 19 | }; 20 | }; 21 | const items = [ 22 | getItem('Navigation One', 'sub1', 1, , [ 23 | getItem('Option 1', '1', 2), 24 | getItem('Option 2', '2', 2), 25 | getItem('Option 3', '3', 2), 26 | getItem('Option 4', '4', 2), 27 | ]), 28 | getItem('Navigation Two', 'sub2', 1, , [ 29 | getItem('Option 5', '5', 2), 30 | getItem('Option 6', '6', 2, null, [ 31 | getItem('Option 13', '13', 3), 32 | getItem('Option 14', '14', 3), 33 | ]), 34 | getItem('Submenu', 'sub3', 2, null, [ 35 | getItem('Option 15', '15', 3), 36 | getItem('Option 16', '16', 3), 37 | ]), 38 | ]), 39 | getItem('Navigation Three', 'sub4', 1, , [ 40 | getItem('Option 9', '9', 2), 41 | getItem('Option 10', '10', 2), 42 | getItem('Option 11', '11', 2), 43 | getItem('Option 12', '12', 2), 44 | ]), 45 | ]; 46 | const handleRouteChange = (page: string) => { 47 | console.log(page); 48 | }; 49 | return ; 50 | } 51 | -------------------------------------------------------------------------------- /src/Menu/demos/index5.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Menu from '..'; 3 | import Layout from '../../Layout'; 4 | import Header from '../../Layout/Header'; 5 | import Slider from '../../Layout/Slider'; 6 | import Content from '../../Layout/Content'; 7 | import Footer from '../../Layout/Footer'; 8 | import { AppstoreOutlined, MailOutlined, SettingOutlined } from '@ant-design/icons'; 9 | 10 | export default function MenuDemos1() { 11 | const getItem = ( 12 | label: string, 13 | key: string | number, 14 | level: string | number, 15 | icon?: JSX.Element | null, 16 | children?: Array | null, 17 | ) => { 18 | return { 19 | label, 20 | key, 21 | level, 22 | icon, 23 | children, 24 | }; 25 | }; 26 | const items = [ 27 | getItem('Navigation One', 'sub1', 1, , [ 28 | getItem('Option 1', '1', 2), 29 | getItem('Option 2', '2', 2), 30 | getItem('Option 3', '3', 2), 31 | getItem('Option 4', '4', 2), 32 | ]), 33 | getItem('Navigation Two', 'sub2', 1, , [ 34 | getItem('Option 5', '5', 2), 35 | getItem('Option 6', '6', 2, null, [ 36 | getItem('Option 13', '13', 3), 37 | getItem('Option 14', '14', 3), 38 | ]), 39 | getItem('Submenu', 'sub3', 2, null, [ 40 | getItem('Option 15', '15', 3), 41 | getItem('Option 16', '16', 3), 42 | ]), 43 | ]), 44 | getItem('Navigation Three', 'sub4', 1, , [ 45 | getItem('Option 9', '9', 2), 46 | getItem('Option 10', '10', 2), 47 | getItem('Option 11', '11', 2), 48 | getItem('Option 12', '12', 2), 49 | ]), 50 | ]; 51 | const handleRouteChange = (page: string) => { 52 | console.log(page); 53 | }; 54 | return ( 55 | 56 |
header
57 | 58 | 59 | 60 | 61 | content 62 | 63 |
footer
64 | 65 | ); 66 | } 67 | -------------------------------------------------------------------------------- /src/Menu/demos/index6.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Menu from '..'; 3 | import { AppstoreOutlined, MailOutlined, SettingOutlined } from '@ant-design/icons'; 4 | 5 | export default function MenuDemos1() { 6 | const getItem = ( 7 | label: string, 8 | key: string | number, 9 | level: string | number, 10 | icon?: JSX.Element | null, 11 | children?: Array | null, 12 | ) => { 13 | return { 14 | label, 15 | key, 16 | level, 17 | icon, 18 | children, 19 | }; 20 | }; 21 | const items = [ 22 | getItem('Navigation One', 'sub1', 1, , [ 23 | getItem('Option 1', '1', 2), 24 | getItem('Option 2', '2', 2), 25 | getItem('Option 3', '3', 2), 26 | getItem('Option 4', '4', 2), 27 | ]), 28 | getItem('Navigation Two', 'sub2', 1, , [ 29 | getItem('Option 5', '5', 2), 30 | getItem('Option 6', '6', 2, null, [ 31 | getItem('Option 13', '13', 3), 32 | getItem('Option 14', '14', 3), 33 | ]), 34 | getItem('Submenu', 'sub3', 2, null, [ 35 | getItem('Option 15', '15', 3), 36 | getItem('Option 16', '16', 3), 37 | ]), 38 | ]), 39 | getItem('Navigation Three', 'sub4', 1, , [ 40 | getItem('Option 9', '9', 2), 41 | getItem('Option 10', '10', 2), 42 | getItem('Option 11', '11', 2), 43 | getItem('Option 12', '12', 2), 44 | ]), 45 | ]; 46 | const handleRouteChange = (page: string) => { 47 | console.log(page); 48 | }; 49 | return ; 50 | } 51 | -------------------------------------------------------------------------------- /src/Menu/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Menu 导航菜单 3 | nav: 4 | title: 通用 5 | path: /common 6 | group: 7 | path: /common 8 | --- 9 | 10 | # Menu 导航菜单 11 | 12 | 为页面和功能提供导航的菜单列表。 13 | 14 | ## 何时使用 15 | 16 | 导航菜单是一个网站的灵魂,用户依赖导航在各个页面中进行跳转。一般分为顶部导航和侧边导航,顶部导航提供全局性的类目和功能,侧边导航提供多级结构来收纳和排列网站架构。 17 | 18 | ## 基本使用 19 | 20 | 21 | 22 | ## 自定义宽度 23 | 24 | 25 | 26 | ## 深色风格 27 | 28 | 29 | 30 | ## 手风琴风格 31 | 32 | 33 | 34 | ## 交互 35 | 36 | - 应用于快速布局 Layout-Slider 侧边栏中 37 | 38 | 39 | 40 | ## 默认展开 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/Menu/index.module.less: -------------------------------------------------------------------------------- 1 | .menu { 2 | width: 220px; 3 | padding: 2px 0px; 4 | color: black; 5 | -moz-user-select: none; 6 | -webkit-user-select: none; 7 | user-select: none; 8 | .menuOptions { 9 | padding: 0 10px; 10 | overflow: hidden; 11 | font-size: 14px; 12 | line-height: 50px; 13 | cursor: pointer; 14 | transition: 0.2s linear; 15 | .fatherTitle { 16 | display: flex; 17 | align-items: center; 18 | justify-content: space-between; 19 | height: 50px; 20 | &:hover { 21 | color: #1890ff; 22 | } 23 | } 24 | .activeFatherTitle { 25 | display: flex; 26 | align-items: center; 27 | justify-content: space-between; 28 | height: 50px; 29 | color: #1890ff; 30 | &:hover { 31 | color: #1890ff; 32 | } 33 | } 34 | .left { 35 | i { 36 | margin-right: 10px; 37 | } 38 | } 39 | } 40 | .activeMenuOptions { 41 | display: flex; 42 | flex-direction: column; 43 | box-sizing: border-box; 44 | padding-left: 5px; 45 | overflow: hidden; 46 | color: #1890ff; 47 | font-size: 14px; 48 | line-height: 50px; 49 | background-color: #e6f7ff; 50 | cursor: pointer; 51 | transition: 0.2s linear; 52 | :after { 53 | width: 3px; 54 | height: 50px; 55 | background-color: #1890ff; 56 | content: ''; 57 | } 58 | } 59 | .childMenuOptions { 60 | display: flex; 61 | flex-direction: column; 62 | box-sizing: border-box; 63 | padding-left: 5px; 64 | overflow: hidden; 65 | font-size: 14px; 66 | line-height: 50px; 67 | cursor: pointer; 68 | transition: 0.2s linear; 69 | } 70 | } 71 | .darkMenu { 72 | box-sizing: border-box; 73 | width: 200px; 74 | padding: 2px 0px; 75 | color: #ffffffa6; 76 | background-color: #001529; 77 | -moz-user-select: none; 78 | -webkit-user-select: none; 79 | user-select: none; 80 | .menuOptions { 81 | padding: 0 10px; 82 | overflow: hidden; 83 | font-size: 14px; 84 | line-height: 50px; 85 | cursor: pointer; 86 | transition: 0.2s linear; 87 | .fatherTitle { 88 | display: flex; 89 | align-items: center; 90 | justify-content: space-between; 91 | height: 50px; 92 | &:hover { 93 | color: #fff; 94 | } 95 | } 96 | .activeFatherTitle { 97 | display: flex; 98 | align-items: center; 99 | justify-content: space-between; 100 | box-sizing: border-box; 101 | height: 50px; 102 | color: #fff; 103 | &:hover { 104 | color: #fff; 105 | } 106 | } 107 | .left { 108 | i { 109 | margin-right: 10px; 110 | } 111 | } 112 | } 113 | .activeMenuOptions { 114 | position: relative; 115 | display: flex; 116 | flex-direction: column; 117 | box-sizing: border-box; 118 | padding-left: 5px; 119 | overflow: hidden; 120 | color: #fff; 121 | font-size: 14px; 122 | line-height: 50px; 123 | background-color: #1890ff; 124 | cursor: pointer; 125 | transition: 0.2s linear; 126 | } 127 | .childMenuOptions { 128 | position: relative; 129 | display: flex; 130 | flex-direction: column; 131 | box-sizing: border-box; 132 | padding-left: 5px; 133 | overflow: hidden; 134 | font-size: 14px; 135 | line-height: 50px; 136 | cursor: pointer; 137 | transition: 0.2s linear; 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/Menu/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useState, useEffect, memo, useCallback, useMemo } from 'react'; 2 | import { CaretUpOutlined, CaretDownOutlined } from '@ant-design/icons'; 3 | import './index.module.less'; 4 | 5 | interface MenuProps { 6 | /** 7 | * @description 配置对象 8 | * @default {} 9 | */ 10 | items: Array; 11 | /** 12 | * @description 自定义宽度 13 | * @default 220px 14 | */ 15 | width?: string | number; 16 | /** 17 | * @description 深度模式 18 | * @default false 19 | */ 20 | dark?: Boolean; 21 | /** 22 | * @description 手风琴 23 | * @default false 24 | */ 25 | ableToggle?: Boolean; 26 | /** 27 | * @description 默认展开 28 | * @default false 29 | */ 30 | defaultOpen?: Boolean; 31 | /** 32 | * @description 切换菜单回调函数 33 | */ 34 | handleRouteChange?: Function; 35 | } 36 | interface MenuHeightProps { 37 | key: string; 38 | height: string; 39 | childNum: number | string; 40 | level: number | number; 41 | children?: Array; 42 | } 43 | interface RenderOptions { 44 | label: string; 45 | key: string | number; 46 | level?: string | number; 47 | icon?: JSX.Element | null; 48 | children?: Array | null | undefined; 49 | } 50 | 51 | const Menu: FC = (props) => { 52 | const [nowActiveMainKey, setNowActiveMainKey] = useState(''); //选中的一级菜单key 53 | const [nowActiveKey, setNowActiveKey] = useState(''); //选中的子菜单key 54 | const [parentMenuHeightList, setParentMenuHeightList] = useState({}); //父菜单高度集合 55 | 56 | const { items, width, dark, ableToggle, defaultOpen, handleRouteChange } = props; 57 | 58 | useEffect(() => { 59 | const initList = initParentMenuHeight(items, {}, ''); 60 | if (defaultOpen) { 61 | //默认展开 62 | for (var key in initList) { 63 | initList[key].height = initList[key].childNum + 1; 64 | if (initList[key].children.length > 0) { 65 | initList[key].children.map( 66 | (item: any) => (item.height = (item.childNum + 1) * 50 + 'px'), 67 | ); 68 | initList[key].height += initList[key].children.reduce( 69 | (pre: MenuHeightProps, next: MenuHeightProps) => { 70 | return (pre.childNum as number) + (next.childNum as number); 71 | }, 72 | ); 73 | } 74 | initList[key].height = initList[key].height * 50 + 'px'; 75 | } 76 | } 77 | setParentMenuHeightList(initList); 78 | }, []); 79 | useEffect(() => { 80 | handleRouteChange && handleRouteChange(nowActiveKey); 81 | }, [nowActiveKey]); 82 | const initParentMenuHeight = ( 83 | item: Array, 84 | obj: any, 85 | fatherKey: string | number, 86 | ) => { 87 | //初始化父级菜单高度 88 | item.forEach((m) => { 89 | if (m.children) { 90 | if (m.level == 1) { 91 | obj[m.key] = { 92 | key: m.key, 93 | height: '50px', 94 | childNum: m.children.length, 95 | level: m.level, 96 | children: [], 97 | }; 98 | } else { 99 | obj[fatherKey]?.children.push({ 100 | key: m.key, 101 | height: '50px', 102 | childNum: m.children.length, 103 | level: m.level, 104 | }); 105 | } 106 | initParentMenuHeight(m.children, obj, m.level && m.level == 1 ? m.key : ''); 107 | } 108 | }); 109 | return obj; 110 | }; 111 | 112 | const toggleFirstMenu = (fMenu: RenderOptions, e: any) => { 113 | //点击父级菜单 114 | e.stopPropagation(); 115 | const selectKey = fMenu.key; 116 | const refreshObject = { ...parentMenuHeightList }; 117 | refreshObject[selectKey].height = 118 | refreshObject[selectKey].height == '50px' 119 | ? (refreshObject[selectKey].childNum + 1) * 50 + 'px' 120 | : '50px'; 121 | if (ableToggle) { 122 | //手风琴折叠 123 | if (refreshObject[selectKey].height !== '50px') { 124 | for (var key in refreshObject) { 125 | if (key !== selectKey) { 126 | refreshObject[key].height = '50px'; 127 | if (refreshObject[key].children) { 128 | refreshObject[key].children.map((item: MenuHeightProps) => (item.height = '50px')); 129 | } 130 | } 131 | } 132 | } 133 | } else { 134 | //普通折叠 135 | if (refreshObject[selectKey].children.length !== 0) { 136 | refreshObject[selectKey].children.forEach((c: MenuHeightProps) => { 137 | c.height = '50px'; 138 | }); 139 | } 140 | } 141 | setParentMenuHeightList(refreshObject); 142 | }; 143 | const toggleChildMenu = (cMenu: RenderOptions, e: any, fKey: string) => { 144 | //点击子级菜单 145 | if ((cMenu.level == 2 && !cMenu.children) || cMenu.level == 3) { 146 | setNowActiveMainKey(fKey); 147 | setNowActiveKey(cMenu.key as string); 148 | } 149 | if (cMenu.level == 2) { 150 | //二级菜单扩展切换 151 | const refreshObject = { ...parentMenuHeightList }; 152 | for (var key in refreshObject) { 153 | if ( 154 | refreshObject[key].children && 155 | refreshObject[key].children.findIndex( 156 | (item: MenuHeightProps) => item.key == cMenu.key, 157 | ) !== -1 158 | ) { 159 | //找出是哪个一级菜单的children 160 | const childIndex = refreshObject[key].children.findIndex( 161 | (item: MenuHeightProps) => item.key == cMenu.key, 162 | ); 163 | refreshObject[key].children[childIndex].height = 164 | refreshObject[key].children[childIndex].height == '50px' 165 | ? (refreshObject[key].children[childIndex].childNum + 1) * 50 + 'px' 166 | : '50px'; 167 | let parentHeight = 168 | (refreshObject[key].childNum - refreshObject[key].children.length) * 50 + 50; //改变子菜单高度后统计父菜单高度 169 | parentHeight += refreshObject[key].children.reduce( 170 | (pre: MenuHeightProps, next: MenuHeightProps) => { 171 | return Number(pre.height.split('px')[0]) + Number(next.height.split('px')[0]); 172 | }, 173 | ); 174 | refreshObject[key].height = parentHeight; 175 | } 176 | } 177 | setParentMenuHeightList(refreshObject); 178 | // setParentMenuHeightList((old: any) => { 179 | // for (var key in old) { 180 | // if ( 181 | // old[key].children && 182 | // old[key].children.findIndex((item: MenuHeightProps) => item.key == cMenu.key) !== -1 183 | // ) { 184 | // //找出是哪个一级菜单的children 185 | // const childIndex = old[key].children.findIndex( 186 | // (item: MenuHeightProps) => item.key == cMenu.key, 187 | // ); 188 | // old[key].children[childIndex].height = 189 | // old[key].children[childIndex].height == '50px' 190 | // ? (old[key].children[childIndex].childNum + 1) * 50 + 'px' 191 | // : '50px'; 192 | // let parentHeight = (old[key].childNum - old[key].children.length) * 50 + 50; //改变子菜单高度后统计父菜单高度 193 | // parentHeight += old[key].children.reduce( 194 | // (pre: MenuHeightProps, next: MenuHeightProps) => { 195 | // return Number(pre.height.split('px')[0]) + Number(next.height.split('px')[0]); 196 | // }, 197 | // ); 198 | // old[key].height = parentHeight; 199 | // } 200 | // } 201 | // return JSON.parse(JSON.stringify(old)); 202 | // }); 203 | } 204 | if (cMenu.level == 3) { 205 | for (var key in parentMenuHeightList) { 206 | if ( 207 | parentMenuHeightList[key].children && 208 | parentMenuHeightList[key].children.findIndex( 209 | (item: MenuHeightProps) => item.key == fKey, 210 | ) !== -1 211 | ) { 212 | setNowActiveMainKey(parentMenuHeightList[key].key); 213 | } 214 | } 215 | } 216 | }; 217 | const firstMenuHeight = (key: any) => { 218 | //第一级菜单高度 219 | if (parentMenuHeightList[key]) { 220 | return { 221 | height: parentMenuHeightList[key]?.height, 222 | }; 223 | } 224 | return { 225 | height: '50px', 226 | }; 227 | }; 228 | const childMenuHeight = useCallback( 229 | (key: any) => { 230 | //第二级菜单高度 231 | for (var i in parentMenuHeightList) { 232 | const childIndex = parentMenuHeightList[i].children?.findIndex( 233 | (item: RenderOptions) => item.key == key, 234 | ); 235 | if (childIndex !== -1) { 236 | return { 237 | height: parentMenuHeightList[i].children[childIndex].height, 238 | }; 239 | } 240 | } 241 | return { 242 | height: '50px', 243 | }; 244 | }, 245 | [parentMenuHeightList], 246 | ); 247 | const customWidth = useMemo(() => { 248 | if (width) { 249 | if (typeof width == 'string') { 250 | return { 251 | width: (width as string).includes('%') ? width : width + 'px', 252 | }; 253 | } else if (typeof width == 'number') { 254 | return { 255 | width: width + 'px', 256 | }; 257 | } 258 | } 259 | return { 260 | width: '220px', 261 | }; 262 | }, [width]); 263 | 264 | const renderChildOptions = (childM: RenderOptions): JSX.Element | any => { 265 | //传入level为1的children,进行子项递归 266 | if (childM.children) { 267 | return childM.children.map((m) => { 268 | return ( 269 |
270 |
274 |
i.key == nowActiveKey) !== -1 278 | ? 'activeFatherTitle' 279 | : 'fatherTitle' 280 | } 281 | onClick={(e) => toggleChildMenu(m, e, childM.key as string)} 282 | > 283 | {m.label} 284 | {m.children && 285 | (childMenuHeight(m.key).height == '50px' ? ( 286 | 287 | ) : ( 288 | 289 | ))} 290 |
291 |
292 | {m.children && renderChildOptions(m)} 293 |
294 |
295 |
296 | ); 297 | }); 298 | } 299 | }; 300 | 301 | return ( 302 |
303 | {items.map((m) => { 304 | return ( 305 |
306 |
307 |
toggleFirstMenu(m, e)} 310 | > 311 |
312 | {m.icon} 313 | {m.label} 314 |
315 | {firstMenuHeight(m.key).height == '50px' ? ( 316 | 317 | ) : ( 318 | 319 | )} 320 |
321 | <>{m.children && renderChildOptions(m)} 322 |
323 |
324 | ); 325 | })} 326 |
327 | ); 328 | }; 329 | 330 | export default memo(Menu); 331 | -------------------------------------------------------------------------------- /src/Pagination/demos/index1.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Pagination from '..'; 3 | 4 | export default function PatginationDemo1() { 5 | const changePageCallback = (pageNum: number) => { 6 | console.log(pageNum); 7 | }; 8 | return ; 9 | } 10 | -------------------------------------------------------------------------------- /src/Pagination/demos/index2.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Pagination from '..'; 3 | 4 | export default function PatginationDemo2() { 5 | const changePageCallback = (pageNum: number) => { 6 | console.log(pageNum); 7 | }; 8 | return ; 9 | } 10 | -------------------------------------------------------------------------------- /src/Pagination/demos/index3.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Pagination from '..'; 3 | 4 | export default function PatginationDemo3() { 5 | const options = [10, 20, 30, 50]; 6 | const changePageCallback = (pageNum: number) => { 7 | console.log(pageNum); 8 | }; 9 | return ( 10 | 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /src/Pagination/demos/index4.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Pagination from '..'; 3 | 4 | export default function PatginationDemo4() { 5 | const options = [10, 20, 30, 50]; 6 | const changePageCallback = (pageNum: number) => { 7 | console.log(pageNum); 8 | }; 9 | return ( 10 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /src/Pagination/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Pagination 分页器 3 | nav: 4 | title: 通用 5 | path: /common 6 | group: 7 | path: /common 8 | --- 9 | 10 | # Pagination 分页器 11 | 12 | 采用分页的形式分隔长列表,每次只加载一个页面。 13 | 14 | ## 何时使用 15 | 16 | - 当加载/渲染所有数据将花费很多时间时; 17 | - 可切换页码浏览数据。 18 | 19 | ## 基本使用 20 | 21 | 22 | 23 | ## 大数据分页 24 | 25 | 26 | 27 | ## 每页条数限制 28 | 29 | 30 | 31 | ## 输入框跳转 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/Pagination/index.module.less: -------------------------------------------------------------------------------- 1 | .pagination { 2 | display: flex; 3 | } 4 | .prev, 5 | .next, 6 | .numberBox { 7 | min-width: 30px; 8 | height: 30px; 9 | margin: 0 5px; 10 | font-size: 14px; 11 | line-height: 30px; 12 | text-align: center; 13 | border: 1px solid #ccc; 14 | border-radius: 5px; 15 | cursor: pointer; 16 | -moz-user-select: none; 17 | -o-user-select: none; 18 | -khtml-user-select: none; 19 | -webkit-user-select: none; 20 | -ms-user-select: none; 21 | user-select: none; 22 | } 23 | .numberBox:hover { 24 | color: #1890ff; 25 | border: 1px solid #1890ff; 26 | cursor: pointer; 27 | } 28 | .disabled { 29 | color: #c0c4cc; 30 | } 31 | .actived { 32 | color: #1890ff; 33 | border-color: #1890ff; 34 | } 35 | .pageSizeSelect { 36 | width: 80px; 37 | height: 30px; 38 | margin-left: 10px; 39 | padding: 0 5px; 40 | font-size: 14px; 41 | line-height: 30px; 42 | text-align: center; 43 | border: 1px solid #ccc; 44 | cursor: pointer; 45 | transition: 0.2s linear; 46 | -moz-user-select: none; 47 | -o-user-select: none; 48 | -khtml-user-select: none; 49 | -webkit-user-select: none; 50 | -ms-user-select: none; 51 | user-select: none; 52 | .size { 53 | margin-right: 5px; 54 | } 55 | .options { 56 | position: relative; 57 | top: 5px; 58 | display: flex; 59 | flex-direction: column; 60 | width: 80px; 61 | box-shadow: 0 0 20px rgb(218, 218, 218); 62 | transition: 0.2s linear; 63 | .option { 64 | font-size: 14px; 65 | line-height: 2em; 66 | text-align: center; 67 | &:hover { 68 | color: #1890ff; 69 | } 70 | } 71 | } 72 | &:hover { 73 | border: 1px solid #1890ff; 74 | } 75 | } 76 | .jumpBox { 77 | margin-left: 10px; 78 | .jump { 79 | width: 40px; 80 | height: 25px; 81 | margin: 0 5px; 82 | border: 1px solid #ccc; 83 | border-radius: 5px; 84 | outline-color: #1890ff; 85 | transition: 0.2s linear; 86 | &:hover { 87 | border-color: #1890ff; 88 | } 89 | &:active { 90 | border-color: #1890ff; 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/Pagination/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, FC, useMemo, memo } from 'react'; 2 | import { EllipsisOutlined, LeftOutlined, RightOutlined, DownOutlined } from '@ant-design/icons'; 3 | import { Select } from '..'; 4 | import './index.module.less'; 5 | 6 | interface PaginationProps { 7 | /** 8 | * @description 总数据条数 9 | * @default 0 10 | */ 11 | total: number; 12 | /** 13 | * @description 显示每页条数Select 14 | * @default false 15 | */ 16 | showSizeChanger?: Boolean; 17 | /** 18 | * @description 每页条数配置 19 | * @default 每页10条数据 20 | */ 21 | pageSizeOptions?: Array; 22 | /** 23 | * @description 改变页码后的回调 24 | * @default {} 25 | */ 26 | showJumpInput?: Boolean; 27 | /** 28 | * @description 显示跳转页面输入框 29 | * @default false 30 | */ 31 | changePageCallback: Function; 32 | } 33 | const Pagination: FC = (props) => { 34 | const { changePageCallback, total, pageSizeOptions, showJumpInput, showSizeChanger } = props; 35 | 36 | const [nowIndex, setNowIndex] = useState(1); 37 | const [pageRenderArray, setPageRenderArray] = useState>([]); 38 | const [sizePage, setSizePage] = useState(pageSizeOptions ? pageSizeOptions[0] : 10); 39 | const [showSizeOptions, setShowSizeOptions] = useState(false); 40 | 41 | const totalPage = useMemo(() => { 42 | setNowIndex(1); 43 | if (Math.ceil(total / sizePage) > 6) { 44 | setPageRenderArray([2, 3, 4, 5, 6]); 45 | } else { 46 | if (Math.ceil(total / sizePage) > 2) { 47 | const array = new Array((Math.ceil(total / sizePage) as number) - 2).fill(0); 48 | array.forEach((item, index) => { 49 | array[index] = index + 2; 50 | }); 51 | setPageRenderArray(array); 52 | } else { 53 | setPageRenderArray([]); 54 | } 55 | } 56 | return Math.ceil(total / sizePage); 57 | }, [total, sizePage]); 58 | useEffect(() => { 59 | console.log(typeof nowIndex); 60 | }, [nowIndex]); 61 | 62 | //点击改页码 63 | const changePage = (pageNum: number) => { 64 | return () => { 65 | //小型分页器 66 | if (totalPage <= 6) { 67 | changePageCallback(pageNum); 68 | return setNowIndex(pageNum); 69 | } 70 | if (pageNum > 4 && pageNum <= totalPage - 4) { 71 | setPageRenderArray([pageNum - 2, pageNum - 1, pageNum, pageNum + 1, pageNum + 2]); 72 | } 73 | //页码为1的情况 74 | if (pageNum <= 4) { 75 | setPageRenderArray([2, 3, 4, 5, 6]); 76 | } 77 | //页码到倒数第四页内的情况 78 | if (pageNum > totalPage - 4) { 79 | setPageRenderArray([ 80 | totalPage - 5, 81 | totalPage - 4, 82 | totalPage - 3, 83 | totalPage - 2, 84 | totalPage - 1, 85 | ]); 86 | } 87 | setNowIndex(pageNum); 88 | changePageCallback(pageNum); 89 | }; 90 | }; 91 | //向前翻一页 92 | const prevPage = () => { 93 | if (nowIndex === 1) { 94 | return; 95 | } 96 | setNowIndex(nowIndex - 1); 97 | if (totalPage > 6) { 98 | if (nowIndex > totalPage - 3) { 99 | return; 100 | } else if (nowIndex > 4) { 101 | setPageRenderArray( 102 | pageRenderArray.map((item) => { 103 | return item - 1; 104 | }), 105 | ); 106 | } else if (nowIndex - 5 <= 4) { 107 | //开头几页翻页的情况,回到第一页 108 | setPageRenderArray([2, 3, 4, 5, 6]); 109 | } 110 | } 111 | changePageCallback(nowIndex - 1); 112 | }; 113 | //向后翻一页 114 | const nextPage = () => { 115 | if (nowIndex === totalPage) { 116 | return; 117 | } 118 | setNowIndex(nowIndex + 1); 119 | if (totalPage > 6) { 120 | if (nowIndex + 5 > totalPage) { 121 | setPageRenderArray([ 122 | totalPage - 5, 123 | totalPage - 4, 124 | totalPage - 3, 125 | totalPage - 2, 126 | totalPage - 1, 127 | ]); 128 | } else if (nowIndex < 4) { 129 | return; 130 | } else if (nowIndex + 5 < totalPage) { 131 | setPageRenderArray( 132 | pageRenderArray.map((item) => { 133 | return item + 1; 134 | }), 135 | ); 136 | } 137 | } 138 | changePageCallback(nowIndex + 1); 139 | }; 140 | //向前翻五页 141 | const prevFivePage = () => { 142 | var updateIndex: number = 0; 143 | if (nowIndex - 5 <= 4) { 144 | //开头几页翻页的情况,回到第一页 145 | setPageRenderArray([2, 3, 4, 5, 6]); 146 | updateIndex = nowIndex - 5 <= 1 ? 1 : nowIndex - 5; 147 | } else if (nowIndex + 5 > totalPage) { 148 | setPageRenderArray([nowIndex - 7, nowIndex - 6, nowIndex - 5, nowIndex - 4, nowIndex - 3]); 149 | updateIndex = nowIndex - 5; 150 | } else if (nowIndex - 5 > 4) { 151 | //中间翻页的情况 152 | setPageRenderArray( 153 | pageRenderArray.map((item) => { 154 | return item - 5; 155 | }), 156 | ); 157 | updateIndex = nowIndex - 5; 158 | } 159 | setNowIndex(updateIndex); 160 | changePageCallback(updateIndex); 161 | }; 162 | //向后翻五页 163 | const nextFivePage = () => { 164 | var updateIndex: number = 0; 165 | if (nowIndex + 7 >= totalPage) { 166 | setPageRenderArray([ 167 | totalPage - 5, 168 | totalPage - 4, 169 | totalPage - 3, 170 | totalPage - 2, 171 | totalPage - 1, 172 | ]); 173 | updateIndex = nowIndex + 5 > totalPage ? totalPage : nowIndex + 5; 174 | } else if (nowIndex - 5 < 0) { 175 | setPageRenderArray([nowIndex + 3, nowIndex + 4, nowIndex + 5, nowIndex + 6, nowIndex + 7]); 176 | updateIndex = nowIndex + 5; 177 | } else if (nowIndex + 5 < totalPage) { 178 | setPageRenderArray( 179 | pageRenderArray.map((item) => { 180 | return item + 5; 181 | }), 182 | ); 183 | updateIndex = nowIndex + 5; 184 | } 185 | setNowIndex(updateIndex); 186 | changePageCallback(updateIndex); 187 | }; 188 | //跳页 189 | const jumpPageNum = (e: any) => { 190 | if (e.keyCode === 13) { 191 | const jumpPage = Number(e.target.value); 192 | if (jumpPage > totalPage || jumpPage < 0 || isNaN(jumpPage)) { 193 | //超出页码范围,不挑 194 | return (e.target.value = ''); 195 | } 196 | if (jumpPage > 6 && jumpPage < totalPage - 6) { 197 | setPageRenderArray([jumpPage - 2, jumpPage - 1, jumpPage, jumpPage + 1, jumpPage + 2]); 198 | } else if (jumpPage - 5 < 0) { 199 | setPageRenderArray([2, 3, 4, 5, 6]); 200 | } else if (jumpPage + 5 > totalPage) { 201 | setPageRenderArray([ 202 | totalPage - 5, 203 | totalPage - 4, 204 | totalPage - 3, 205 | totalPage - 2, 206 | totalPage - 1, 207 | ]); 208 | } 209 | setNowIndex(jumpPage); 210 | changePageCallback(jumpPage); 211 | e.target.value = ''; 212 | } 213 | }; 214 | //select回调 215 | const handleSelectCallback = (pageSize: number) => { 216 | console.log(pageSize); 217 | setSizePage(pageSize); 218 | }; 219 | 220 | return ( 221 |
222 |
223 | 224 |
225 |
226 | 1 227 |
228 | {nowIndex > 4 && totalPage > 6 && ( 229 |
230 | 231 |
232 | )} 233 | 234 | {totalPage <= 4 && 235 | pageRenderArray.length >= 1 && 236 | pageRenderArray.map((index) => { 237 | return ( 238 |
243 | {index + 2} 244 |
245 | ); 246 | })} 247 | {totalPage > 4 && 248 | pageRenderArray.map((item, index) => { 249 | { 250 | return ( 251 |
256 | {item} 257 |
258 | ); 259 | } 260 | })} 261 | {totalPage - nowIndex >= 4 && totalPage > 6 && ( 262 |
263 | 264 |
265 | )} 266 | {totalPage > 1 && ( 267 |
271 | {totalPage} 272 |
273 | )} 274 |
278 | 279 |
280 | {/* { 281 | Array.isArray(pageSizeOptions) && showSizeChanger 282 | && 283 |
setShowSizeOptions(!showSizeOptions)}> 284 | <> 285 | {sizePage} 条/页 286 | 287 | 288 | 289 | { 290 | showSizeOptions 291 | && 292 |
293 | { 294 | pageSizeOptions.map(s => { 295 | return ( 296 |
setSizePage(s as number)}> 297 | {s} 条/页 298 |
299 | ) 300 | }) 301 | } 302 |
303 | } 304 |
305 | } */} 306 | {Array.isArray(pageSizeOptions) && showSizeChanger && ( 307 | 322 | 323 |
324 | )} 325 | 326 | ); 327 | }; 328 | export default memo(Pagination); 329 | -------------------------------------------------------------------------------- /src/Radio/RadioGroup/index.module.less: -------------------------------------------------------------------------------- 1 | .radioGroup { 2 | display: flex; 3 | align-items: center; 4 | height: 32px; 5 | .radioBox { 6 | display: inline-block; 7 | height: 32px; 8 | margin: 0 5px; 9 | cursor: pointer; 10 | .radio { 11 | cursor: pointer !important; 12 | } 13 | .disabledRadio { 14 | cursor: not-allowed; 15 | } 16 | .radioLabel { 17 | font-size: 14px; 18 | } 19 | .disabledLabel { 20 | color: #00000040; 21 | font-size: 14px; 22 | } 23 | } 24 | .addOption { 25 | display: flex; 26 | flex-direction: row; 27 | } 28 | .groupDisabledStyle { 29 | display: inline-block; 30 | height: 32px; 31 | margin: 0; 32 | padding: 0 20px; 33 | color: #000000d9; 34 | font-size: 14px; 35 | line-height: 30px; 36 | background: #00000040; 37 | border: 1px solid #d9d9d9; 38 | cursor: pointer; 39 | opacity: 0.5; 40 | } 41 | .groupStyle { 42 | display: inline-block; 43 | height: 32px; 44 | margin: 0; 45 | padding: 0 20px; 46 | color: #000000d9; 47 | font-size: 14px; 48 | line-height: 30px; 49 | background: #fff; 50 | border: 1px solid #d9d9d9; 51 | cursor: pointer; 52 | transition: 0.4s linear; 53 | &:hover { 54 | color: #1890ff; 55 | } 56 | } 57 | .groupActive { 58 | display: inline-block; 59 | height: 32px; 60 | margin: 0; 61 | padding: 0 20px; 62 | color: #fff; 63 | font-size: 14px; 64 | line-height: 30px; 65 | background: #1890ff; 66 | border: 1px solid #d9d9d9; 67 | cursor: pointer; 68 | transition: 0.4s linear; 69 | &:hover { 70 | opacity: 0.7; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Radio/RadioGroup/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, useState, useEffect, memo, useCallback } from 'react'; 2 | import Input from '@/Input'; 3 | import './index.module.less'; 4 | 5 | interface RadioGroupProps { 6 | children: Array; 7 | /** 8 | * @description 默认值 9 | * @default 0 10 | */ 11 | value?: Number; 12 | canAddOption?: Boolean; 13 | boxStyle?: Boolean; 14 | onChange?: Function; 15 | } 16 | interface RadioProps { 17 | children: string; 18 | disabled: Boolean; 19 | } 20 | 21 | const RadioGroup: FC = (props) => { 22 | const { children, value, canAddOption, boxStyle, onChange } = props; 23 | 24 | const [selectIndex, setSelectIndex] = useState(value || 0); //选中索引 25 | const [renderOptions, setRenderOptions] = useState(children); 26 | const [addOptionVal, setAddOptionVal] = useState(''); 27 | const [showAddOption, setShowAddOption] = useState(canAddOption && false); 28 | 29 | useEffect(() => { 30 | console.log(boxStyle); 31 | }); 32 | const changeOptions = (item: RadioProps, i: number, e: any) => { 33 | if (item.disabled) return; 34 | e && e.stopPropagation(); 35 | setSelectIndex(i); 36 | onChange && onChange(item, i); 37 | canAddOption && setShowAddOption(false); 38 | }; 39 | const addOptions = () => { 40 | //新增options 41 | setSelectIndex(renderOptions.length); 42 | setShowAddOption(true); 43 | }; 44 | const handleKeyDown = (e: any) => { 45 | //新增确认 46 | if (e.keyCode == 13 && addOptionVal) { 47 | setRenderOptions((old) => { 48 | const addOption = { 49 | props: { 50 | children: addOptionVal, 51 | }, 52 | }; 53 | return [...old, addOption]; 54 | }); 55 | setShowAddOption(false); 56 | } 57 | }; 58 | const handleIptChange = (val: string) => { 59 | //新增输入框 60 | setAddOptionVal(val); 61 | }; 62 | const boxStyleClassName = useCallback( 63 | (props: RadioProps, i: number) => { 64 | if (props.disabled) { 65 | return 'groupDisabledStyle'; 66 | } 67 | if (i == selectIndex) { 68 | return 'groupActive'; 69 | } 70 | return 'groupStyle'; 71 | }, 72 | [children, boxStyle, value, selectIndex], 73 | ); 74 | 75 | return ( 76 |
77 | {renderOptions.map((item: any, index: number) => { 78 | return boxStyle ? ( 79 |
changeOptions(item.props, index, e)} 84 | > 85 | {item.props.children} 86 |
87 | ) : ( 88 |
changeOptions(item.props, index, e)} 93 | > 94 | 95 | {item.props.children} 96 | 97 | 104 |
105 | ); 106 | })} 107 | { 108 | //新增Options项(优雅之王) 109 | canAddOption ? ( 110 | boxStyle ? ( 111 |
112 |
116 | More... 117 |
118 | {showAddOption && ( 119 | 120 | )} 121 |
122 | ) : ( 123 |
124 |
125 | More... 126 | 132 |
133 | {showAddOption && ( 134 | 135 | )} 136 |
137 | ) 138 | ) : ( 139 | <> 140 | ) 141 | } 142 |
143 | ); 144 | }; 145 | 146 | export default memo(RadioGroup); 147 | -------------------------------------------------------------------------------- /src/Radio/demos/index1.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Radio from '..'; 3 | import RadioGroup from '../RadioGroup'; 4 | 5 | export default function RadioBoxDemo1() { 6 | const onChange = (a: string, b: number) => { 7 | console.log(a, b); 8 | }; 9 | return ( 10 | <> 11 | 12 | Apple 13 | Orange 14 | Watch 15 | 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /src/Radio/demos/index2.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Radio from '..'; 3 | import RadioGroup from '../RadioGroup'; 4 | 5 | export default function RadioBoxDemo1() { 6 | const onChange = (a: string, b: number) => { 7 | console.log(a, b); 8 | }; 9 | return ( 10 | <> 11 | 12 | Apple 13 | Orange 14 | Watch 15 | 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /src/Radio/demos/index3.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Radio from '..'; 3 | import RadioGroup from '../RadioGroup'; 4 | 5 | export default function RadioBoxDemo1() { 6 | const onChange = (a: string, b: number) => { 7 | console.log(a, b); 8 | }; 9 | return ( 10 | <> 11 | 12 | Apple 13 | Orange 14 | Watch 15 | 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /src/Radio/demos/index4.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Radio from '..'; 3 | import RadioGroup from '../RadioGroup'; 4 | 5 | export default function RadioBoxDemo1() { 6 | const onChange = (a: string, b: number) => { 7 | console.log(a, b); 8 | }; 9 | return ( 10 | <> 11 | 12 | Apple 13 | Orange 14 | Watch 15 | 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /src/Radio/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Radio 单选框 3 | nav: 4 | title: 通用 5 | path: /common 6 | group: 7 | path: /common 8 | --- 9 | 10 | # Radio 单选框 11 | 12 | 单选框。 13 | 14 | ## 何时使用 15 | 16 | - 用于在多个备选项中选中单个状态。 17 | - 和 Select 的区别是,Radio 所有选项默认可见,方便用户在比较中选择,因此选项不宜过多。 18 | 19 | ## 基本使用 20 | 21 | 22 | 23 | ## 禁用 24 | 25 | 26 | 27 | ## 扩展性 28 | 29 | 30 | 31 | ## 盒子样式单选 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/Radio/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC, memo } from 'react'; 2 | 3 | interface RadioProps { 4 | children: any; 5 | /** 6 | * @description 默认选中索引 7 | * @default 0 8 | */ 9 | value?: Number; 10 | /** 11 | * @description 禁用 12 | * @default 0 13 | */ 14 | disabled?: Boolean; 15 | /** 16 | * @description 支持手动扩展 17 | * @default false 18 | */ 19 | canAddOption?: Boolean; 20 | /** 21 | * @description 方形样式 22 | * @default false 23 | */ 24 | boxStyle?: Boolean; 25 | /** 26 | * @description 选项改变回调函数 27 | */ 28 | onChange?: Function; 29 | } 30 | const Radio: FC = (props) => { 31 | const { children } = props; 32 | return
{children}
; 33 | }; 34 | 35 | export default memo(Radio); 36 | -------------------------------------------------------------------------------- /src/Select/demos/index1.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Select from '..'; 3 | /** 4 | * transform: true 5 | */ 6 | export default function SelectDemo1() { 7 | const option = [ 8 | { 9 | label: 'Mucy', 10 | value: 'mucy', 11 | }, 12 | { 13 | label: 'Mike', 14 | value: 'mike', 15 | }, 16 | { 17 | label: 'aMck', 18 | value: 'amck', 19 | }, 20 | ]; 21 | const handleSelectCallback = (v: number) => { 22 | console.log(v); 23 | }; 24 | return ( 25 | 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/Select/demos/index3.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Select from '..'; 3 | /** 4 | * transform: true 5 | */ 6 | export default function SelectDemo1() { 7 | const option = [ 8 | { 9 | label: 'Lucy', 10 | value: 'lucy', 11 | }, 12 | { 13 | label: 'Mike', 14 | value: 'mike', 15 | }, 16 | { 17 | label: 'Jack', 18 | value: 'jack', 19 | }, 20 | ]; 21 | const handleSelectCallback = (v: number) => { 22 | console.log(v); 23 | }; 24 | return ( 25 | 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /src/Select/demos/index5.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Select from '..'; 3 | /** 4 | * transform: true 5 | */ 6 | export default function SelectDemo1() { 7 | const option = [ 8 | { 9 | label: 'Mucy', 10 | value: 'mucy', 11 | }, 12 | { 13 | label: 'Mike', 14 | value: 'mike', 15 | }, 16 | { 17 | label: 'aMck', 18 | value: 'amck', 19 | }, 20 | ]; 21 | const handleSelectCallback = (v: number) => { 22 | console.log(v); 23 | }; 24 | const handleTextChange = (v: number) => { 25 | console.log(v); 26 | }; 27 | return ( 28 | handleInputChange(e)} 157 | /> 158 | {clearable ? ( 159 | setSelected('')} /> 160 | ) : ( 161 | 162 | )} 163 | 164 |
165 | {inputFilterOtpions.map((s) => { 166 | return ( 167 |
changeOptions(s as Options, e)} 174 | > 175 | {s.label} 176 |
177 | ); 178 | })} 179 |
180 | 181 | 182 | ) : ( 183 |
184 |
185 | {selected ? ( 186 |
{selected}
187 | ) : ( 188 | (placeholder &&
{placeholder}
) ||
189 | )} 190 | {loading ? : } 191 |
192 |
193 | {option.map((s) => { 194 | return ( 195 |
changeOptions(s as Options, e)} 200 | > 201 | {s.label} 202 |
203 | ); 204 | })} 205 |
206 |
207 | ); 208 | }; 209 | export default memo(Select); 210 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Button } from './Button'; 2 | export { default as Divider } from './Divider'; 3 | export { default as Layout } from './Layout'; 4 | export { default as Header } from './Layout/Header'; 5 | export { default as Slider } from './Layout/Slider'; 6 | export { default as Content } from './Layout/Content'; 7 | export { default as Footer } from './Layout/Footer'; 8 | export { default as Pagination } from './Pagination'; 9 | export { default as Select } from './Select'; 10 | export { default as Input } from './Input'; 11 | export { default as Radio } from './Radio'; 12 | export { default as RadioGroup } from './Radio/RadioGroup'; 13 | export { default as Menu } from './Menu'; 14 | export { default as Affix } from './Affix'; 15 | export { default as DatePicker } from './DatePicker'; 16 | export { default as LazyLoad } from './LazyLoad'; 17 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "es2015", 5 | "lib": ["dom", "dom.iterable", "esnext"], 6 | "allowJs": true, 7 | "skipLibCheck": true, 8 | "esModuleInterop": true, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "moduleResolution": "node", //node环境 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "noEmit": true, 16 | "jsx": "react", 17 | "baseUrl": "src", 18 | "paths": { 19 | "@/*": ["./*"] 20 | }, 21 | "experimentalDecorators": true 22 | }, 23 | "include": [ 24 | "src", 25 | "typings.d.ts" //配置的.d.ts文件 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /typings.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.module.css' { 2 | const classes: { readonly [key: string]: string }; 3 | export default classes; 4 | } 5 | 6 | declare module '*.module.less' { 7 | const classes: { readonly [key: string]: string }; 8 | export default classes; 9 | } 10 | 11 | declare module '*.module.scss' { 12 | const classes: { readonly [key: string]: string }; 13 | export default classes; 14 | } 15 | declare module 'react-view-ui/lib/umd'; 16 | declare module 'react-view-ui/dist/my-lib-esm'; 17 | --------------------------------------------------------------------------------