publicContent.jsx 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714
  1. import React, { Component } from "react";
  2. import Taro from "@tarojs/taro";
  3. import { Button, Text, View } from "@tarojs/components";
  4. import dayjs from "dayjs";
  5. import "./index.less";
  6. import ImagePicker from "../../components/common/imagePicker";
  7. import { AtButton, AtTextarea, AtIcon, AtCalendar } from "taro-ui";
  8. import {
  9. addPublicRelease,
  10. getWorkingHoursList,
  11. } from "../../utils/servers/servers";
  12. import {
  13. getHours,
  14. getNumHourse,
  15. getUserWordTimes,
  16. } from "../../utils/tools";
  17. import "taro-ui/dist/style/components/form.scss";
  18. import "taro-ui/dist/style/components/button.scss";
  19. import "taro-ui/dist/style/components/loading.scss";
  20. import "taro-ui/dist/style/components/icon.scss";
  21. import "taro-ui/dist/style/components/icon.scss";
  22. import "taro-ui/dist/style/components/textarea.scss";
  23. import "taro-ui/dist/style/components/calendar.scss";
  24. import "./publicContent.less";
  25. import InquiryModal from "../../components/common/inquiryModal"; //提示弹窗
  26. import UserQuery from "../../components/common/userquery"; //选择协单人弹窗
  27. class PublicContent extends Component {
  28. constructor(props) {
  29. super(props);
  30. this.state = {
  31. rangeStartVal: "",
  32. rangeEndVal: "",
  33. rangeEndMinuteVal: "",
  34. rangeStartMinuteVal: "",
  35. reason: "",
  36. plan: "",
  37. expectedEffect: "",
  38. imgs: [],
  39. validDates: [],
  40. totalDuration: 0,
  41. loading: false,
  42. workTypeList: [],
  43. workType: 0,
  44. isPickerRender: false,
  45. coorderList: [],
  46. cList: [],
  47. popup: false,
  48. };
  49. this.onSubmit = this.onSubmit.bind(this);
  50. this.selectArrder = this.selectArrder.bind(this);
  51. this.onChange = this.onChange.bind(this);
  52. this.getWorkingHoursList = this.getWorkingHoursList.bind(this);
  53. this.onPickerHide = this.onPickerHide.bind(this);
  54. this.onSetPickerTime = this.onSetPickerTime.bind(this);
  55. this.getNumHourse = this.getNumHourse.bind(this);
  56. }
  57. componentDidMount() {
  58. Taro.eventCenter.on("result", () => {
  59. this.imagePickerRef && this.imagePickerRef.clear();
  60. this.setState({
  61. rangeStartVal: "",
  62. rangeEndVal: "",
  63. rangeEndMinuteVal: "",
  64. rangeStartMinuteVal: "",
  65. reason: "",
  66. plan: "",
  67. expectedEffect: "",
  68. imgs: [],
  69. validDates: [],
  70. totalDuration: 0,
  71. loading: false,
  72. });
  73. });
  74. this.getWorkingHoursList();
  75. }
  76. getNumHourse(startTime, endTime) {
  77. this.setState({
  78. totalDuration: getNumHourse(
  79. startTime,
  80. endTime,
  81. this.state.validDates.length
  82. ),
  83. });
  84. }
  85. // 增加 省 市 区拼接,地址带区域的则把区域去掉
  86. getArea() {
  87. const { selectArrderLocation } = this.props
  88. let pcd = selectArrderLocation.province + selectArrderLocation.city + selectArrderLocation.district
  89. let str = selectArrderLocation.name
  90. let index = str.lastIndexOf("区")
  91. let name = str.substring(index + 1)
  92. return pcd + name
  93. }
  94. onSubmit() {
  95. if (!this.state.rangeStartMinuteVal) {
  96. Taro.showToast({ title: "请选择公出时间", icon: "none" });
  97. return;
  98. }
  99. if (!this.state.rangeEndMinuteVal) {
  100. Taro.showToast({ title: "请选择公出时间", icon: "none" });
  101. return;
  102. }
  103. if (
  104. !(
  105. this.props.selectArrderLocation.longitude &&
  106. this.props.selectArrderLocation.latitude
  107. )
  108. ) {
  109. Taro.showToast({ title: "请选择公出地点", icon: "none" });
  110. return;
  111. }
  112. if (!this.state.reason) {
  113. Taro.showToast({ title: "请输入公出目标", icon: "none" });
  114. return;
  115. }
  116. if (!this.state.plan && this.props.enterpriseInfor.type != 2) {
  117. Taro.showToast({ title: "请输入公出计划", icon: "none" });
  118. return;
  119. }
  120. if (!this.state.expectedEffect && this.props.enterpriseInfor.type != 2) {
  121. Taro.showToast({ title: "请输入预计效果", icon: "none" });
  122. return;
  123. }
  124. if (this.state.totalDuration === 0) {
  125. Taro.showToast({
  126. title: "请选择公出时间,目前设置公出时间不足0.5小时",
  127. icon: "none",
  128. });
  129. return;
  130. }
  131. this.setState({
  132. loading: true,
  133. });
  134. const { selectArrderLocation } = this.props
  135. let datas = {
  136. uid: this.props.enterpriseInfor.id,
  137. releaseStarts: this.state.rangeStartMinuteVal,
  138. releaseEnds: this.state.rangeEndMinuteVal,
  139. remarks: this.state.reason,
  140. // plan: this.state.plan,
  141. // expectedEffect: this.state.expectedEffect,
  142. // userName: selectArrderLocation.province +
  143. // selectArrderLocation.city +
  144. // selectArrderLocation.district +
  145. // selectArrderLocation.name,
  146. userName: this.getArea(),
  147. longitude: this.props.selectArrderLocation.longitude,
  148. latitude: this.props.selectArrderLocation.latitude,
  149. annexUrl: this.state.imgs.length === 0 ? "" : this.state.imgs.join(","),
  150. duration: this.state.totalDuration,
  151. type: this.props.enterpriseInfor.type,
  152. validDate: JSON.stringify(this.state.validDates),
  153. assist: this.state.coorderList.length === 0 ? 0 : 1,
  154. assistAid: this.state.coorderList.length === 0 ? "" : this.state.coorderList.join(","),
  155. };
  156. if (this.props.enterpriseInfor.type == 0) {
  157. datas = {
  158. ...datas,
  159. ...{
  160. plan: this.state.plan,
  161. expectedEffect: this.state.expectedEffect,
  162. }
  163. };
  164. } else if (this.props.enterpriseInfor.type == 2) {
  165. datas = datas;
  166. } else {
  167. datas = {
  168. ...datas,
  169. ...{
  170. plan: this.state.plan,
  171. expectedEffect: this.state.expectedEffect,
  172. orderNo: this.props.enterpriseInfor.orderNo,
  173. }
  174. };
  175. }
  176. addPublicRelease(datas)
  177. .then((v) => {
  178. this.setState({
  179. loading: false,
  180. });
  181. if (v.error.length === 0) {
  182. Taro.eventCenter.trigger("publicContent", v.data);
  183. } else {
  184. Taro.showToast({
  185. title: v.error[0].message,
  186. icon: "none",
  187. });
  188. }
  189. })
  190. .catch(() => {
  191. this.setState({
  192. loading: false,
  193. });
  194. });
  195. }
  196. selectArrder() {
  197. const key = "AWBBZ-GRAC2-JFYUO-CLA7I-JAHXK-YFFGT"; //使用在腾讯位置服务申请的key
  198. const referer = "科德打卡定位"; //调用插件的app的名称
  199. const category = "公司企业,房产小区";
  200. Taro.navigateTo({
  201. url:
  202. "plugin://chooseLocation/index?key=" +
  203. key +
  204. "&referer=" +
  205. referer +
  206. "&category=" +
  207. category,
  208. });
  209. }
  210. onChange(value, type) {
  211. let arr = this.state.imgs.concat([]);
  212. if (type === "add") {
  213. arr.push(value);
  214. } else if (type === "remove") {
  215. arr.splice(value, 1);
  216. }
  217. this.setState({
  218. imgs: arr,
  219. });
  220. }
  221. getWorkingHoursList() {
  222. getWorkingHoursList({})
  223. .then((v) => {
  224. if (v.error.length === 0) {
  225. let obj = Taro.getStorageSync("userInfor");
  226. let index = v.data.findIndex(
  227. (value) => value.type === obj.workTimeInfor.type
  228. );
  229. this.setState({
  230. workType: index !== -1 ? index : 0,
  231. });
  232. this.setState({
  233. workTypeList: v.data,
  234. });
  235. } else {
  236. Taro.showToast({
  237. title: v.error[0].message,
  238. icon: "none",
  239. });
  240. }
  241. })
  242. .catch((err) => {
  243. //console.log(err);
  244. });
  245. }
  246. onPickerHide() {
  247. this.setState({
  248. isPickerRender: false,
  249. });
  250. }
  251. onSetPickerTime(val) {
  252. let data = val.detail;
  253. this.setState({
  254. rangeStartMinuteVal: data.selectStartTime,
  255. rangeEndMinuteVal: data.selectEndTime,
  256. });
  257. let arr = [];
  258. if (data.startTime && data.endTime) {
  259. let a = dayjs(data.startTime);
  260. let b = dayjs(data.endTime);
  261. let num;
  262. if (a.hour() <= b.hour()) {
  263. num = b.diff(a, "day") + 1;
  264. } else {
  265. num = b.diff(a, "day") + 2;
  266. }
  267. let strAdd = data.startTime;
  268. for (let i = 0; i < num; i++) {
  269. let time = dayjs(strAdd).add(i, "days").format("YYYY-MM-DD");
  270. arr.push({ value: time });
  271. }
  272. }
  273. this.setState(
  274. {
  275. rangeEndVal: dayjs(data.endTime).format("YYYY-MM-DD"),
  276. rangeStartVal: dayjs(data.startTime).format("YYYY-MM-DD"),
  277. validDates: arr,
  278. },
  279. () => {
  280. let a1 = dayjs(dayjs(data.endTime).format("YYYY-MM-DD HH:mm:ss"));
  281. let b1 = dayjs(dayjs(data.startTime).format("YYYY-MM-DD HH:mm:ss"));
  282. this.getNumHourse(b1, a1);
  283. }
  284. );
  285. }
  286. // 新增技术协单
  287. setList(list, arr) {
  288. this.setState({
  289. cList: list,
  290. coorderList: arr
  291. })
  292. }
  293. // 弹窗开关
  294. popupClick() {
  295. this.setState({
  296. popup: !this.state.popup
  297. })
  298. }
  299. render() {
  300. const { cList, coorderList, popup } = this.state
  301. return (
  302. <View className="publicContent">
  303. <View className="formItem">
  304. <View className="formName">公出企业:</View>
  305. <View className="formValue">{this.props.enterpriseInfor.name}</View>
  306. </View>
  307. <View className="formItem">
  308. <View className="formName">公出类型:</View>
  309. <View className="formValue">
  310. {this.props.enterpriseInfor.type == 0
  311. ? "业务公出" : this.props.enterpriseInfor.type == 1
  312. ? "技术公出" : this.props.enterpriseInfor.type == 2
  313. ? "行政公出" : this.props.enterpriseInfor.type == 3
  314. ? "技术协单" : ""}
  315. </View>
  316. </View>
  317. {this.props.enterpriseInfor.contractNo ? (
  318. <View className="formItem">
  319. <View className="formName">合同编号:</View>
  320. <View className="formValue">
  321. {this.props.enterpriseInfor.contractNo}
  322. </View>
  323. </View>
  324. ) : (
  325. ""
  326. )}
  327. <View className="formItem">
  328. <View className="formName">作息时间类型:</View>
  329. <View className="formValue">
  330. <picker
  331. onChange={(e) => {
  332. this.setState({
  333. workType: e.detail.value,
  334. });
  335. if (this.state.workTypeList[e.detail.value]) {
  336. let obj = Taro.getStorageSync("userInfor");
  337. obj.workTimeInfor = this.state.workTypeList[e.detail.value];
  338. Taro.setStorageSync("userInfor", obj);
  339. this.setState({
  340. rangeStartVal: "",
  341. rangeStartMinuteVal: "",
  342. rangeEndMinuteVal: "",
  343. rangeEndVal: "",
  344. validDates: [],
  345. totalDuration: 0,
  346. });
  347. }
  348. }}
  349. value={this.state.workType}
  350. rangeKey="name"
  351. range={this.state.workTypeList}
  352. >
  353. <view style={{ display: "flex", alignItems: "center" }}>
  354. {this.state.workTypeList[this.state.workType]
  355. ? this.state.workTypeList[this.state.workType].name
  356. : ""}
  357. <AtIcon value="chevron-right" size="20" color="#bbbbbb" />
  358. </view>
  359. </picker>
  360. </View>
  361. </View>
  362. <View className="formItem">
  363. <View className="formName">公出时间:</View>
  364. <View className="formValue">
  365. <View
  366. className="time"
  367. onClick={() => {
  368. this.setState({
  369. isPickerRender: true,
  370. });
  371. }}
  372. >
  373. {this.state.rangeStartMinuteVal &&
  374. this.state.rangeEndMinuteVal ? (
  375. <View className="timeContent">
  376. <View>开始时间:{this.state.rangeStartMinuteVal}</View>
  377. <View>结束时间:{this.state.rangeEndMinuteVal}</View>
  378. </View>
  379. ) : (
  380. "请选择公出时间"
  381. )}
  382. </View>
  383. <timePicker
  384. config={{
  385. endDate: true,
  386. column: "minute",
  387. dateLimit: false,
  388. // initStartTime: "2019-01-01 12:32:44", //默认开始时间
  389. // initEndTime: "2019-12-01 12:32:44", //默认结束时间
  390. limitStartTime: dayjs()
  391. .subtract(3, "year")
  392. .format("YYYY-MM-DD HH:mm:ss"),
  393. limitEndTime: dayjs()
  394. .add(3, "year")
  395. .format("YYYY-MM-DD HH:mm:ss"),
  396. }}
  397. isPartition
  398. pickerShow={this.state.isPickerRender}
  399. onconditionaljudgment={(v) => {
  400. let a = dayjs(
  401. dayjs(v.detail.endTime)
  402. .second(0)
  403. .format("YYYY-MM-DD HH:mm:ss")
  404. );
  405. let b = dayjs(dayjs().second(0).format("YYYY-MM-DD HH:mm:ss"));
  406. if (a.isBefore(b)) {
  407. Taro.showToast({
  408. title: "结束时间不能小于当前时间",
  409. icon: "none",
  410. });
  411. v.detail.setLv(false);
  412. }
  413. }}
  414. onhidepicker={() => {
  415. this.onPickerHide();
  416. }}
  417. onsetpickertime={(v) => {
  418. this.onSetPickerTime(v);
  419. }}
  420. />
  421. </View>
  422. </View>
  423. <View className="formItem">
  424. <View className="formName">总时长:</View>
  425. <View className="formValue">{this.state.totalDuration}小时</View>
  426. </View>
  427. {this.state.validDates.length !== 0 ? (
  428. <View
  429. className="formItem"
  430. style={{ display: "block", paddingTop: "15px" }}
  431. >
  432. <View className="formName">
  433. <View>去除放假时间:</View>
  434. </View>
  435. <View className="tips">
  436. 绿色方块为公出时间,长按即可去除放假时间
  437. </View>
  438. <AtCalendar
  439. currentDate={this.state.rangeStartVal}
  440. minDate={this.state.rangeStartVal}
  441. maxDate={this.state.rangeEndVal}
  442. isSwiper={false}
  443. validDates={this.state.validDates}
  444. onDayLongClick={(item) => {
  445. if (
  446. !(
  447. dayjs(item.value).isSame(this.state.rangeStartVal) ||
  448. dayjs(item.value).isSame(this.state.rangeEndVal)
  449. )
  450. ) {
  451. if (
  452. !(
  453. dayjs(item.value).isAfter(this.state.rangeStartVal) &&
  454. dayjs(item.value).isBefore(this.state.rangeEndVal)
  455. )
  456. ) {
  457. return;
  458. }
  459. } else {
  460. Taro.showToast({
  461. title: "开始结束时间不能取消",
  462. icon: "none",
  463. });
  464. return;
  465. }
  466. let index = this.state.validDates.findIndex((v) => {
  467. let lv = dayjs(v.value).isSame(item.value);
  468. return lv;
  469. });
  470. const { start, end } = getUserWordTimes();
  471. let totalDuration = getHours(
  472. dayjs().format("YYYY-MM-DD") + start,
  473. dayjs(dayjs().format("YYYY-MM-DD") + end)
  474. ); //一天工作时长
  475. if (index < 0) {
  476. let arr = item.value.split("-");
  477. this.state.validDates.push({ value: arr.join("-") });
  478. this.setState({
  479. validDates: this.state.validDates,
  480. totalDuration: this.state.totalDuration + totalDuration,
  481. });
  482. } else {
  483. this.state.validDates.splice(index, 1);
  484. this.setState({
  485. validDates: this.state.validDates,
  486. totalDuration: this.state.totalDuration - totalDuration,
  487. });
  488. }
  489. }}
  490. />
  491. </View>
  492. ) : null}
  493. <View className="formItem" style={{ paddingBottom: 0 }}>
  494. <View className="formName">公出地点:</View>
  495. <View className="formValue" onClick={this.selectArrder}>
  496. <Text className="formValueText">
  497. {this.props.selectArrderLocation.name}
  498. </Text>
  499. <AtIcon value="chevron-right" size="30" color="#bbbbbb" />
  500. </View>
  501. </View>
  502. <View className="tips">
  503. 以地图为中心100米范围为可打卡区域,移动红标只需要拖动地图即可
  504. </View>
  505. {
  506. this.props.enterpriseInfor.type === 0 &&
  507. <View className="formItems" style={{ paddingBottom: 0, marginTop: 10 }}>
  508. <View className="formNames">技术协单: </View>
  509. <View className="formValues">
  510. {
  511. cList.length == 0
  512. ? <Text>无</Text>
  513. : cList.map((item, index) =>
  514. <View className="fitems" key={index}>
  515. {item.name}
  516. <View className="fclose"
  517. onClick={() => {
  518. coorderList.includes(item.id) &&
  519. (
  520. cList.splice(coorderList.indexOf(item.id), 1),
  521. coorderList.splice(coorderList.indexOf(item.id), 1)
  522. )
  523. this.setState({
  524. cList,
  525. coorderList,
  526. })
  527. }}
  528. ><AtIcon value="close-circle" size="15" color="#bbbbbb" /></View>
  529. </View>
  530. )
  531. }
  532. {cList.length < 5 && <Button className="addbuttom" type='primary' onClick={this.popupClick.bind(this)}>新增技术协单</Button>}
  533. </View>
  534. </View>
  535. }
  536. {
  537. this.props.enterpriseInfor.type === 0 &&
  538. <View className="tips">
  539. 请与技术协商后,再发起协单,协单将统计公出成本等
  540. </View>
  541. }
  542. <View
  543. className="formItem"
  544. style={{ display: "block", paddingTop: "15px" }}
  545. >
  546. {/* 公出目标 */}
  547. <View className="formName">
  548. <Text style={{ color: "red" }}>*公出目标:</Text>
  549. <View
  550. className="formValue"
  551. style={{ paddingTop: "10px", textAlign: "left" }}
  552. >
  553. <AtTextarea
  554. height={46}
  555. value={this.state.reason}
  556. onChange={(v) => {
  557. this.setState({
  558. reason: v,
  559. });
  560. }}
  561. maxLength={200}
  562. placeholder={
  563. this.props.enterpriseInfor.type == 2
  564. ? "详细说明本次公出的内容,预计完成的内容"
  565. : "本次公出目标,谈的思路与步骤?"
  566. }
  567. />
  568. </View>
  569. </View>
  570. {/* 公出计划 */}
  571. {this.props.enterpriseInfor.type != 2 && (
  572. <View className="formName">
  573. <Text style={{ color: "red" }}>*公出计划:</Text>
  574. <View
  575. className="formValue"
  576. style={{ paddingTop: "10px", textAlign: "left" }}
  577. >
  578. <AtTextarea
  579. height={46}
  580. value={this.state.plan}
  581. onChange={(v) => {
  582. this.setState({
  583. plan: v,
  584. });
  585. }}
  586. maxLength={200}
  587. placeholder="本次公出准备工作"
  588. />
  589. </View>
  590. </View>
  591. )}
  592. {/* 预计效果 */}
  593. {this.props.enterpriseInfor.type != 2 && (
  594. <View className="formName">
  595. <Text style={{ color: "red" }}>*预计效果:</Text>
  596. <View
  597. className="formValue"
  598. style={{ paddingTop: "10px", textAlign: "left" }}
  599. >
  600. <AtTextarea
  601. height={46}
  602. value={this.state.expectedEffect}
  603. onChange={(v) => {
  604. this.setState({
  605. expectedEffect: v,
  606. });
  607. }}
  608. maxLength={200}
  609. placeholder="预计本次公出效果"
  610. />
  611. </View>
  612. </View>
  613. )}
  614. </View>
  615. {/* */}
  616. <View className="formItem" style={{ display: "block" }}>
  617. <View className="formName">附件:</View>
  618. <View
  619. className="formValue"
  620. style={{ paddingTop: "10px", textAlign: "left" }}
  621. >
  622. <ImagePicker
  623. showAddBtn
  624. ref={(ref) => (this.imagePickerRef = ref)}
  625. url="/api/admin/release/upload"
  626. onChange={this.onChange}
  627. />
  628. </View>
  629. </View>
  630. <View className="operation">
  631. <AtButton
  632. disabled={this.state.loading}
  633. type="secondary"
  634. circle
  635. onClick={() => {
  636. Taro.eventCenter.trigger("enterpriseBack");
  637. }}
  638. >
  639. 上一步
  640. </AtButton>
  641. <AtButton
  642. type="primary"
  643. loading={this.state.loading}
  644. circle
  645. onClick={() => {
  646. if (this.state.loading) {
  647. return;
  648. }
  649. this.setState({
  650. isInquiryOpened: true,
  651. inquiryTitle: "提醒",
  652. inquiryContent: "您确定要提交此申请吗?",
  653. inquiryFn: () => {
  654. this.onSubmit();
  655. },
  656. });
  657. }}
  658. >
  659. 提交申请
  660. </AtButton>
  661. </View>
  662. <InquiryModal
  663. isOpened={this.state.isInquiryOpened}
  664. title={this.state.inquiryTitle}
  665. content={this.state.inquiryContent}
  666. onClose={() => {
  667. this.setState({
  668. isInquiryOpened: false,
  669. inquiryTitle: "",
  670. inquiryContent: "",
  671. });
  672. }}
  673. onDetermine={() => {
  674. this.state.inquiryFn();
  675. this.setState({
  676. isInquiryOpened: false,
  677. inquiryTitle: "",
  678. inquiryContent: "",
  679. inquiryFn: () => { },
  680. });
  681. }}
  682. />
  683. {popup &&
  684. <UserQuery
  685. isOpened={popup}
  686. onDesc={this.popupClick.bind(this)}
  687. cList={cList}
  688. coorderList={coorderList}
  689. setList={this.setList.bind(this)}
  690. />
  691. }
  692. </View>
  693. );
  694. }
  695. }
  696. export default PublicContent;