From 18e249b43b54f919c937bbfefa734529b01b04d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=B8=85?= Date: Wed, 17 Apr 2019 09:45:47 +0800 Subject: [PATCH] SearchListApplications finish --- .../src/{_mock.js => _mock.ts} | 0 .../src/components/PageHeaderWrapper/index.js | 25 -- .../components/PageHeaderWrapper/index.less | 81 ++++++- .../components/PageHeaderWrapper/index.tsx | 56 +++++ .../StandardFormRow/{index.js => index.tsx} | 17 +- .../src/components/TagSelect/index.less | 33 +++ .../src/components/TagSelect/index.tsx | 169 ++++++++++++++ SearchListApplications/src/data.d.ts | 29 +++ SearchListApplications/src/index.js | 213 ------------------ SearchListApplications/src/index.tsx | 212 +++++++++++++++++ SearchListApplications/src/model.js | 28 --- SearchListApplications/src/model.ts | 54 +++++ .../src/{service.js => service.ts} | 0 SearchListArticles/src/index.js | 21 +- SearchListProjects/src/index.js | 103 ++++----- 15 files changed, 701 insertions(+), 340 deletions(-) rename SearchListApplications/src/{_mock.js => _mock.ts} (100%) delete mode 100644 SearchListApplications/src/components/PageHeaderWrapper/index.js create mode 100644 SearchListApplications/src/components/PageHeaderWrapper/index.tsx rename SearchListApplications/src/components/StandardFormRow/{index.js => index.tsx} (67%) create mode 100644 SearchListApplications/src/components/TagSelect/index.less create mode 100644 SearchListApplications/src/components/TagSelect/index.tsx create mode 100644 SearchListApplications/src/data.d.ts delete mode 100644 SearchListApplications/src/index.js create mode 100644 SearchListApplications/src/index.tsx delete mode 100644 SearchListApplications/src/model.js create mode 100644 SearchListApplications/src/model.ts rename SearchListApplications/src/{service.js => service.ts} (100%) diff --git a/SearchListApplications/src/_mock.js b/SearchListApplications/src/_mock.ts similarity index 100% rename from SearchListApplications/src/_mock.js rename to SearchListApplications/src/_mock.ts diff --git a/SearchListApplications/src/components/PageHeaderWrapper/index.js b/SearchListApplications/src/components/PageHeaderWrapper/index.js deleted file mode 100644 index 1a40e25d..00000000 --- a/SearchListApplications/src/components/PageHeaderWrapper/index.js +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react'; -import { FormattedMessage } from 'umi-plugin-react/locale'; -import Link from 'umi/link'; -import { PageHeader } from 'ant-design-pro'; -import styles from './index.less'; - -const PageHeaderWrapper = ({ children, wrapperClassName, ...restProps }) => ( -
- } - key="pageheader" - {...restProps} - linkElement={Link} - itemRender={item => { - if (item.locale) { - return ; - } - return item.title; - }} - /> - {children ?
{children}
: null} -
-); - -export default PageHeaderWrapper; diff --git a/SearchListApplications/src/components/PageHeaderWrapper/index.less b/SearchListApplications/src/components/PageHeaderWrapper/index.less index 39a44965..908db575 100644 --- a/SearchListApplications/src/components/PageHeaderWrapper/index.less +++ b/SearchListApplications/src/components/PageHeaderWrapper/index.less @@ -1,11 +1,86 @@ @import '~antd/lib/style/themes/default.less'; -.content { +.children-content { margin: 24px 24px 0; } -@media screen and (max-width: @screen-sm) { +.main { + .detail { + display: flex; + } + + .row { + display: flex; + width: 100%; + } + + .title-content { + margin-bottom: 16px; + } + + @media screen and (max-width: @screen-sm) { + .content { + margin: 24px 0 0; + } + } + + .title, .content { - margin: 24px 0 0; + flex: auto; + } + + .extraContent, + .main { + flex: 0 1 auto; + } + + .main { + width: 100%; + } + + .title { + margin-bottom: 16px; + } + + .logo, + .content, + .extraContent { + margin-bottom: 16px; + } + + .extraContent { + min-width: 242px; + margin-left: 88px; + text-align: right; + } +} + +@media screen and (max-width: @screen-xl) { + .extraContent { + margin-left: 44px; + } +} + +@media screen and (max-width: @screen-lg) { + .extraContent { + margin-left: 20px; + } +} + +@media screen and (max-width: @screen-md) { + .row { + display: block; + } + + .action, + .extraContent { + margin-left: 0; + text-align: left; + } +} + +@media screen and (max-width: @screen-sm) { + .detail { + display: block; } } diff --git a/SearchListApplications/src/components/PageHeaderWrapper/index.tsx b/SearchListApplications/src/components/PageHeaderWrapper/index.tsx new file mode 100644 index 00000000..fe1a33b8 --- /dev/null +++ b/SearchListApplications/src/components/PageHeaderWrapper/index.tsx @@ -0,0 +1,56 @@ +import React from 'react'; +import { RouteContext } from '@ant-design/pro-layout'; +import { PageHeader, Typography } from 'antd'; +import styles from './index.less'; +import { GridContent } from '@ant-design/pro-layout'; + +interface IPageHeaderWrapperProps { + content?: React.ReactNode; + title: React.ReactNode; + extraContent?: React.ReactNode; +} + +const PageHeaderWrapper: React.SFC = ({ + children, + title, + content, + extraContent, + ...restProps +}) => ( + + {value => ( +
+ + {title} + + } + {...restProps} + {...value} + > +
+
+
+ {content &&
{content}
} + {extraContent &&
{extraContent}
} +
+
+
+
+ {children ? ( + +
{children}
+
+ ) : null} +
+ )} +
+); + +export default PageHeaderWrapper; diff --git a/SearchListApplications/src/components/StandardFormRow/index.js b/SearchListApplications/src/components/StandardFormRow/index.tsx similarity index 67% rename from SearchListApplications/src/components/StandardFormRow/index.js rename to SearchListApplications/src/components/StandardFormRow/index.tsx index 8cb0e444..01a8bb5d 100644 --- a/SearchListApplications/src/components/StandardFormRow/index.js +++ b/SearchListApplications/src/components/StandardFormRow/index.tsx @@ -2,7 +2,22 @@ import React from 'react'; import classNames from 'classnames'; import styles from './index.less'; -const StandardFormRow = ({ title, children, last, block, grid, ...rest }) => { +interface StandardFormRowProps { + title?: string; + last?: boolean; + block?: boolean; + grid?: boolean; + style?: React.CSSProperties; +} + +const StandardFormRow: React.SFC = ({ + title, + children, + last, + block, + grid, + ...rest +}) => { const cls = classNames(styles.standardFormRow, { [styles.standardFormRowBlock]: block, [styles.standardFormRowLast]: last, diff --git a/SearchListApplications/src/components/TagSelect/index.less b/SearchListApplications/src/components/TagSelect/index.less new file mode 100644 index 00000000..93694653 --- /dev/null +++ b/SearchListApplications/src/components/TagSelect/index.less @@ -0,0 +1,33 @@ +@import '~antd/lib/style/themes/default.less'; + +.tagSelect { + position: relative; + max-height: 32px; + margin-left: -8px; + overflow: hidden; + line-height: 32px; + transition: all 0.3s; + user-select: none; + :global { + .ant-tag { + margin-right: 24px; + padding: 0 8px; + font-size: @font-size-base; + } + } + &.expanded { + max-height: 200px; + transition: all 0.3s; + } + .trigger { + position: absolute; + top: 0; + right: 0; + i { + font-size: 12px; + } + } + &.hasExpandTag { + padding-right: 50px; + } +} diff --git a/SearchListApplications/src/components/TagSelect/index.tsx b/SearchListApplications/src/components/TagSelect/index.tsx new file mode 100644 index 00000000..96416a4b --- /dev/null +++ b/SearchListApplications/src/components/TagSelect/index.tsx @@ -0,0 +1,169 @@ +import React, { Component } from 'react'; +import classNames from 'classnames'; +import { Tag, Icon } from 'antd'; + +import styles from './index.less'; + +const { CheckableTag } = Tag; + +export interface TagSelectOptionProps { + value?: string | number; + style?: React.CSSProperties; + checked?: boolean; + onChange?: (value: string | number | undefined, state: boolean) => void; +} + +export interface TagSelectProps { + onChange?: (value: (string | number)[]) => void; + expandable?: boolean; + value?: (string | number)[]; + defaultValue?: (string | number)[]; + style?: React.CSSProperties; + hideCheckAll?: boolean; + actionsText?: { + expandText?: React.ReactNode; + collapseText?: React.ReactNode; + selectAllText?: React.ReactNode; + }; + className?: string; + Option?: TagSelectOptionProps; + children?: React.ReactElement | Array>; +} + +const TagSelectOption: React.SFC & { + isTagSelectOption: boolean; +} = ({ children, checked, onChange, value }) => ( + onChange && onChange(value, state)} + > + {children} + +); + +TagSelectOption.isTagSelectOption = true; + +interface TagSelectState { + expand: boolean; + value: (string | number)[]; +} + +class TagSelect extends Component { + static defaultProps = { + hideCheckAll: false, + actionsText: { + expandText: 'Expand', + collapseText: 'Collapse', + selectAllText: 'All', + }, + }; + static Option: TagSelectOption = TagSelectOption; + constructor(props: TagSelectProps) { + super(props); + this.state = { + expand: false, + value: props.value || props.defaultValue || [], + }; + } + + static getDerivedStateFromProps(nextProps: TagSelectProps) { + if ('value' in nextProps) { + return { value: nextProps.value || [] }; + } + return null; + } + + onChange = (value: (string | number)[]) => { + const { onChange } = this.props; + if (!('value' in this.props)) { + this.setState({ value }); + } + if (onChange) { + onChange(value); + } + }; + + onSelectAll = (checked: boolean) => { + let checkedTags: (string | number)[] = []; + if (checked) { + checkedTags = this.getAllTags(); + } + this.onChange(checkedTags); + }; + + getAllTags() { + let { children } = this.props; + const childrenArray = React.Children.toArray(children) as React.ReactElement[]; + const checkedTags = childrenArray + .filter(child => this.isTagSelectOption(child)) + .map(child => child.props.value); + return checkedTags || []; + } + + handleTagChange = (value: string | number, checked: boolean) => { + const { value: StateValue } = this.state; + const checkedTags: (string | number)[] = [...StateValue]; + + const index = checkedTags.indexOf(value); + if (checked && index === -1) { + checkedTags.push(value); + } else if (!checked && index > -1) { + checkedTags.splice(index, 1); + } + this.onChange(checkedTags); + }; + + handleExpand = () => { + const { expand } = this.state; + this.setState({ + expand: !expand, + }); + }; + + isTagSelectOption = (node: React.ReactElement) => + node && + node.type && + (node.type.isTagSelectOption || node.type.displayName === 'TagSelectOption'); + + render() { + const { value, expand } = this.state; + const { children, hideCheckAll, className, style, expandable, actionsText = {} } = this.props; + const checkedAll = this.getAllTags().length === value.length; + const { expandText = 'Expand', collapseText = 'Collapse', selectAllText = 'All' } = actionsText; + + const cls = classNames(styles.tagSelect, className, { + [styles.hasExpandTag]: expandable, + [styles.expanded]: expand, + }); + + return ( +
+ {hideCheckAll ? null : ( + + {selectAllText} + + )} + {value && + React.Children.map(children, (child: React.ReactElement) => { + if (this.isTagSelectOption(child)) { + return React.cloneElement(child, { + key: `tag-select-${child.props.value}`, + value: child.props.value, + checked: value.indexOf(child.props.value) > -1, + onChange: this.handleTagChange, + }); + } + return child; + })} + {expandable && ( + + {expand ? collapseText : expandText} + + )} +
+ ); + } +} + +export default TagSelect; diff --git a/SearchListApplications/src/data.d.ts b/SearchListApplications/src/data.d.ts new file mode 100644 index 00000000..6a4a88ce --- /dev/null +++ b/SearchListApplications/src/data.d.ts @@ -0,0 +1,29 @@ +export interface Member { + avatar: string; + name: string; + id: string; +} + +export interface ListItemDataType { + id: string; + owner: string; + title: string; + avatar: string; + cover: string; + status: 'normal' | 'exception' | 'active' | 'success'; + percent: number; + logo: string; + href: string; + body?: any; + updatedAt: number; + createdAt: number; + subDescription: string; + description: string; + activeUser: number; + newUser: number; + star: number; + like: number; + message: number; + content: string; + members: Member[]; +} diff --git a/SearchListApplications/src/index.js b/SearchListApplications/src/index.js deleted file mode 100644 index 5de54a08..00000000 --- a/SearchListApplications/src/index.js +++ /dev/null @@ -1,213 +0,0 @@ -import React, { PureComponent } from 'react'; -import numeral from 'numeral'; -import { connect } from 'dva'; -import { - Row, - Col, - Form, - Card, - Select, - Icon, - Avatar, - List, - Tooltip, - Dropdown, - Menu, - Input, -} from 'antd'; -import { TagSelect } from 'ant-design-pro'; - -import StandardFormRow from './components/StandardFormRow'; -import PageHeaderWrapper from './components/PageHeaderWrapper'; -import { formatWan } from './utils/utils'; -import styles from './style.less'; - -const { Option } = Select; -const FormItem = Form.Item; - -@connect(({ BLOCK_NAME_CAMEL_CASE, loading }) => ({ - BLOCK_NAME_CAMEL_CASE, - loading: loading.models.BLOCK_NAME_CAMEL_CASE, -})) -@Form.create({ - onValuesChange({ dispatch }, changedValues, allValues) { - // 表单项变化时请求数据 - // eslint-disable-next-line - console.log(changedValues, allValues); - // 模拟查询表单生效 - dispatch({ - type: 'BLOCK_NAME_CAMEL_CASE/fetch', - payload: { - count: 8, - }, - }); - }, -}) -class PAGE_NAME_UPPER_CAMEL_CASE extends PureComponent { - componentDidMount() { - const { dispatch } = this.props; - dispatch({ - type: 'BLOCK_NAME_CAMEL_CASE/fetch', - payload: { - count: 8, - }, - }); - } - - render() { - const { - BLOCK_NAME_CAMEL_CASE: { list }, - loading, - form, - } = this.props; - const { getFieldDecorator } = form; - - const CardInfo = ({ activeUser, newUser }) => ( -
-
-

活跃用户

-

{activeUser}

-
-
-

新增用户

-

{newUser}

-
-
- ); - - const formItemLayout = { - wrapperCol: { - xs: { span: 24 }, - sm: { span: 16 }, - }, - }; - - const itemMenu = ( - - - - 1st menu item - - - - - 2nd menu item - - - - - 3d menu item - - - - ); - - const mainSearch = ( -
- -
- ); - - return ( - -
- -
- - - {getFieldDecorator('category')( - - 类目一 - 类目二 - 类目三 - 类目四 - 类目五 - 类目六 - 类目七 - 类目八 - 类目九 - 类目十 - 类目十一 - 类目十二 - - )} - - - - - - - {getFieldDecorator('author', {})( - - )} - - - - - {getFieldDecorator('rate', {})( - - )} - - - - -
-
- ( - - - - , - - - , - - - , - - - , - ]} - > - } - title={item.title} - /> -
- -
-
-
- )} - /> -
-
- ); - } -} - -export default PAGE_NAME_UPPER_CAMEL_CASE; diff --git a/SearchListApplications/src/index.tsx b/SearchListApplications/src/index.tsx new file mode 100644 index 00000000..5160d0d0 --- /dev/null +++ b/SearchListApplications/src/index.tsx @@ -0,0 +1,212 @@ +import React, { Component } from 'react'; +import numeral from 'numeral'; +import { connect } from 'dva'; +import { + Row, + Col, + Form, + Card, + Select, + Icon, + Avatar, + List, + Tooltip, + Dropdown, + Menu, + Input, +} from 'antd'; +import TagSelect from './components/TagSelect'; +import StandardFormRow from './components/StandardFormRow'; +import PageHeaderWrapper from './components/PageHeaderWrapper'; +import { formatWan } from './utils/utils'; +import styles from './style.less'; +import { IStateType } from './model'; +import { Dispatch } from 'redux'; +import { FormComponentProps } from 'antd/lib/form'; +import { ListItemDataType } from './data'; + +const { Option } = Select; +const FormItem = Form.Item; + +interface PAGE_NAME_UPPER_CAMEL_CASEProps extends FormComponentProps { + dispatch: Dispatch; + BLOCK_NAME_CAMEL_CASE: IStateType; + loading: boolean; +} + +@connect( + ({ + BLOCK_NAME_CAMEL_CASE, + loading, + }: { + BLOCK_NAME_CAMEL_CASE: IStateType; + loading: { models: { [key: string]: boolean } }; + }) => ({ + BLOCK_NAME_CAMEL_CASE, + loading: loading.models.BLOCK_NAME_CAMEL_CASE, + }) +) +class PAGE_NAME_UPPER_CAMEL_CASE extends Component { + componentDidMount() { + const { dispatch } = this.props; + dispatch({ + type: 'BLOCK_NAME_CAMEL_CASE/fetch', + payload: { + count: 8, + }, + }); + } + + render() { + const { + BLOCK_NAME_CAMEL_CASE: { list }, + loading, + form, + } = this.props; + const { getFieldDecorator } = form; + + const CardInfo: React.SFC<{ + activeUser: React.ReactNode; + newUser: React.ReactNode; + }> = ({ activeUser, newUser }) => ( +
+
+

活跃用户

+

{activeUser}

+
+
+

新增用户

+

{newUser}

+
+
+ ); + + const formItemLayout = { + wrapperCol: { + xs: { span: 24 }, + sm: { span: 16 }, + }, + }; + + const itemMenu = ( + + + + 1st menu item + + + + + 2nd menu item + + + + + 3d menu item + + + + ); + + return ( +
+ +
+ + + {getFieldDecorator('category')( + + 类目一 + 类目二 + 类目三 + 类目四 + 类目五 + 类目六 + 类目七 + 类目八 + 类目九 + 类目十 + 类目十一 + 类目十二 + + )} + + + + + + + {getFieldDecorator('author', {})( + + )} + + + + + {getFieldDecorator('rate', {})( + + )} + + + + +
+
+ + rowKey="id" + grid={{ gutter: 24, xl: 4, lg: 3, md: 3, sm: 2, xs: 1 }} + loading={loading} + dataSource={list} + renderItem={item => ( + + + + , + + + , + + + , + + + , + ]} + > + } title={item.title} /> +
+ +
+
+
+ )} + /> +
+ ); + } +} + +export default Form.create({ + onValuesChange({ dispatch }: PAGE_NAME_UPPER_CAMEL_CASEProps, changedValues, allValues) { + // 表单项变化时请求数据 + // 模拟查询表单生效 + dispatch({ + type: 'BLOCK_NAME_CAMEL_CASE/fetch', + payload: { + count: 8, + }, + }); + }, +})(PAGE_NAME_UPPER_CAMEL_CASE); diff --git a/SearchListApplications/src/model.js b/SearchListApplications/src/model.js deleted file mode 100644 index ead65a8d..00000000 --- a/SearchListApplications/src/model.js +++ /dev/null @@ -1,28 +0,0 @@ -import { queryFakeList } from './service'; - -export default { - namespace: 'BLOCK_NAME_CAMEL_CASE', - - state: { - list: [], - }, - - effects: { - *fetch({ payload }, { call, put }) { - const response = yield call(queryFakeList, payload); - yield put({ - type: 'queryList', - payload: Array.isArray(response) ? response : [], - }); - }, - }, - - reducers: { - queryList(state, action) { - return { - ...state, - list: action.payload, - }; - }, - }, -}; diff --git a/SearchListApplications/src/model.ts b/SearchListApplications/src/model.ts new file mode 100644 index 00000000..24c2230c --- /dev/null +++ b/SearchListApplications/src/model.ts @@ -0,0 +1,54 @@ +import { queryFakeList } from './service'; +import { ListItemDataType } from './data'; +import { Reducer } from 'redux'; +import { EffectsCommandMap } from 'dva'; +import { AnyAction } from 'redux'; + +export interface IStateType { + list: ListItemDataType[]; +} + +export type Effect = ( + action: AnyAction, + effects: EffectsCommandMap & { select: (func: (state: IStateType) => T) => T } +) => void; + +export interface ModelType { + namespace: string; + state: IStateType; + effects: { + fetch: Effect; + }; + reducers: { + queryList: Reducer; + }; +} + +const Model: ModelType = { + namespace: 'BLOCK_NAME_CAMEL_CASE', + + state: { + list: [], + }, + + effects: { + *fetch({ payload }, { call, put }) { + const response = yield call(queryFakeList, payload); + yield put({ + type: 'queryList', + payload: Array.isArray(response) ? response : [], + }); + }, + }, + + reducers: { + queryList(state, action) { + return { + ...state, + list: action.payload, + }; + }, + }, +}; + +export default Model; diff --git a/SearchListApplications/src/service.js b/SearchListApplications/src/service.ts similarity index 100% rename from SearchListApplications/src/service.js rename to SearchListApplications/src/service.ts diff --git a/SearchListArticles/src/index.js b/SearchListArticles/src/index.js index 34e7f2df..773a81ba 100644 --- a/SearchListArticles/src/index.js +++ b/SearchListArticles/src/index.js @@ -1,11 +1,10 @@ import React, { Component } from 'react'; import { connect } from 'dva'; -import { Form, Card, Select, List, Tag, Icon, Row, Col, Button, Input } from 'antd'; +import { Form, Card, Select, List, Tag, Icon, Row, Col, Button } from 'antd'; -import TagSelect from 'ant-design-pro/lib/TagSelect'; +import TagSelect from './components/TagSelect'; import StandardFormRow from './components/StandardFormRow'; import ArticleListContent from './components/ArticleListContent'; -import PageHeaderWrapper from './components/PageHeaderWrapper'; import styles from './style.less'; const { Option } = Select; @@ -120,20 +119,8 @@ class SearchList extends Component { ) : null; - const mainSearch = ( -
- -
- ); - return ( - + <>
@@ -247,7 +234,7 @@ class SearchList extends Component { )} /> - + ); } } diff --git a/SearchListProjects/src/index.js b/SearchListProjects/src/index.js index 4db3ba69..f5869fb2 100644 --- a/SearchListProjects/src/index.js +++ b/SearchListProjects/src/index.js @@ -6,7 +6,6 @@ import { Row, Col, Form, Card, Select, List, Input } from 'antd'; import { TagSelect, AvatarList, Ellipsis } from 'ant-design-pro'; import StandardFormRow from './components/StandardFormRow'; -import PageHeaderWrapper from './components/PageHeaderWrapper'; import styles from './style.less'; const { Option } = Select; @@ -107,58 +106,56 @@ class CoverCardList extends PureComponent { ); return ( - -
- - - - - {getFieldDecorator('category')( - - 类目一 - 类目二 - 类目三 - 类目四 - 类目五 - 类目六 - 类目七 - 类目八 - 类目九 - 类目十 - 类目十一 - 类目十二 - - )} - - - - - - - {getFieldDecorator('author', {})( - - )} - - - - - {getFieldDecorator('rate', {})( - - )} - - - - - - -
{cardList}
-
-
+
+ +
+ + + {getFieldDecorator('category')( + + 类目一 + 类目二 + 类目三 + 类目四 + 类目五 + 类目六 + 类目七 + 类目八 + 类目九 + 类目十 + 类目十一 + 类目十二 + + )} + + + + + + + {getFieldDecorator('author', {})( + + )} + + + + + {getFieldDecorator('rate', {})( + + )} + + + + +
+
+
{cardList}
+
); } } -- GitLab