SupportCreate.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. import React, { Component } from 'react';
  2. import pathToRegexp from 'path-to-regexp';
  3. import { connect } from 'dva';
  4. import { routerRedux } from 'dva/router';
  5. import { Form, Modal, Card, Button, Input, Switch, Row, Col, Carousel, Select } from 'antd';
  6. import { renderStatus, statusToBool, boolToStatus, genAbsolutePicUrl } from '../../../utils/utils';
  7. import AXDragSortTable from '../../../components/AXDragSortTable';
  8. import Selector from '../../../components/AXTableSelector/Selector';
  9. import FooterToolbar from '../../../components/FooterToolbar';
  10. import styles from './SupportCreate.less';
  11. const fieldLabels = {
  12. code: '配套编号',
  13. title: '配套标题',
  14. subTitle: '配套副标题',
  15. name: '配套全称',
  16. merchant: '内容提供商',
  17. digest: '配套概要',
  18. detail: '配套详情',
  19. coverUrl: '配套封面图',
  20. imgList: '配套滚动图册',
  21. status: '配套状态',
  22. };
  23. const formItemLayout = {
  24. labelCol: {
  25. xs: { span: 24 },
  26. sm: { span: 3 },
  27. },
  28. wrapperCol: {
  29. xs: { span: 24 },
  30. sm: { span: 14 },
  31. md: { span: 12 },
  32. },
  33. };
  34. @connect(({ loading, product, resource, merchant }) => ({
  35. product,
  36. resource,
  37. merchant,
  38. pLoading: loading.models.product,
  39. rLoading: loading.models.resource,
  40. submitting: loading.models.product,
  41. }))
  42. @Form.create()
  43. export default class SupportCreatePage extends Component {
  44. state = {
  45. coverSelectorDestroy: true,
  46. carouselSelectorDestroy: true,
  47. supportSelectorDestroy: true,
  48. };
  49. componentWillMount() {
  50. const match = pathToRegexp('/product/support/create').exec(this.props.location.pathname);
  51. if (match) {
  52. this.cleanPageState();
  53. }
  54. }
  55. componentDidMount() {
  56. const matchId = this.isEdit();
  57. if (matchId) {
  58. this.props.dispatch({
  59. type: 'product/fetchProductItem',
  60. payload: { pid: matchId },
  61. });
  62. }
  63. this.props.dispatch({
  64. type: 'merchant/fetchMerchantList',
  65. payload: { pageSize: 1000 }, // TODO 以后商户多了需要改写交互样式
  66. });
  67. }
  68. isEdit = () => {
  69. const { location } = this.props;
  70. const match = pathToRegexp('/product/support/edit/:id').exec(location.pathname);
  71. if (match) {
  72. return match[1];
  73. }
  74. return false;
  75. };
  76. cleanPageState = () => {
  77. this.props.dispatch({
  78. type: 'product/cleanItemState',
  79. payload: {},
  80. });
  81. };
  82. selectorDataFetcher = (name, params) => {
  83. switch (name) {
  84. case 'cover':
  85. this.props.dispatch({
  86. type: 'resource/fetchImageList',
  87. payload: params,
  88. });
  89. break;
  90. case 'carousel':
  91. this.props.dispatch({
  92. type: 'resource/fetchImageList',
  93. payload: params,
  94. });
  95. break;
  96. case 'support':
  97. this.props.dispatch({
  98. type: 'product/fetchSupportList',
  99. payload: params,
  100. });
  101. break;
  102. default:
  103. break;
  104. }
  105. };
  106. currentItemFormatter = (name, rows) => {
  107. let payload;
  108. switch (name) {
  109. case 'cover':
  110. payload = { coverUrl: rows[0].path };
  111. break;
  112. case 'carousel':
  113. payload = { imgList: rows.map(row => row.path) };
  114. break;
  115. case 'support':
  116. payload = { supportList: rows };
  117. break;
  118. default:
  119. break;
  120. }
  121. return payload;
  122. };
  123. handleSelectorModalShow = (name) => {
  124. this.setState({
  125. [`${name}SelectorDestroy`]: false,
  126. });
  127. this.selectorDataFetcher(name);
  128. };
  129. handleSelectorChange = (name, params) => {
  130. this.selectorDataFetcher(name, params);
  131. };
  132. handleSelectorFinish = (name, rows) => {
  133. this.setState({
  134. [`${name}SelectorDestroy`]: true,
  135. });
  136. const payload = this.currentItemFormatter(name, rows);
  137. this.props.dispatch({
  138. payload,
  139. type: 'product/fixCurrentItem',
  140. });
  141. };
  142. handleDragSortTableChange = (name, rows) => {
  143. const payload = this.currentItemFormatter(name, rows);
  144. this.props.dispatch({
  145. payload,
  146. type: 'product/fixCurrentItem',
  147. });
  148. };
  149. handleSelectorCancel = (name) => {
  150. this.setState({
  151. [`${name}SelectorDestroy`]: true,
  152. });
  153. };
  154. handlePageBack = () => {
  155. this.props.dispatch(routerRedux.push({
  156. pathname: '/product/support/list',
  157. state: this.props.location.state,
  158. }));
  159. };
  160. handlePageSubmit = (e) => {
  161. e.preventDefault();
  162. this.props.form.validateFieldsAndScroll((err, values) => {
  163. if (!err) {
  164. // 从表单提取基础信息字段
  165. const { status, title, subTitle, name, ...rest } = values;
  166. const newName = `${title}_${subTitle}`;
  167. const newVals = { newName, title, subTitle, status: boolToStatus(status), ...rest };
  168. // 从props中提取coverUrl、imgList、supportList字段
  169. const { product } = this.props;
  170. const { currentItem } = product;
  171. const { imgList, supportList, coverUrl } = currentItem;
  172. // 防止supportList为空
  173. let supportIdList;
  174. if (supportList && supportList.length) {
  175. supportIdList = supportList.map(item => item.id);
  176. }
  177. // 更新或者创建操作
  178. const matchId = this.isEdit();
  179. if (matchId) {
  180. const params = {
  181. imgList,
  182. coverUrl,
  183. supportList: supportIdList,
  184. id: matchId,
  185. ...newVals,
  186. };
  187. this.props.dispatch({
  188. type: 'product/updateSupportItem',
  189. payload: params,
  190. states: this.props.location.state,
  191. });
  192. } else {
  193. const params = {
  194. imgList,
  195. coverUrl,
  196. supportList: supportIdList,
  197. ...newVals,
  198. };
  199. this.props.dispatch({
  200. type: 'product/createSupportItem',
  201. payload: params,
  202. states: this.props.location.state,
  203. });
  204. }
  205. }
  206. });
  207. };
  208. render() {
  209. const {
  210. form, submitting, rLoading, pLoading, product, resource, merchant,
  211. } = this.props;
  212. const {
  213. supportSelectorDestroy, coverSelectorDestroy, carouselSelectorDestroy,
  214. } = this.state;
  215. const {
  216. currentItem,
  217. } = product;
  218. const {
  219. code, title, subTitle, name, digest, detail, status, coverUrl, cpId,
  220. imgList = [], supportList = [],
  221. } = currentItem;
  222. const {
  223. getFieldDecorator,
  224. } = form;
  225. const supportColumns = [{
  226. title: '配套编号',
  227. dataIndex: 'code',
  228. key: 1,
  229. width: '20%',
  230. render: (text, record) => (
  231. <a
  232. className="a-link"
  233. target="_blank"
  234. rel="noopener noreferrer"
  235. href={`/product/support/edit/${record.id}`}
  236. >
  237. {text}
  238. </a>
  239. ),
  240. }, {
  241. title: '配套名称',
  242. dataIndex: 'name',
  243. key: 2,
  244. width: '30%',
  245. render: (text, record) => (
  246. <a
  247. className="a-link"
  248. target="_blank"
  249. rel="noopener noreferrer"
  250. href={`/product/support/edit/${record.id}`}
  251. >
  252. {text}
  253. </a>
  254. ),
  255. }, {
  256. title: '配套状态',
  257. dataIndex: 'status',
  258. key: 3,
  259. render: text => renderStatus(text),
  260. }];
  261. const getMerchants = () => {
  262. const { list } = merchant;
  263. return list.map(item => ({
  264. text: item.name,
  265. key: item.id,
  266. }));
  267. };
  268. const getResourceModal = (isCover) => {
  269. return (
  270. <Modal
  271. width={isCover ? 900 : 1100}
  272. footer={null}
  273. visible
  274. title="图片资源"
  275. maskClosable={false}
  276. onCancel={() => this.handleSelectorCancel(isCover ? 'cover' : 'carousel')}
  277. >
  278. <Selector
  279. loading={rLoading}
  280. list={resource.list}
  281. pageNo={resource.pageNo}
  282. pageSize={resource.pageSize}
  283. totalSize={resource.totalSize}
  284. multiple={!isCover}
  285. selectorName={isCover ? 'PictureSingle' : 'Index'}
  286. onCancel={() => this.handleSelectorCancel(isCover ? 'cover' : 'carousel')}
  287. onChange={data => this.handleSelectorChange(isCover ? 'cover' : 'carousel', data)}
  288. onFinish={rows => this.handleSelectorFinish(isCover ? 'cover' : 'carousel', rows)}
  289. />
  290. </Modal>
  291. );
  292. };
  293. const getSupportModal = () => {
  294. return (
  295. <Modal
  296. width={1100}
  297. footer={null}
  298. visible
  299. title="配套资源"
  300. maskClosable={false}
  301. onCancel={() => this.handleSelectorCancel('support')}
  302. >
  303. <Selector
  304. multiple
  305. loading={pLoading}
  306. selectorName="Support"
  307. list={product.list}
  308. pageNo={product.pageNo}
  309. pageSize={product.pageSize}
  310. totalSize={product.totalSize}
  311. onCancel={() => this.handleSelectorCancel('support')}
  312. onChange={data => this.handleSelectorChange('support', data)}
  313. onFinish={rows => this.handleSelectorFinish('support', rows)}
  314. />
  315. </Modal>
  316. );
  317. };
  318. const renderSupportCardName = () => {
  319. return (
  320. <Button
  321. type="primary"
  322. onClick={() => this.handleSelectorModalShow('support')}
  323. >相关配套
  324. </Button>
  325. );
  326. };
  327. const renderCoverCardName = () => {
  328. return (
  329. <div className={styles.cardName}>
  330. <span>
  331. <a onClick={() => this.handleSelectorModalShow('cover')}>配套封面</a>
  332. </span>
  333. </div>
  334. );
  335. };
  336. const renderCarouselCardName = () => {
  337. return (
  338. <div className={styles.cardName}>
  339. <span>
  340. <a onClick={() => this.handleSelectorModalShow('carousel')}>详情图册</a>
  341. </span>
  342. </div>
  343. );
  344. };
  345. return (
  346. <div>
  347. {/* 基础信息Card */}
  348. <Card title="基础信息" style={{ marginBottom: 16 }}>
  349. <Form>
  350. <Form.Item hasFeedback label={fieldLabels.code} {...formItemLayout}>
  351. {getFieldDecorator('code', {
  352. rules: [
  353. {
  354. required: true, message: '请填写配套编号',
  355. }, {
  356. pattern: /^[a-zA-Z0-9|-]+$/g, message: '编号包含非法字符',
  357. },
  358. ],
  359. initialValue: code,
  360. })(
  361. <Input placeholder="请输入" />
  362. )}
  363. </Form.Item>
  364. {this.isEdit() && (
  365. <Form.Item label={fieldLabels.name} {...formItemLayout}>
  366. {getFieldDecorator('name', {
  367. initialValue: name,
  368. })(
  369. <Input disabled />
  370. )}
  371. </Form.Item>
  372. )}
  373. <Form.Item hasFeedback label={fieldLabels.title} {...formItemLayout}>
  374. {getFieldDecorator('title', {
  375. rules: [{ required: true, message: '请填写配套标题' }],
  376. initialValue: title,
  377. })(
  378. <Input placeholder="请输入" />
  379. )}
  380. </Form.Item>
  381. <Form.Item hasFeedback label={fieldLabels.subTitle} {...formItemLayout}>
  382. {getFieldDecorator('subTitle', {
  383. rules: [{ required: true, message: '请填写配套副标题' }],
  384. initialValue: subTitle,
  385. })(
  386. <Input placeholder="请输入" />
  387. )}
  388. </Form.Item>
  389. <Form.Item hasFeedback label={fieldLabels.merchant} {...formItemLayout}>
  390. {getFieldDecorator('cpId', {
  391. rules: [{ required: true, message: '请选择供应商' }],
  392. initialValue: cpId,
  393. })(
  394. <Select placeholder="请选择">
  395. {
  396. getMerchants().map(item => (
  397. <Select.Option key={item.key} value={item.key}>
  398. {item.text}
  399. </Select.Option>
  400. ))
  401. }
  402. </Select>
  403. )}
  404. </Form.Item>
  405. <Form.Item label={fieldLabels.digest} {...formItemLayout}>
  406. {getFieldDecorator('digest', {
  407. initialValue: digest,
  408. })(
  409. <Input.TextArea rows={4} placeholder="请输入" />
  410. )}
  411. </Form.Item>
  412. <Form.Item label={fieldLabels.detail} {...formItemLayout}>
  413. {getFieldDecorator('detail', {
  414. initialValue: detail,
  415. })(
  416. <Input.TextArea rows={6} placeholder="请输入" />
  417. )}
  418. </Form.Item>
  419. <Form.Item label={fieldLabels.status} {...formItemLayout}>
  420. {getFieldDecorator('status', {
  421. valuePropName: 'checked',
  422. initialValue: statusToBool(status),
  423. })(
  424. <Switch
  425. checkedChildren="正常"
  426. unCheckedChildren="删除"
  427. />
  428. )}
  429. </Form.Item>
  430. </Form>
  431. </Card>
  432. {/* 封面及走马灯选择Card */}
  433. <Card title="封面|图册" style={{ marginBottom: 16 }}>
  434. <Row gutter={16}>
  435. <Col
  436. md={{ span: 10, offset: 1 }}
  437. lg={{ span: 8, offset: 2 }}
  438. xl={{ span: 6, offset: 4 }}
  439. xxl={{ span: 4, offset: 5 }}
  440. >
  441. <Card
  442. hoverable
  443. title={renderCoverCardName()}
  444. >
  445. <div className={styles.cover}>
  446. <img src={coverUrl && genAbsolutePicUrl(coverUrl)} alt="" />
  447. </div>
  448. {!coverSelectorDestroy && getResourceModal(true)}
  449. </Card>
  450. </Col>
  451. <Col
  452. md={{ span: 10, offset: 2 }}
  453. lg={{ span: 8, offset: 4 }}
  454. xl={{ span: 6, offset: 4 }}
  455. xxl={{ span: 4, offset: 5 }}
  456. >
  457. <Card
  458. hoverable
  459. title={renderCarouselCardName()}
  460. >
  461. <div className={styles.carousel}>
  462. <Carousel autoplay>
  463. {
  464. imgList.map(
  465. path => (
  466. <img key={path} src={genAbsolutePicUrl(path)} alt="" />
  467. )
  468. )
  469. }
  470. </Carousel>
  471. </div>
  472. {!carouselSelectorDestroy && getResourceModal(false)}
  473. </Card>
  474. </Col>
  475. </Row>
  476. </Card>
  477. {/* 相关周边配套选择Card */}
  478. <Card title={renderSupportCardName()} style={{ marginBottom: 70 }}>
  479. <AXDragSortTable
  480. columns={supportColumns}
  481. data={supportList}
  482. onChange={rows => this.handleDragSortTableChange('support', rows)}
  483. />
  484. {!supportSelectorDestroy && getSupportModal()}
  485. </Card>
  486. <FooterToolbar style={{ width: '100%' }}>
  487. <Button
  488. onClick={this.handlePageBack}
  489. style={{ marginRight: 10 }}
  490. >取消
  491. </Button>
  492. <Button
  493. type="primary"
  494. loading={submitting}
  495. onClick={this.handlePageSubmit}
  496. >提交
  497. </Button>
  498. </FooterToolbar>
  499. </div>
  500. );
  501. }
  502. }