index.tsx 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. import React from 'react'
  2. import classnames from 'classnames'
  3. import moment, { Moment } from 'moment'
  4. import OperatorTypes from 'utils/operatorTypes'
  5. import { Row, Col, Input, InputNumber, DatePicker, Button, Tag, Switch } from 'antd'
  6. import { DEFAULT_DATETIME_FORMAT } from 'app/globalConstants'
  7. import Styles from './ConditionValuesControl.less'
  8. export type ConditionValueTypes = string | number | boolean
  9. interface IConditionValuesControlProps {
  10. className?: string
  11. size: 'small' | 'default'
  12. visualType: string
  13. operatorType: OperatorTypes
  14. conditionValues: ConditionValueTypes[]
  15. onChange: (values: ConditionValueTypes[]) => void
  16. }
  17. interface IConditionValuesControlStates {
  18. localValues: ConditionValueTypes[]
  19. tagInputting: boolean
  20. tagInputValue: ConditionValueTypes
  21. }
  22. export class ConditionValuesControl extends React.PureComponent<IConditionValuesControlProps, IConditionValuesControlStates> {
  23. public static defaultProps: Partial<IConditionValuesControlProps> = { size: 'default' }
  24. private controlStyle: React.CSSProperties = { width: '100%' }
  25. public constructor (props: IConditionValuesControlProps) {
  26. super(props)
  27. this.state = {
  28. localValues: [],
  29. tagInputting: false,
  30. tagInputValue: ''
  31. }
  32. }
  33. public componentDidMount () {
  34. const { operatorType, visualType, conditionValues } = this.props
  35. this.initLocalValues(operatorType, visualType, conditionValues)
  36. }
  37. public componentWillReceiveProps (nextProps: IConditionValuesControlProps) {
  38. const { visualType, operatorType, conditionValues } = nextProps
  39. this.initLocalValues(operatorType, visualType, conditionValues)
  40. }
  41. private initLocalValues (
  42. operatorType: OperatorTypes,
  43. visualType: string,
  44. conditionValues: ConditionValueTypes[]
  45. ) {
  46. const values = []
  47. const initValue = this.getInitValueByVisualType(visualType)
  48. let valuesCount = 0
  49. switch (operatorType) {
  50. case OperatorTypes.Contain:
  51. case OperatorTypes.Equal:
  52. case OperatorTypes.GreaterThan:
  53. case OperatorTypes.GreaterThanOrEqual:
  54. case OperatorTypes.LessThan:
  55. case OperatorTypes.LessThanOrEqual:
  56. case OperatorTypes.NotEqual:
  57. valuesCount = 1
  58. break
  59. case OperatorTypes.Between:
  60. valuesCount = 2
  61. break
  62. case OperatorTypes.In:
  63. valuesCount = conditionValues.length
  64. break
  65. }
  66. for (let idx = 0; idx < valuesCount; idx++) {
  67. if (conditionValues[idx] && typeof conditionValues[idx] === typeof initValue) {
  68. values.push(conditionValues[idx])
  69. } else {
  70. values.push(initValue)
  71. }
  72. }
  73. this.setState({ localValues: values })
  74. }
  75. private getInitValueByVisualType (visualType: string): ConditionValueTypes {
  76. switch (visualType) {
  77. case 'string':
  78. case 'geoCountry':
  79. case 'geoProvince':
  80. case 'geoCity':
  81. return ''
  82. case 'number':
  83. return 0
  84. case 'date':
  85. return moment().format('YYYY-MM-DD')
  86. case 'boolean':
  87. return false
  88. default:
  89. return null
  90. }
  91. }
  92. private getControlValueByVisualType (visualType: string, args: any[]) {
  93. let value: ConditionValueTypes
  94. switch (visualType) {
  95. case 'string':
  96. case 'geoCountry':
  97. case 'geoProvince':
  98. case 'geoCity':
  99. value = (args[0] as React.ChangeEvent<HTMLInputElement>).target.value
  100. break
  101. case 'number':
  102. case 'boolean':
  103. value = args[0]
  104. break
  105. case 'date':
  106. value = args[1]
  107. break
  108. }
  109. return value
  110. }
  111. private localValuesChange = (idx: number) => (...args: any[]) => {
  112. const { onChange, visualType } = this.props
  113. const value = this.getControlValueByVisualType(visualType, args)
  114. const { localValues } = this.state
  115. const values = [...localValues]
  116. values.splice(idx, 1, value)
  117. onChange(values)
  118. }
  119. private renderControl = (idx: number) => {
  120. const { visualType, size } = this.props
  121. const { localValues } = this.state
  122. let control: React.ReactNode
  123. switch (visualType) {
  124. case 'string':
  125. case 'geoCountry':
  126. case 'geoProvince':
  127. case 'geoCity':
  128. const stringValue = localValues[idx] as string
  129. control = (
  130. <Input
  131. style={this.controlStyle}
  132. size={size}
  133. value={stringValue}
  134. onChange={this.localValuesChange(idx)}
  135. />
  136. )
  137. break
  138. case 'number':
  139. const numberValue = localValues[idx] as number
  140. control = (
  141. <InputNumber
  142. style={this.controlStyle}
  143. size={size}
  144. value={numberValue}
  145. onChange={this.localValuesChange(idx)}
  146. />
  147. )
  148. break
  149. case 'date':
  150. const dateValue = moment(localValues[idx] as string)
  151. control = (
  152. <DatePicker
  153. style={this.controlStyle}
  154. size={size}
  155. format={DEFAULT_DATETIME_FORMAT}
  156. showTime={{ defaultValue: moment('00:00:00', 'HH:mm:ss') }}
  157. value={dateValue}
  158. onChange={this.localValuesChange(idx)}
  159. />
  160. )
  161. break
  162. case 'boolean':
  163. const booleanValue = localValues[idx] as boolean
  164. control = (
  165. <Switch
  166. size={size}
  167. checkedChildren="是"
  168. unCheckedChildren="否"
  169. checked={booleanValue}
  170. onChange={this.localValuesChange(idx)}
  171. />
  172. )
  173. }
  174. return control
  175. }
  176. private renderRow = () => {
  177. const { operatorType } = this.props
  178. let controls: React.ReactNode
  179. switch (operatorType) {
  180. case OperatorTypes.Contain:
  181. case OperatorTypes.Equal:
  182. case OperatorTypes.GreaterThan:
  183. case OperatorTypes.GreaterThanOrEqual:
  184. case OperatorTypes.LessThan:
  185. case OperatorTypes.LessThanOrEqual:
  186. case OperatorTypes.NotEqual:
  187. controls = this.renderControl(0)
  188. break
  189. case OperatorTypes.Between:
  190. controls = (
  191. <Row key="between" type="flex" align="middle" className={Styles.rowBlock}>
  192. <Col span={11}>
  193. {this.renderControl(0)}
  194. </Col>
  195. <Col span={2} className={Styles.colDivider}>-</Col>
  196. <Col span={11}>
  197. {this.renderControl(1)}
  198. </Col>
  199. </Row>
  200. )
  201. break
  202. case OperatorTypes.In:
  203. controls = this.renderTags()
  204. break
  205. }
  206. return controls
  207. }
  208. private renderTags = () => {
  209. const { visualType, size } = this.props
  210. const { localValues, tagInputting, tagInputValue } = this.state
  211. const tagList = localValues.map((val) => (
  212. <Tag key={val.toString()} className={Styles.tag} closable onClose={this.removeTag(val)}>{val}</Tag>
  213. ))
  214. const tagInputControl = []
  215. if (tagInputting) {
  216. switch (visualType) {
  217. case 'string':
  218. case 'geoCountry':
  219. case 'geoProvince':
  220. case 'geoCity':
  221. tagInputControl.push(
  222. <Input
  223. key="input"
  224. type="text"
  225. size="small"
  226. className={Styles.tagInput}
  227. value={(tagInputValue as string)}
  228. onChange={this.tagInputValueChange}
  229. onBlur={this.addTag}
  230. onPressEnter={this.addTag}
  231. />
  232. )
  233. break
  234. case 'number':
  235. tagInputControl.push(
  236. <InputNumber
  237. key="inputNumber"
  238. size="small"
  239. className={Styles.tagInput}
  240. value={(tagInputValue as number)}
  241. onChange={this.tagInputValueChange}
  242. />
  243. )
  244. break
  245. case 'date':
  246. const dateValue = moment((tagInputValue || moment().format('YYYY-MM-DD')) as string)
  247. tagInputControl.push(
  248. <DatePicker
  249. key="datePicker"
  250. size="small"
  251. className={Styles.tagInput}
  252. value={dateValue}
  253. onChange={this.tagInputValueChange}
  254. />
  255. )
  256. break
  257. }
  258. tagInputControl.push(
  259. <Button
  260. key="saveTag"
  261. className={Styles.tagBtn}
  262. size="small"
  263. type="dashed"
  264. onClick={this.addTag}
  265. >
  266. 确定
  267. </Button>)
  268. } else {
  269. tagInputControl.push(
  270. <Button
  271. key="addTag"
  272. className={Styles.tagBtn}
  273. size="small"
  274. type="dashed"
  275. onClick={this.showTagInput}
  276. >
  277. + 添加
  278. </Button>)
  279. }
  280. const rowCls = classnames({
  281. [Styles.rowBlock]: true,
  282. [Styles.tagList]: true
  283. })
  284. return (
  285. <Row key="tag" type="flex" align="middle" className={rowCls}>
  286. {tagList}{tagInputControl}
  287. </Row>
  288. )
  289. }
  290. private showTagInput = () => {
  291. this.setState({
  292. tagInputting: true
  293. })
  294. }
  295. private addTag = () => {
  296. const { tagInputValue, localValues } = this.state
  297. if (tagInputValue) {
  298. const { onChange, visualType, operatorType } = this.props
  299. onChange([...localValues.filter((val) => val !== tagInputValue), tagInputValue])
  300. this.setState({
  301. tagInputting: false,
  302. tagInputValue: this.getInitValueByVisualType(visualType)
  303. })
  304. }
  305. }
  306. private removeTag = (tag) => () => {
  307. const { onChange } = this.props
  308. const { localValues } = this.state
  309. onChange(localValues.filter((val) => val !== tag))
  310. }
  311. private tagInputValueChange = (...args: any[]) => {
  312. const { visualType } = this.props
  313. const tagInputValue = this.getControlValueByVisualType(visualType, args)
  314. this.setState({ tagInputValue })
  315. }
  316. public render () {
  317. const { className } = this.props
  318. return (
  319. <div className={className}>
  320. {this.renderRow()}
  321. </div>
  322. )
  323. }
  324. }
  325. export default ConditionValuesControl