techDemandDesc.jsx 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144
  1. import React from 'react';
  2. import { Icon, Tabs, Table, Tooltip, Modal, Popover, message, Spin, Upload, Input, InputNumber, Select, DatePicker, Button, Radio, Form, Cascader, Tag } from 'antd';
  3. import moment from 'moment';
  4. import './techDemand.less';
  5. import ajax from 'jquery/src/ajax/xhr.js';
  6. import $ from 'jquery/src/ajax';
  7. import { getTechField } from '../../DicTechFieldList.js';
  8. import { IndustryObject, getIndustryCategory } from '../../DicIndustryList.js';
  9. import { beforeUploadFile, splitUrl, companySearch, getDemandType, getAchievementCategory } from '../../tools.js';
  10. import ImgList from "../../common/imgList";
  11. const KeyWordTagGroup = React.createClass({
  12. getInitialState() {
  13. return {
  14. tags: [],
  15. inputVisible: false,
  16. inputValue: ''
  17. };
  18. },
  19. handleClose(removedTag) {
  20. const tags = this.state.tags.filter(tag => tag !== removedTag);
  21. this.props.tagsArr(tags);
  22. this.setState({ tags });
  23. },
  24. showInput() {
  25. this.setState({ inputVisible: true }, () => this.input.focus());
  26. },
  27. handleInputChange(e) {
  28. this.setState({ inputValue: e.target.value });
  29. },
  30. handleInputConfirm() {
  31. const state = this.state;
  32. const inputValue = state.inputValue;
  33. let tags = state.tags;
  34. if (inputValue && tags.indexOf(inputValue) === -1 && inputValue.replace(/(^\s*)|(\s*$)/g, "").length != 0) {
  35. tags = [...tags, inputValue.replace(/(^\s*)|(\s*$)/g, "")];
  36. }
  37. this.props.tagsArr(tags);
  38. this.setState({
  39. tags,
  40. inputVisible: false,
  41. inputValue: '',
  42. });
  43. },
  44. componentWillMount() {
  45. this.state.tags = this.props.keyWordArr;
  46. },
  47. componentWillReceiveProps(nextProps) {
  48. if (this.props.keyWordArr != nextProps.keyWordArr) {
  49. this.state.tags = nextProps.keyWordArr;
  50. };
  51. },
  52. render() {
  53. const { tags, inputVisible, inputValue } = this.state;
  54. return (
  55. <div className="keyWord-tips">
  56. {tags.map((tag, index) => {
  57. const isLongTag = tag.length > 10;
  58. const tagElem = (
  59. <Tag key={tag} closable={index !== -1} afterClose={() => this.handleClose(tag)}>
  60. {isLongTag ? `${tag.slice(0, 10)}...` : tag}
  61. </Tag>
  62. );
  63. return isLongTag ? <Tooltip title={tag}>{tagElem}</Tooltip> : tagElem;
  64. })}
  65. {inputVisible && (
  66. <Input
  67. ref={input => this.input = input}
  68. type="text"
  69. style={{ width: 78 }}
  70. value={inputValue}
  71. onChange={this.handleInputChange}
  72. onBlur={this.handleInputConfirm}
  73. onPressEnter={this.handleInputConfirm}
  74. />
  75. )}
  76. {!inputVisible && <Button className="addtips" type="dashed" disabled={tags.length > 9 ? true : false} onClick={this.showInput}>+ 新关键词</Button>}
  77. </div>
  78. );
  79. }
  80. });
  81. const PicturesWall = React.createClass({
  82. getInitialState() {
  83. return {
  84. previewVisible: false,
  85. previewImage: '',
  86. fileList: [],
  87. }
  88. },
  89. handleCancel() {
  90. this.setState({ previewVisible: false })
  91. },
  92. handlePreview(file) {
  93. this.setState({
  94. previewImage: file.url || file.thumbUrl,
  95. previewVisible: true,
  96. });
  97. },
  98. handleChange(info) {
  99. let fileList = info.fileList;
  100. this.setState({ fileList });
  101. this.props.fileList(fileList);
  102. },
  103. componentWillReceiveProps(nextProps) {
  104. this.state.fileList = nextProps.pictureUrl;
  105. },
  106. render() {
  107. const { previewVisible, previewImage, fileList } = this.state;
  108. const uploadButton = (
  109. <div>
  110. <Icon type="plus" />
  111. <div className="ant-upload-text">点击上传</div>
  112. </div>
  113. );
  114. return (
  115. <div className="clearfix">
  116. <Upload
  117. action={globalConfig.context + "/api/user/demand/uploadPicture"}
  118. data={{ 'sign': 'demand_picture', 'uid': this.props.uid }}
  119. listType="picture-card"
  120. fileList={fileList}
  121. multiple
  122. onPreview={this.handlePreview}
  123. onChange={this.handleChange} >
  124. {uploadButton}
  125. </Upload>
  126. <Modal maskClosable={false} visible={previewVisible} footer={null} onCancel={this.handleCancel}>
  127. <img alt="example" style={{ width: '100%' }} src={previewImage} />
  128. </Modal>
  129. </div>
  130. );
  131. }
  132. });
  133. const DemandDetailShow = Form.create()(React.createClass({
  134. getInitialState() {
  135. return {
  136. visible: false,
  137. loading: false,
  138. contactsObj: {},
  139. salesmanObj: {},
  140. pictureUrl: [],
  141. tags: []
  142. };
  143. },
  144. loadData(id) {
  145. this.setState({
  146. loading: true
  147. });
  148. $.ajax({
  149. method: "get",
  150. dataType: "json",
  151. crossDomain: false,
  152. url: globalConfig.context + (window.userData.type == '1' ? '/api/user/demand/orgDemandDetail' : '/api/user/demand/userDemandDetail'),
  153. data: {
  154. id: id
  155. },
  156. success: function (data) {
  157. let thisData = data.data;
  158. if (!thisData) {
  159. if (data.error && data.error.length) {
  160. message.warning(data.error[0].message);
  161. };
  162. thisData = {};
  163. };
  164. this.setState({
  165. data: thisData,
  166. tags: thisData.keyword ? thisData.keyword.split(",") : [],
  167. pictureUrl: thisData.pictureUrl ? splitUrl(thisData.pictureUrl, ',', globalConfig.avatarHost + '/upload') : []
  168. });
  169. }.bind(this),
  170. }).always(function () {
  171. this.setState({
  172. loading: false
  173. });
  174. }.bind(this));
  175. },
  176. offShelf() {
  177. this.setState({
  178. loading: true
  179. });
  180. $.ajax({
  181. method: "post",
  182. dataType: "json",
  183. crossDomain: false,
  184. url: globalConfig.context + "/api/user/demand/offShelf",
  185. data: { "id": this.props.data.id }
  186. }).done(function (data) {
  187. this.setState({
  188. loading: false
  189. });
  190. if (!data.error.length) {
  191. message.success('撤销成功!');
  192. this.setState({
  193. visible: false,
  194. releaseSubmitVisible: false
  195. });
  196. this.props.handleOk();
  197. } else {
  198. message.warning(data.error[0].message);
  199. }
  200. }.bind(this));
  201. },
  202. componentWillMount() {
  203. if (this.props.data.id) {
  204. this.loadData(this.props.data.id);
  205. } else {
  206. this.state.data = {};
  207. };
  208. },
  209. componentWillReceiveProps(nextProps) {
  210. if (!this.props.visible && nextProps.visible) {
  211. if (nextProps.data.id) {
  212. this.loadData(nextProps.data.id);
  213. } else {
  214. this.state.data = {};
  215. };
  216. this.props.form.resetFields();
  217. };
  218. },
  219. render() {
  220. const theData = this.state.data || {};
  221. const { getFieldDecorator } = this.props.form;
  222. const FormItem = Form.Item
  223. const formItemLayout = {
  224. labelCol: { span: 6 },
  225. wrapperCol: { span: 12 },
  226. };
  227. return (
  228. <Form horizontal onSubmit={this.handleSubmit} id="demand-form">
  229. <Spin spinning={this.state.loading}>
  230. <div className="clearfix">
  231. <FormItem className="half-item"
  232. {...formItemLayout}
  233. label="编号" >
  234. <span>{theData.serialNumber}</span>
  235. </FormItem>
  236. <FormItem className="half-item"
  237. {...formItemLayout}
  238. label="需求名称" >
  239. <span>{theData.name}</span>
  240. </FormItem>
  241. <FormItem className="half-item"
  242. {...formItemLayout}
  243. label="雇主名称" >
  244. <span>{theData.username}</span>
  245. </FormItem>
  246. {window.userData.type == '1' ? <FormItem className="half-item"
  247. {...formItemLayout}
  248. label="联系人" >
  249. <span>{this.props.contactsObj[theData.contacts]}</span>
  250. </FormItem> : <div></div>}
  251. {window.userData.type == '0' ? <FormItem className="half-item"
  252. {...formItemLayout}
  253. label="联系方式" >
  254. <span>{theData.mobile}</span>
  255. </FormItem> : <div></div>}
  256. </div>
  257. <div className="clearfix">
  258. <FormItem className="half-item"
  259. {...formItemLayout}
  260. label="是否发布" >
  261. {(() => {
  262. switch (theData.releaseStatus) {
  263. case "0":
  264. return <span>未发布</span>;
  265. case "1":
  266. return <div>
  267. <span>已发布</span>
  268. <Button style={{ marginLeft: '10px' }} onClick={() => { this.setState({ releaseSubmitVisible: true }); }}>撤消发布</Button>
  269. <Modal maskClosable={false} title="确认"
  270. visible={this.state.releaseSubmitVisible}
  271. width='300px'
  272. footer={null}
  273. onCancel={() => { this.setState({ releaseSubmitVisible: false }) }} >
  274. <span>确认要撤销发布吗?</span>
  275. <Button className="modal-submit" type="primary" onClick={this.offShelf} >确认</Button>
  276. <Button className="modal-submit" type="ghost" onClick={() => { this.setState({ releaseSubmitVisible: false }) }}>取消</Button>
  277. </Modal>
  278. </div>;
  279. }
  280. })()}
  281. </FormItem>
  282. <FormItem className="half-item"
  283. {...formItemLayout}
  284. label="发布时间" >
  285. <span>{theData.releaseDateFormattedDate}</span>
  286. </FormItem>
  287. </div>
  288. <div className="clearfix">
  289. <FormItem className="half-item"
  290. {...formItemLayout}
  291. label="需求状态" >
  292. {(() => {
  293. switch (theData.status) {
  294. case "0":
  295. return <span>未解决</span>;
  296. case "1":
  297. return <span>已解决</span>;
  298. }
  299. })()}
  300. </FormItem>
  301. <FormItem className="half-item"
  302. {...formItemLayout}
  303. label="审核状态" >
  304. {(() => {
  305. switch (theData.auditStatus) {
  306. case "0":
  307. return <span>未提交审核(草稿)</span>;
  308. case "1":
  309. return <span>提交审核</span>;
  310. case "2":
  311. return <span>审核中</span>;
  312. case "3":
  313. return <span>审核通过</span>;
  314. case "4":
  315. return <span>审核未通过</span>;
  316. }
  317. })()}
  318. </FormItem>
  319. </div>
  320. <div className="clearfix">
  321. <FormItem className="half-item"
  322. {...formItemLayout}
  323. label="信息来源" >
  324. {(() => {
  325. switch (theData.infoSources) {
  326. case "0":
  327. return <span>平台采集</span>;
  328. case "1":
  329. return <span>客户发布</span>;
  330. case "2":
  331. return <span>批量导入</span>;
  332. case "3":
  333. return <span>三方对接</span>;
  334. }
  335. })()}
  336. </FormItem>
  337. </div>
  338. <FormItem
  339. labelCol={{ span: 3 }}
  340. wrapperCol={{ span: 20 }}
  341. label="关键词" >
  342. {this.state.tags.map((tag) => {
  343. return <Tooltip key={tag} title={tag}>
  344. <Tag key={tag}>{tag}</Tag>
  345. </Tooltip>;
  346. })}
  347. </FormItem>
  348. <FormItem
  349. labelCol={{ span: 3 }}
  350. wrapperCol={{ span: 18 }}
  351. label="行业类别" >
  352. <span>{getIndustryCategory(theData.industryCategoryA, theData.industryCategoryB)}</span>
  353. </FormItem>
  354. <div className="clearfix">
  355. <FormItem className="half-item"
  356. {...formItemLayout}
  357. label="需求类型" >
  358. <span>{getDemandType(theData.demandType)}</span>
  359. </FormItem>
  360. </div>
  361. <FormItem
  362. labelCol={{ span: 3 }}
  363. wrapperCol={{ span: 18 }}
  364. label="问题说明" >
  365. <span>{theData.problemDes}</span>
  366. </FormItem>
  367. <FormItem
  368. labelCol={{ span: 3 }}
  369. wrapperCol={{ span: 18 }}
  370. label="技术指标要求" >
  371. <span>{theData.technicalRequirements}</span>
  372. </FormItem>
  373. <FormItem
  374. labelCol={{ span: 3 }}
  375. wrapperCol={{ span: 18 }}
  376. label="需求文件(图片)" >
  377. <div className="clearfix">
  378. {/*<Upload className="demandDetailShow-upload"*/}
  379. {/* listType="picture-card"*/}
  380. {/* fileList={this.state.pictureUrl}*/}
  381. {/* onPreview={(file) => {*/}
  382. {/* this.setState({*/}
  383. {/* previewImage: file.url || file.thumbUrl,*/}
  384. {/* previewVisible: true,*/}
  385. {/* });*/}
  386. {/* }} >*/}
  387. <div style={{paddingTop:'10px',paddingBottom:'10px'}}>
  388. <ImgList fileList={this.state.pictureUrl} ItemWidth={'96px'}/>
  389. </div>
  390. <Modal maskClosable={false} footer={null}
  391. visible={this.state.previewVisible}
  392. onCancel={() => { this.setState({ previewVisible: false }) }}>
  393. <img alt="" style={{ width: '100%' }} src={this.state.previewImage || ''} />
  394. </Modal>
  395. </div>
  396. </FormItem>
  397. <FormItem
  398. labelCol={{ span: 3 }}
  399. wrapperCol={{ span: 6 }}
  400. label="需求文件(文本)" >
  401. <div>{theData.textFileUrl ?
  402. <span className="download-file">
  403. <a onClick={() => { window.open(globalConfig.context + '/api/user/demand/download?id=' + this.props.data.id) }}>需求文件(文本文件)</a>
  404. </span>
  405. : <span><Icon type="exclamation-circle" style={{ color: '#ffbf00', marginRight: '6px' }} />未上传!</span>}</div>
  406. </FormItem>
  407. <FormItem
  408. labelCol={{ span: 3 }}
  409. wrapperCol={{ span: 18 }}
  410. label="需求文件(视频地址)" >
  411. <span>{theData.videoUrl}</span>
  412. </FormItem>
  413. <div className="clearfix">
  414. <FormItem className="half-item"
  415. {...formItemLayout}
  416. label="固定周期" >
  417. <span>{theData.fixedCycle}</span>
  418. </FormItem>
  419. <FormItem className="half-item"
  420. {...formItemLayout}
  421. label="固定人数" >
  422. <span>{theData.peopleNumber || 0} 人</span>
  423. </FormItem>
  424. </div>
  425. <FormItem
  426. labelCol={{ span: 3 }}
  427. wrapperCol={{ span: 18 }}
  428. label="固定方案" >
  429. <span>{theData.fixedScheme}</span>
  430. </FormItem>
  431. <div className="clearfix">
  432. <FormItem className="half-item"
  433. {...formItemLayout}
  434. label="预算费用" >
  435. <span>{theData.budgetCost || 0} 万元</span>
  436. </FormItem>
  437. <FormItem className="half-item"
  438. {...formItemLayout}
  439. label="费用托管" >
  440. {(() => {
  441. switch (theData.costEscrow) {
  442. case 0:
  443. return <span>否</span>;
  444. case 1:
  445. return <span>是</span>;
  446. }
  447. })()}
  448. </FormItem>
  449. <FormItem className="half-item"
  450. {...formItemLayout}
  451. label="有效期限" >
  452. <span>{theData.validityPeriodFormattedDate}</span>
  453. </FormItem>
  454. </div>
  455. <FormItem wrapperCol={{ span: 12, offset: 3 }}>
  456. <Button className="set-submit" type="primary" onClick={this.props.closeDesc}>取消</Button>
  457. </FormItem>
  458. </Spin>
  459. </Form >
  460. )
  461. }
  462. }));
  463. const DemandDetailForm = Form.create()(React.createClass({
  464. getInitialState() {
  465. return {
  466. visible: false,
  467. loading: false,
  468. auditStatus: 0,
  469. textFileList: [],
  470. videoFileList: [],
  471. pictureUrl: [],
  472. tags: []
  473. };
  474. },
  475. loadData(id) {
  476. this.setState({
  477. loading: true
  478. });
  479. $.ajax({
  480. method: "get",
  481. dataType: "json",
  482. crossDomain: false,
  483. url: globalConfig.context + (window.userData.type == '1' ? '/api/user/demand/orgDemandDetail' : '/api/user/demand/userDemandDetail'),
  484. data: {
  485. id: id
  486. },
  487. success: function (data) {
  488. let thisData = data.data;
  489. if (!thisData) {
  490. if (data.error && data.error.length) {
  491. message.warning(data.error[0].message);
  492. };
  493. thisData = {};
  494. };
  495. this.setState({
  496. data: thisData,
  497. tags: thisData.keyword ? thisData.keyword.split(",") : [],
  498. pictureUrl: thisData.pictureUrl ? splitUrl(thisData.pictureUrl, ',', globalConfig.avatarHost + '/upload') : []
  499. });
  500. }.bind(this),
  501. }).always(function () {
  502. this.setState({
  503. loading: false
  504. });
  505. }.bind(this));
  506. },
  507. getTagsArr(e) {
  508. this.setState({ tags: e });
  509. },
  510. getPictureUrl(e) {
  511. this.setState({ pictureUrl: e });
  512. },
  513. handleSubmit(e) {
  514. e.preventDefault();
  515. this.props.form.validateFields((err, values) => {
  516. //keyword长度判断
  517. if (this.state.tags.length < 3) {
  518. message.warning('关键词数量不能小于3个!');
  519. return;
  520. };
  521. //url转化
  522. let thePictureUrl = [];
  523. if (this.state.pictureUrl.length) {
  524. let picArr = [];
  525. this.state.pictureUrl.map(function (item) {
  526. if ( item.response && item.response.data && item.response.data.length ){
  527. picArr.push(item.response.data);
  528. }
  529. });
  530. thePictureUrl = picArr.join(",");
  531. };
  532. if (!err) {
  533. this.setState({
  534. loading: true
  535. });
  536. $.ajax({
  537. method: "POST",
  538. dataType: "json",
  539. crossDomain: false,
  540. url: this.props.data.id ? globalConfig.context + '/api/user/demand/update' : globalConfig.context + '/api/user/demand/apply',
  541. data: {
  542. id: this.props.data.id,
  543. dataCategory: Number(window.userData.type),
  544. serialNumber: this.props.data.serialNumber,
  545. name: values.name,
  546. keyword: this.state.tags ? this.state.tags.join(",") : [],
  547. keywords: this.state.tags,
  548. infoSources: 1,
  549. industryCategoryA: values.industryCategory ? values.industryCategory[0] : undefined,
  550. industryCategoryB: values.industryCategory ? values.industryCategory[1] : undefined,
  551. demandType: values.demandType,
  552. problemDes: values.problemDes,
  553. technicalRequirements: values.technicalRequirements,
  554. pictureUrl: thePictureUrl,
  555. textFileUrl: this.state.textFileUrl,
  556. videoUrl: values.videoUrl,
  557. fixedBudget: values.fixedBudget,
  558. fixedCycle: values.fixedCycle,
  559. peopleNumber: values.peopleNumber,
  560. fixedScheme: values.fixedScheme,
  561. costEscrow: values.costEscrow,
  562. budgetCost: values.budgetCost,
  563. employerId: values.employerId || this.state.data.employerId,
  564. releaseStatus: values.releaseStatus,
  565. principalId: values.principalId,
  566. validityPeriodFormattedDate: values.validityPeriod ? values.validityPeriod.format('YYYY-MM-DD') : undefined,
  567. //
  568. contacts: values.contacts,
  569. status: this.state.status,
  570. auditStatus: this.state.auditStatus
  571. }
  572. }).done(function (data) {
  573. this.state.auditStatus = 0;
  574. this.setState({
  575. loading: false
  576. });
  577. if (!data.error.length) {
  578. message.success('保存成功!');
  579. this.setState({
  580. visible: false
  581. });
  582. this.props.handleOk();
  583. } else {
  584. message.warning(data.error[0].message);
  585. }
  586. }.bind(this));
  587. }
  588. });
  589. },
  590. componentWillMount() {
  591. if (this.props.data.id) {
  592. this.loadData(this.props.data.id);
  593. } else {
  594. this.state.data = {};
  595. };
  596. },
  597. componentWillReceiveProps(nextProps) {
  598. if (!this.props.visible && nextProps.visible) {
  599. this.state.textFileList = [];
  600. this.state.videoFileList = [];
  601. if (nextProps.data.id) {
  602. this.loadData(nextProps.data.id);
  603. } else {
  604. this.state.data = {};
  605. this.state.tags = [];
  606. this.state.pictureUrl = [];
  607. };
  608. this.props.form.resetFields();
  609. };
  610. },
  611. render() {
  612. const theData = this.state.data || {};
  613. const { getFieldDecorator } = this.props.form;
  614. const FormItem = Form.Item
  615. const formItemLayout = {
  616. labelCol: { span: 6 },
  617. wrapperCol: { span: 12 },
  618. };
  619. return (
  620. <Form horizontal onSubmit={this.handleSubmit} id="demand-form">
  621. <Spin spinning={this.state.loading}>
  622. <div className="clearfix">
  623. <FormItem className="half-item"
  624. {...formItemLayout}
  625. label="编号" >
  626. <span>{theData.serialNumber}</span>
  627. </FormItem>
  628. <FormItem className="half-item"
  629. {...formItemLayout}
  630. label="需求名称" >
  631. {getFieldDecorator('name', {
  632. rules: [{ required: true, message: '此项为必填项!' }],
  633. initialValue: theData.name
  634. })(
  635. <Input />
  636. )}
  637. </FormItem>
  638. {theData.username ? <FormItem className="half-item"
  639. {...formItemLayout}
  640. label="雇主名称" >
  641. <span>{theData.username}</span>
  642. </FormItem> : <div></div>}
  643. {window.userData.type == "1" ? <FormItem className="half-item"
  644. {...formItemLayout}
  645. label="联系人" >
  646. {getFieldDecorator('contacts', {
  647. initialValue: theData.contacts
  648. })(
  649. <Select style={{ width: 260 }}
  650. placeholder="请选择联系人"
  651. notFoundContent="未获取到联系人列表"
  652. showSearch
  653. filterOption={companySearch}>
  654. {this.props.contactsOption}
  655. </Select>
  656. )}
  657. </FormItem> : <div></div>}
  658. {window.userData.type == "0" ? <FormItem className="half-item"
  659. {...formItemLayout}
  660. label="联系方式" >
  661. <span>{theData.mobile}</span>
  662. </FormItem> : <div></div>}
  663. </div>
  664. {theData.releaseStatus ? <div className="clearfix">
  665. <FormItem className="half-item"
  666. {...formItemLayout}
  667. label="是否发布" >
  668. {(() => {
  669. switch (theData.releaseStatus) {
  670. case "0":
  671. return <span>未发布</span>;
  672. case "1":
  673. return <span>已发布</span>;
  674. }
  675. })()}
  676. </FormItem>
  677. <FormItem className="half-item"
  678. {...formItemLayout}
  679. label="发布时间" >
  680. <span>{theData.releaseDateFormattedDate}</span>
  681. </FormItem>
  682. </div> : <div></div>}
  683. <div className="clearfix">
  684. {theData.releaseStatus == "1" ? <FormItem className="half-item"
  685. {...formItemLayout}
  686. label="需求状态" >
  687. {getFieldDecorator('status', {
  688. initialValue: theData.status
  689. })(
  690. <Radio.Group>
  691. <Radio value="0">未解决</Radio>
  692. <Radio value="1">已解决</Radio>
  693. </Radio.Group>
  694. )}
  695. </FormItem> : <div></div>}
  696. <FormItem className="half-item"
  697. {...formItemLayout}
  698. label="审核状态" >
  699. {(() => {
  700. switch (theData.auditStatus) {
  701. case "0":
  702. return <span>未提交审核(草稿)</span>;
  703. case "1":
  704. return <span>提交审核</span>;
  705. case "2":
  706. return <span>审核中</span>;
  707. case "3":
  708. return <span>审核通过</span>;
  709. case "4":
  710. return <span>审核未通过</span>;
  711. }
  712. })()}
  713. </FormItem>
  714. </div>
  715. <FormItem
  716. labelCol={{ span: 3 }}
  717. wrapperCol={{ span: 20 }}
  718. label="关键词" >
  719. <KeyWordTagGroup
  720. keyWordArr={this.state.tags}
  721. tagsArr={this.getTagsArr}
  722. visible={this.props.visible} />
  723. </FormItem>
  724. <FormItem
  725. labelCol={{ span: 3 }}
  726. wrapperCol={{ span: 18 }}
  727. label="行业类别" >
  728. {getFieldDecorator('industryCategory', {
  729. rules: [{ type: 'array', required: true, message: '此项为必填项!' }],
  730. initialValue: [theData.industryCategoryA, theData.industryCategoryB]
  731. })(
  732. <Cascader style={{ width: 300 }} options={IndustryObject} placeholder="请选择一个行业" />
  733. )}
  734. </FormItem>
  735. <div className="clearfix">
  736. <FormItem className="half-item"
  737. {...formItemLayout}
  738. label="需求类型" >
  739. {getFieldDecorator('demandType', {
  740. rules: [{ required: true, message: '此项为必填项!' }],
  741. initialValue: theData.demandType
  742. })(
  743. <Select style={{ width: 160 }} placeholder="请选择需求类型">
  744. {this.props.demandTypeOption}
  745. </Select>
  746. )}
  747. </FormItem>
  748. </div>
  749. <FormItem
  750. labelCol={{ span: 3 }}
  751. wrapperCol={{ span: 18 }}
  752. label="问题说明" >
  753. {getFieldDecorator('problemDes', {
  754. rules: [{ required: true, message: '此项为必填项!' }],
  755. initialValue: theData.problemDes
  756. })(
  757. <Input type="textarea" rows={4} />
  758. )}
  759. </FormItem>
  760. <FormItem
  761. labelCol={{ span: 3 }}
  762. wrapperCol={{ span: 18 }}
  763. label="技术指标要求" >
  764. {getFieldDecorator('technicalRequirements', {
  765. initialValue: theData.technicalRequirements
  766. })(
  767. <Input type="textarea" rows={2} />
  768. )}
  769. </FormItem>
  770. <FormItem
  771. labelCol={{ span: 3 }}
  772. wrapperCol={{ span: 18 }}
  773. label="需求文件(图片)" >
  774. <PicturesWall
  775. uid={theData.employerId}
  776. fileList={this.getPictureUrl}
  777. pictureUrl={this.state.pictureUrl}
  778. visible={this.props.visible} />
  779. </FormItem>
  780. <FormItem
  781. labelCol={{ span: 3 }}
  782. wrapperCol={{ span: 6 }}
  783. label="需求文件(文本)" >
  784. <Upload
  785. name="ratepay"
  786. action={globalConfig.context + "/api/user/demand/uploadTextFile"}
  787. data={{ 'sign': 'demand_text_file', 'uid': theData.employerId }}
  788. beforeUpload={beforeUploadFile}
  789. fileList={this.state.textFileList}
  790. onChange={(info) => {
  791. if (info.file.status !== 'uploading') {
  792. console.log(info.file, info.fileList);
  793. }
  794. if (info.file.status === 'done') {
  795. if (!info.file.response.error.length) {
  796. message.success(`${info.file.name} 文件上传成功!`);
  797. } else {
  798. message.warning(info.file.response.error[0].message);
  799. return;
  800. };
  801. this.state.textFileUrl = info.file.response.data;
  802. } else if (info.file.status === 'error') {
  803. message.error(`${info.file.name} 文件上传失败。`);
  804. };
  805. this.setState({ textFileList: info.fileList.slice(-1) });
  806. }} >
  807. <Button><Icon type="upload" /> 上传需求文本文件 </Button>
  808. </Upload>
  809. <p style={{ marginTop: '10px' }}>{theData.textFileUrl ?
  810. <span className="download-file">
  811. <a onClick={() => { window.open(globalConfig.context + '/api/user/demand/download?id=' + this.props.data.id) }}>需求文件(文本文件)</a>
  812. </span>
  813. : <span><Icon type="exclamation-circle" style={{ color: '#ffbf00', marginRight: '6px' }} />未上传!</span>}</p>
  814. </FormItem>
  815. <FormItem
  816. labelCol={{ span: 3 }}
  817. wrapperCol={{ span: 18 }}
  818. label="需求文件(视频地址)" >
  819. {getFieldDecorator('videoUrl', {
  820. initialValue: theData.videoUrl
  821. })(
  822. <Input />
  823. )}
  824. </FormItem>
  825. <div className="clearfix">
  826. <FormItem className="half-item"
  827. {...formItemLayout}
  828. label="固定周期" >
  829. {getFieldDecorator('fixedCycle', {
  830. initialValue: theData.fixedCycle
  831. })(
  832. <Input />
  833. )}
  834. </FormItem>
  835. <FormItem className="half-item"
  836. {...formItemLayout}
  837. label="固定人数" >
  838. {getFieldDecorator('peopleNumber', {
  839. initialValue: theData.peopleNumber
  840. })(
  841. <InputNumber min={0} max={999999} step={1} />
  842. )}
  843. <span style={{ marginLeft: '20px' }}>人</span>
  844. </FormItem>
  845. </div>
  846. <FormItem
  847. labelCol={{ span: 3 }}
  848. wrapperCol={{ span: 18 }}
  849. label="固定方案" >
  850. {getFieldDecorator('fixedScheme', {
  851. initialValue: theData.fixedScheme
  852. })(
  853. <Input type="textarea" rows={2} />
  854. )}
  855. </FormItem>
  856. <div className="clearfix">
  857. <FormItem className="half-item"
  858. {...formItemLayout}
  859. label="预算费用" >
  860. {getFieldDecorator('budgetCost', {
  861. initialValue: theData.budgetCost
  862. })(
  863. <InputNumber min={0} max={999999} step={0.01} />
  864. )}
  865. <span style={{ marginLeft: '20px' }}>万元</span>
  866. </FormItem>
  867. <FormItem className="half-item"
  868. {...formItemLayout}
  869. label="费用托管" >
  870. {getFieldDecorator('costEscrow', {
  871. initialValue: theData.costEscrow
  872. })(
  873. <Radio.Group>
  874. <Radio value={0}>否</Radio>
  875. <Radio value={1}>是</Radio>
  876. </Radio.Group>
  877. )}
  878. </FormItem>
  879. <FormItem className="half-item"
  880. {...formItemLayout}
  881. label="有效期限" >
  882. {getFieldDecorator('validityPeriod', {
  883. initialValue: theData.validityPeriod ? moment(theData.validityPeriod) : null
  884. })(
  885. <DatePicker />
  886. )}
  887. </FormItem>
  888. </div>
  889. <div className="clearfix" style={{ display: 'none' }}>
  890. <FormItem className="half-item"
  891. {...formItemLayout}
  892. label="雇主地址" >
  893. <span>{theData.employerAddress}</span>
  894. </FormItem>
  895. <FormItem className="half-item"
  896. {...formItemLayout}
  897. label="雇主联系人名称" >
  898. <span>{theData.employerContacts}</span>
  899. </FormItem>
  900. <FormItem className="half-item"
  901. {...formItemLayout}
  902. label="雇主联系人电话" >
  903. <span>{theData.employerContactsMobile}</span>
  904. </FormItem>
  905. <FormItem className="half-item"
  906. {...formItemLayout}
  907. label="雇主联系人邮箱" >
  908. <span>{theData.employerContactsMailbox}</span>
  909. </FormItem>
  910. </div>
  911. <FormItem wrapperCol={{ span: 12, offset: 3 }}>
  912. <Button className="set-submit" type="primary" htmlType="submit">保存</Button>
  913. <Button className="set-submit" type="ghost" onClick={(e) => { this.state.auditStatus = 1; }} htmlType="submit">发布</Button>
  914. <Button className="set-submit" type="ghost" onClick={this.props.closeDesc}>取消</Button>
  915. </FormItem>
  916. </Spin>
  917. </Form >
  918. )
  919. }
  920. }));
  921. const DemandDetail = React.createClass({
  922. getInitialState() {
  923. return {
  924. visible: false,
  925. contactsObj: {},
  926. tabsKey: "1",
  927. columns: [
  928. {
  929. title: '编号',
  930. dataIndex: 'serialNumber',
  931. key: 'serialNumber',
  932. }, {
  933. title: '数据类型',
  934. dataIndex: 'dataCategory',
  935. key: 'dataCategory',
  936. render: text => {
  937. switch (text) {
  938. case "0":
  939. return <span>成果</span>;
  940. case "1":
  941. return <span>技术</span>;
  942. case "2":
  943. return <span>项目</span>;
  944. }
  945. }
  946. }, {
  947. title: '名称',
  948. dataIndex: 'name',
  949. key: 'name',
  950. }, {
  951. title: '关键字',
  952. dataIndex: 'keyword',
  953. key: 'keyword',
  954. }, {
  955. title: '类型',
  956. dataIndex: 'category',
  957. key: 'category',
  958. render: text => { return getAchievementCategory(text); }
  959. }, {
  960. title: '所有人名称',
  961. dataIndex: 'theName',
  962. key: 'theName',
  963. }, {
  964. title: '所有人类型',
  965. dataIndex: 'ownerType',
  966. key: 'ownerType',
  967. render: text => {
  968. switch (text) {
  969. case "0":
  970. return <span>个人</span>;
  971. case "1":
  972. return <span>组织</span>
  973. }
  974. }
  975. }, {
  976. title: '发布时间',
  977. dataIndex: 'releaseDateFormattedDate',
  978. key: 'releaseDateFormattedDate',
  979. }
  980. ],
  981. dataSource: [],
  982. };
  983. },
  984. getContactsList() {
  985. $.ajax({
  986. method: "get",
  987. dataType: "json",
  988. crossDomain: false,
  989. url: globalConfig.context + "/api/user/cognizance/getContacts",
  990. success: function (data) {
  991. let theOption = [];
  992. if (!data.data) {
  993. if (data.error && data.error.length) {
  994. message.warning(data.error[0].message);
  995. };
  996. } else {
  997. for (let item in data.data) {
  998. let theData = data.data[item];
  999. theOption.push(
  1000. <Select.Option value={item} key={theData}>{theData}</Select.Option>
  1001. );
  1002. };
  1003. };
  1004. this.setState({
  1005. contactsOption: theOption,
  1006. contactsObj: data.data || {}
  1007. });
  1008. }.bind(this),
  1009. });
  1010. },
  1011. getTableList() {
  1012. this.setState({
  1013. loading: true
  1014. });
  1015. $.ajax({
  1016. method: "get",
  1017. dataType: "json",
  1018. crossDomain: false,
  1019. url: globalConfig.context + "/api/user/demand/achievementDemand",
  1020. data: { id: this.props.data.id },
  1021. success: function (data) {
  1022. let theArr = [];
  1023. if (!data.data) {
  1024. if (data.error && data.error.length) {
  1025. message.warning(data.error[0].message);
  1026. };
  1027. } else if (data.data.length) {
  1028. for (let i = 0; i < data.data.length; i++) {
  1029. let thisdata = data.data[i];
  1030. theArr.push({
  1031. key: i,
  1032. id: thisdata.id,
  1033. serialNumber: thisdata.serialNumber,
  1034. dataCategory: thisdata.dataCategory,
  1035. name: thisdata.name,
  1036. keyword: thisdata.keyword,
  1037. category: thisdata.category,
  1038. ownerType: thisdata.ownerType,
  1039. theName: thisdata.username || thisdata.unitName,
  1040. releaseDate: thisdata.releaseDate,
  1041. releaseDateFormattedDate: thisdata.releaseDateFormattedDate
  1042. });
  1043. };
  1044. };
  1045. this.setState({
  1046. dataSource: theArr,
  1047. });
  1048. }.bind(this),
  1049. }).always(function () {
  1050. this.setState({
  1051. loading: false
  1052. });
  1053. }.bind(this));
  1054. },
  1055. tableRowClick(record, index) {
  1056. window.open(globalConfig.context + '/portal/detail/achievementDetail.html?id=' + record.id + '&type=' + record.ownerType);
  1057. },
  1058. showModal() {
  1059. this.setState({
  1060. visible: true,
  1061. });
  1062. },
  1063. handleCancel(e) {
  1064. this.setState({
  1065. visible: false,
  1066. });
  1067. this.props.closeDesc(false);
  1068. },
  1069. handleOk(e) {
  1070. this.setState({
  1071. visible: false,
  1072. });
  1073. this.props.closeDesc(false, true);
  1074. },
  1075. componentWillMount() {
  1076. this.getContactsList();
  1077. },
  1078. componentWillReceiveProps(nextProps) {
  1079. if (nextProps.showDesc) {
  1080. this.state.tabsKey = "1";
  1081. this.state.tabBool = true;
  1082. };
  1083. this.state.visible = nextProps.showDesc;
  1084. },
  1085. render() {
  1086. if (this.props.data) {
  1087. return (
  1088. <div className="patent-desc">
  1089. <Modal maskClosable={false} visible={this.state.visible}
  1090. onOk={this.checkPatentProcess} onCancel={this.handleCancel}
  1091. width='1000px'
  1092. footer=''
  1093. className="admin-desc-content">
  1094. <Tabs activeKey={this.state.tabsKey}
  1095. onChange={(e) => {
  1096. this.setState({ tabsKey: e });
  1097. if (e == "2" && this.state.tabBool) {
  1098. this.getTableList();
  1099. this.state.tabBool = false;
  1100. };
  1101. }} >
  1102. <Tabs.TabPane tab="需求详情" key="1">
  1103. {(() => {
  1104. if (this.props.data.id && this.props.data.auditStatus != "0" && this.props.data.auditStatus != "4") {
  1105. return <DemandDetailShow
  1106. data={this.props.data}
  1107. demandTypeOption={this.props.demandTypeOption}
  1108. contactsObj={this.state.contactsObj}
  1109. closeDesc={this.handleCancel}
  1110. visible={this.state.visible}
  1111. handleOk={this.handleOk} />
  1112. } else {
  1113. return <DemandDetailForm
  1114. data={this.props.data}
  1115. contactsOption={this.state.contactsOption}
  1116. demandTypeOption={this.props.demandTypeOption}
  1117. closeDesc={this.handleCancel}
  1118. visible={this.state.visible}
  1119. handleOk={this.handleOk} />
  1120. }
  1121. })()}
  1122. </Tabs.TabPane>
  1123. <Tabs.TabPane tab="匹配列表" key="2">
  1124. <Table columns={this.state.columns}
  1125. dataSource={this.state.dataSource}
  1126. pagination={false}
  1127. onRowClick={this.tableRowClick} />
  1128. </Tabs.TabPane>
  1129. </Tabs>
  1130. </Modal>
  1131. </div>
  1132. );
  1133. } else {
  1134. return <div></div>
  1135. }
  1136. },
  1137. });
  1138. export default DemandDetail;