RecommendPoster.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  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 { Card, Table, Modal, Popconfirm, Radio, Switch, Button, Input } from 'antd';
  6. import Selector from '../../../components/AXTableSelector/Selector';
  7. import FooterToolbar from '../../../components/FooterToolbar';
  8. import {
  9. boolToStatus, genAbsolutePicUrl, renderProductType, renderStatus, statusToBool,
  10. } from '../../../utils/utils';
  11. import bitmap from '../../../assets/bitmap.png';
  12. import styles from './RecommendPoster.less';
  13. @connect(({ loading, merchant, shelves, resource }) => ({
  14. shelves,
  15. merchant,
  16. resource,
  17. rLoading: loading.models.resource,
  18. sLoading: loading.models.shelves,
  19. mLoading: loading.models.merchant,
  20. }))
  21. export default class RecommendPosterEditPage extends Component {
  22. state = {
  23. productSelectorDestroy: true,
  24. resourceSelectorDestroy: true,
  25. productType: 'Course',
  26. currentEditPosterId: '',
  27. };
  28. componentDidMount() {
  29. this.props.dispatch({
  30. type: 'merchant/fetchMerchantPoster',
  31. payload: { merchantId: this.getMerchantId() },
  32. });
  33. }
  34. /**
  35. * 1.从URL中提取merchantId
  36. * @returns {String}
  37. */
  38. getMerchantId = () => {
  39. const match = pathToRegexp('/frontend/recommend/poster-edit/:id')
  40. .exec(this.props.location.pathname);
  41. return match[1];
  42. };
  43. /**
  44. * 2.响应产品类型变化
  45. * @param e
  46. */
  47. handleRadioChange = (e) => {
  48. this.setState({ productType: e.target.value });
  49. this.props.dispatch({
  50. type: `shelves/fetch${e.target.value}ItemList`,
  51. payload: { merchantId: this.getMerchantId() },
  52. });
  53. };
  54. /**
  55. * 3.新建一条海报
  56. */
  57. handlePosterItemCreate = () => {
  58. const newData = [...this.props.merchant.posterList];
  59. newData.push({
  60. id: `new-poster-${newData.length + 1}`,
  61. isNew: true,
  62. isEdit: true,
  63. });
  64. this.props.dispatch({
  65. type: 'merchant/fixPosterList',
  66. payload: newData,
  67. });
  68. };
  69. /**
  70. * 4.删除一条海报
  71. * @param posterId
  72. * @param isNew
  73. */
  74. handlePosterItemDelete = (posterId, isNew) => {
  75. if (isNew) {
  76. const originalData = [...this.props.merchant.posterList];
  77. const newData = originalData.filter(data => data.id !== posterId);
  78. this.props.dispatch({
  79. type: 'merchant/fixPosterList',
  80. payload: newData,
  81. });
  82. return;
  83. }
  84. this.props.dispatch({
  85. type: 'merchant/deleteMerchantPosterItem',
  86. payload: { posterId },
  87. });
  88. };
  89. /**
  90. * 5.编辑一条海报
  91. * @param posterId
  92. */
  93. handlePosterItemEdit = (posterId) => {
  94. const newData = [...this.props.merchant.posterList];
  95. for (const index in newData) {
  96. if (newData[index].id === posterId) {
  97. newData[index].isEdit = true;
  98. }
  99. }
  100. this.props.dispatch({
  101. type: 'merchant/fixPosterList',
  102. payload: newData,
  103. });
  104. };
  105. /**
  106. * 6.控制模态框的展现
  107. * @param {String} flag
  108. * @param {String} posterId
  109. */
  110. handleSelectorModalShow = (flag, posterId) => {
  111. this.setState({
  112. [`${flag}SelectorDestroy`]: false,
  113. currentEditPosterId: posterId,
  114. });
  115. if (flag !== 'resource') {
  116. const { productType } = this.state;
  117. this.props.dispatch({
  118. type: `shelves/fetch${productType}ItemList`,
  119. payload: { merchantId: this.getMerchantId() },
  120. });
  121. return;
  122. }
  123. this.props.dispatch({
  124. type: 'resource/fetchImageList',
  125. payload: {},
  126. });
  127. };
  128. /**
  129. * 7.控制模态框的销毁
  130. */
  131. handleSelectorCancel = (flag) => {
  132. this.setState({ [`${flag}SelectorDestroy`]: true });
  133. };
  134. /**
  135. * 8.响应模态框内查询操作
  136. * @param {String} flag
  137. * @param {Object} params
  138. */
  139. handleSelectorChange = (flag, params) => {
  140. if (flag !== 'resource') {
  141. const { productType } = this.state;
  142. this.props.dispatch({
  143. type: `shelves/fetch${productType}ItemList`,
  144. payload: { ...params, merchantId: this.getMerchantId() },
  145. });
  146. return;
  147. }
  148. this.props.dispatch({
  149. type: 'resource/fetchImageList',
  150. payload: params,
  151. });
  152. };
  153. /**
  154. * 9.响应选择完成操作
  155. * @param {String} flag
  156. * @param {Array} rows
  157. */
  158. handleSelectorFinish = (flag, rows) => {
  159. this.setState({ [`${flag}SelectorDestroy`]: true });
  160. const { currentEditPosterId } = this.state;
  161. const originalData = [...this.props.merchant.posterList];
  162. const newData = originalData.map((data) => {
  163. if (flag === 'product' && data.id === currentEditPosterId) {
  164. const { pid, name, code, type } = rows[0];
  165. return { ...data, type, name, code, pid };
  166. }
  167. if (flag === 'resource' && data.id === currentEditPosterId) {
  168. const { path } = rows[0];
  169. return { ...data, img: path };
  170. }
  171. return { ...data };
  172. });
  173. this.props.dispatch({
  174. type: 'merchant/fixPosterList',
  175. payload: newData,
  176. });
  177. };
  178. /**
  179. * 10.修改排序值
  180. * @param e
  181. * @param posterId
  182. */
  183. handleSortInputChange = (e, posterId) => {
  184. const originalData = [...this.props.merchant.posterList];
  185. const newData = originalData.map((data) => {
  186. if (data.id === posterId) {
  187. return { ...data, sort: parseInt(e.target.value, 10) || 0 };
  188. }
  189. return { ...data };
  190. });
  191. this.props.dispatch({
  192. type: 'merchant/fixPosterList',
  193. payload: newData,
  194. });
  195. };
  196. /**
  197. * 11.修改状态
  198. * @param checked
  199. * @param posterId
  200. */
  201. handleStatusSwitchChange = (checked, posterId) => {
  202. const originalData = [...this.props.merchant.posterList];
  203. const newData = originalData.map((data) => {
  204. if (data.id === posterId) {
  205. return { ...data, status: boolToStatus(checked) };
  206. }
  207. return { ...data };
  208. });
  209. this.props.dispatch({
  210. type: 'merchant/fixPosterList',
  211. payload: newData,
  212. });
  213. };
  214. /**
  215. * 12.提交海报内容
  216. * @param posterId
  217. */
  218. handleSaveOperation = (posterId) => {
  219. const originalData = [...this.props.merchant.posterList];
  220. const targetData = originalData.filter(data => data.id === posterId)[0];
  221. const { id, pid, img, sort, type, status, isNew } = targetData;
  222. if (isNew) {
  223. this.props.dispatch({
  224. type: 'merchant/createMerchantPosterItem',
  225. payload: {
  226. img, pid, type, sort, status: boolToStatus(status), merchantId: this.getMerchantId(),
  227. },
  228. posterId: id,
  229. });
  230. return;
  231. }
  232. this.props.dispatch({
  233. type: 'merchant/updateMerchantPosterItem',
  234. payload: {
  235. id, img, pid, type, sort, status: boolToStatus(status), merchantId: this.getMerchantId(),
  236. },
  237. posterId: id,
  238. });
  239. };
  240. /**
  241. * 13.返回上一页
  242. */
  243. handlePageBack = () => {
  244. this.props.dispatch(routerRedux.push({
  245. pathname: '/frontend/recommend',
  246. state: this.props.location.state,
  247. }));
  248. };
  249. render() {
  250. const { productType, productSelectorDestroy, resourceSelectorDestroy } = this.state;
  251. const { merchant, shelves, resource, rLoading, sLoading, mLoading } = this.props;
  252. const { posterList } = merchant;
  253. /* 海报列表格式设定 */
  254. const posterColumns = [{
  255. title: '位置',
  256. key: 1,
  257. dataIndex: 'sort',
  258. width: '15%',
  259. render: (text, record) => {
  260. // 编辑状态下排序可自由填写
  261. const { id, isEdit } = record;
  262. if (isEdit) {
  263. return (
  264. <Input
  265. value={text}
  266. onChange={e => this.handleSortInputChange(e, id)}
  267. placeholder="请填写"
  268. style={{ width: 100 }}
  269. />
  270. );
  271. }
  272. return text;
  273. },
  274. align: 'center',
  275. }, {
  276. title: '海报封面',
  277. key: 2,
  278. dataIndex: 'img',
  279. width: '27%',
  280. render: (text, record) => {
  281. // 将海报封面渲染为一张图片,编辑状态下可更换
  282. const { id, isNew, isEdit } = record;
  283. return (
  284. <div className={styles.cover}>
  285. {isEdit && (
  286. <div className={styles.mongolian}>
  287. <a onClick={() => this.handleSelectorModalShow('resource', id)}>{isNew ? '选择' : '更换'}</a>
  288. </div>
  289. )}
  290. {(isNew && !text) ? <img src={bitmap} alt="" /> : <img src={genAbsolutePicUrl(text)} alt="海报封面" />}
  291. </div>
  292. );
  293. },
  294. align: 'center',
  295. }, {
  296. title: '关联产品信息',
  297. key: 3,
  298. render: (_, record) => {
  299. // 将产品信息渲染成一个小表格
  300. const { id, code, name, type, product, isEdit, isNew } = record;
  301. const columns = [{
  302. dataIndex: 'label',
  303. width: '40%',
  304. }, {
  305. dataIndex: 'value',
  306. width: '60%',
  307. }];
  308. const data = [{
  309. key: 'name',
  310. label: '产品名称',
  311. value: name || (product || {}).name,
  312. }, {
  313. key: 'code',
  314. label: '产品编号',
  315. value: code || (product || {}).code,
  316. }, {
  317. key: 'type',
  318. label: '产品类型',
  319. value: renderProductType(type || (product || {}).type),
  320. }];
  321. return (
  322. <div className={styles.product}>
  323. {isEdit && (
  324. <div className={styles.mongolian}>
  325. <a onClick={() => this.handleSelectorModalShow('product', id)}>{isNew ? '选择' : '更换'}</a>
  326. </div>
  327. )}
  328. <Table
  329. bordered
  330. size="small"
  331. showHeader={false}
  332. pagination={false}
  333. columns={columns}
  334. dataSource={data}
  335. />
  336. </div>
  337. );
  338. },
  339. width: '30%',
  340. align: 'center',
  341. }, {
  342. title: '删除状态',
  343. key: 4,
  344. dataIndex: 'status',
  345. width: '13%',
  346. render: (text, record) => {
  347. const { id, isEdit } = record;
  348. if (isEdit) {
  349. return (
  350. <Switch
  351. checked={statusToBool(text)}
  352. checkedChildren="正常"
  353. unCheckedChildren="删除"
  354. onChange={checked => this.handleStatusSwitchChange(checked, id)}
  355. />
  356. );
  357. }
  358. return renderStatus(text);
  359. },
  360. align: 'center',
  361. }, {
  362. title: '相关操作',
  363. key: 5,
  364. width: '15%',
  365. render: (_, record) => {
  366. const { id, isNew, isEdit } = record;
  367. const getPopconfirmBtn = () => {
  368. return (
  369. <Popconfirm
  370. placement="top"
  371. title="确定要删除该海报?"
  372. okText="确定"
  373. cancelText="取消"
  374. onConfirm={() => this.handlePosterItemDelete(id, isNew)}
  375. >
  376. <Button
  377. size="small"
  378. className="delBtn"
  379. >删除
  380. </Button>
  381. </Popconfirm>
  382. );
  383. };
  384. if (isEdit) {
  385. return (
  386. <div>
  387. <Button
  388. size="small"
  389. className="editBtn"
  390. onClick={() => this.handleSaveOperation(id)}
  391. >保存
  392. </Button>
  393. {getPopconfirmBtn()}
  394. </div>
  395. );
  396. }
  397. return (
  398. <div>
  399. <Button
  400. size="small"
  401. className="editBtn"
  402. onClick={() => this.handlePosterItemEdit(id)}
  403. >编辑
  404. </Button>
  405. {getPopconfirmBtn()}
  406. </div>
  407. );
  408. },
  409. align: 'right',
  410. }];
  411. /* 产品模态框选择器 */
  412. const renderModalTitle = () => {
  413. return (
  414. <Radio.Group
  415. value={productType}
  416. onChange={this.handleRadioChange}
  417. >
  418. <Radio.Button value="Course">课程</Radio.Button>
  419. <Radio.Button value="Support">配套</Radio.Button>
  420. <Radio.Button value="Training">师训</Radio.Button>
  421. <Radio.Button value="Package">套餐包</Radio.Button>
  422. </Radio.Group>
  423. );
  424. };
  425. const getProductModal = () => {
  426. return (
  427. <Modal
  428. visible
  429. width={1100}
  430. footer={null}
  431. title={renderModalTitle()}
  432. maskClosable={false}
  433. onCancel={() => this.handleSelectorCancel('product')}
  434. >
  435. <Selector
  436. multiple={false}
  437. loading={sLoading}
  438. selectorName={productType}
  439. fixedName="Product"
  440. list={shelves.list}
  441. pageNo={shelves.pageNo}
  442. pageSize={shelves.pageSize}
  443. totalSize={shelves.totalSize}
  444. onCancel={() => this.handleSelectorCancel('product')}
  445. onChange={data => this.handleSelectorChange('product', data)}
  446. onFinish={data => this.handleSelectorFinish('product', data)}
  447. />
  448. </Modal>
  449. );
  450. };
  451. /* 封面模态框选择器 */
  452. const getResourceModal = () => {
  453. return (
  454. <Modal
  455. width={1100}
  456. footer={null}
  457. visible
  458. title="图片资源"
  459. maskClosable={false}
  460. onCancel={() => this.handleSelectorCancel('resource')}
  461. >
  462. <Selector
  463. multiple={false}
  464. loading={rLoading}
  465. selectorName="PictureSingle"
  466. list={resource.list}
  467. pageNo={resource.pageNo}
  468. pageSize={resource.pageSize}
  469. totalSize={resource.totalSize}
  470. onCancel={() => this.handleSelectorCancel('resource')}
  471. onChange={data => this.handleSelectorChange('resource', data)}
  472. onFinish={rows => this.handleSelectorFinish('resource', rows)}
  473. />
  474. </Modal>
  475. );
  476. };
  477. return (
  478. <div>
  479. <Card style={{ marginBottom: 70 }}>
  480. <Table
  481. pagination={false}
  482. loading={mLoading}
  483. dataSource={posterList}
  484. columns={posterColumns}
  485. rowKey={record => record.id}
  486. className={styles.posterTable}
  487. />
  488. <Button
  489. type="dashed"
  490. icon="plus"
  491. style={{ width: '100%', marginTop: 16, marginBottom: 8 }}
  492. onClick={this.handlePosterItemCreate}
  493. >新建
  494. </Button>
  495. {!resourceSelectorDestroy && getResourceModal()}
  496. {!productSelectorDestroy && getProductModal()}
  497. </Card>
  498. <FooterToolbar style={{ width: '100%' }}>
  499. <Button type="primary" onClick={this.handlePageBack}>返回上一页</Button>
  500. </FooterToolbar>
  501. </div>
  502. );
  503. }
  504. }