punchClocks.jsx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. import React, { Component } from 'react'
  2. import { connect } from 'react-redux'
  3. import { Input, Textarea, View, Text, Picker, Button, Image } from '@tarojs/components'
  4. import Taro from '@tarojs/taro';
  5. import {
  6. AtButton, AtModal, AtModalContent, AtModalAction, AtTextarea
  7. } from "taro-ui";
  8. import dayjs from 'dayjs';
  9. import Skeleton from 'taro-skeleton'
  10. import ImagePicker from '../../components/common/imagePicker';
  11. import { add, minus, asyncAdd } from '../../actions/counter'
  12. import { recordAdd, myDuration, getInfo, myDurationMonth } from '../../utils/servers/servers';
  13. import Select from "../../image/dui.png";
  14. import { stageList } from '../../utils/tools/config';
  15. import './index.less';
  16. import 'taro-ui/dist/style/components/noticebar.scss';
  17. import 'taro-ui/dist/style/components/icon.scss';
  18. import 'taro-ui/dist/style/components/list.scss';
  19. import "taro-ui/dist/style/components/radio.scss";
  20. import "taro-ui/dist/style/components/icon.scss";
  21. @connect(({ counter }) => ({
  22. counter
  23. }), (dispatch) => ({
  24. add() {
  25. dispatch(add())
  26. },
  27. dec() {
  28. dispatch(minus())
  29. },
  30. asyncAdd() {
  31. dispatch(asyncAdd())
  32. }
  33. }))
  34. class PunchClocks extends Component {
  35. constructor(props) {
  36. super(props);
  37. this.state = {
  38. imgs: [],
  39. nowTime: '00:00:00',
  40. loading: false,
  41. selector: stageList,
  42. dateSel: dayjs().format('YYYY-MM-DD'),
  43. duration: "",
  44. hours: 0,
  45. selectorChecked: {},
  46. remark: "",
  47. data: {},
  48. isOpened: false,
  49. clockId: "",
  50. dateList: [],
  51. isCalendar: true,
  52. year: new Date().getFullYear(),
  53. month: new Date().getMonth() + 1,
  54. day: new Date().getDate(),
  55. }
  56. this.onChange = this.onChange.bind(this);
  57. this.publicReleaseClockIn = this.publicReleaseClockIn.bind(this);
  58. this.onClose = this.onClose.bind(this);
  59. this.onMyDurationMonth = this.onMyDurationMonth.bind(this);
  60. this.onmyDuration = this.onmyDuration.bind(this);
  61. }
  62. nowTime = null;
  63. componentDidMount() {
  64. this.imagePickerRef && this.imagePickerRef.clear();
  65. this.getInfo()
  66. }
  67. getInfo() {
  68. getInfo().then(v => {
  69. })
  70. }
  71. componentWillMount() {
  72. const { year, month } = this.state
  73. this.nowTime = setInterval(() => {
  74. this.setState({
  75. nowTime: dayjs().format('HH:mm:ss')
  76. })
  77. }, 1000)
  78. Taro.eventCenter.on('GoPunchIn', (arg) => {
  79. this.setState({
  80. data: arg
  81. }, () => {
  82. this.onmyDuration()
  83. !!arg.id && this.onMyDurationMonth({ id: arg.id }, { year, month })
  84. })
  85. })
  86. }
  87. componentDidHide() {
  88. }
  89. // 剩余打卡时间
  90. onmyDuration() {
  91. myDuration({
  92. id: this.state.data.id,
  93. recordTime: this.state.dateSel,
  94. }).then(v => {
  95. if (v.code === 200) {
  96. this.setState({
  97. hours: v.data,
  98. })
  99. } else {
  100. Taro.showToast({ title: v.msg, icon: 'none' })
  101. }
  102. }).catch(() => {
  103. Taro.showToast({
  104. title: '系统错误,请稍后再试',
  105. icon: 'none'
  106. })
  107. })
  108. }
  109. onMyDurationMonth(val, time) {
  110. myDurationMonth({
  111. id: val.id,
  112. year: time.year,
  113. month: time.month,
  114. }).then(v => {
  115. if (v.code === 200) {
  116. this.setState({
  117. dateList: v.data,
  118. })
  119. } else {
  120. Taro.showToast({ title: v.msg, icon: 'none' })
  121. }
  122. }).catch(() => {
  123. Taro.showToast({
  124. title: '系统错误,请稍后再试',
  125. icon: 'none'
  126. })
  127. })
  128. }
  129. onChange(value, type) {
  130. let arr = this.state.imgs.concat([]);
  131. if (type === "add") {
  132. arr.push(value);
  133. } else if (type === "remove") {
  134. arr.splice(value, 1);
  135. }
  136. this.setState({
  137. imgs: arr,
  138. });
  139. }
  140. publicReleaseClockIn() {
  141. if (!this.state.dateSel) {
  142. Taro.showToast({ title: '请选择研发时间!', icon: 'none' });
  143. return
  144. }
  145. if (!this.state.duration) {
  146. Taro.showToast({ title: '请填写研发工时!', icon: 'none' });
  147. return
  148. }
  149. if (!this.state.selectorChecked) {
  150. Taro.showToast({ title: '请选择研发阶段!', icon: 'none' });
  151. return
  152. }
  153. Taro.showLoading({ title: '打卡中...' });
  154. recordAdd({
  155. pid: this.state.data.id,
  156. projectStatus: this.state.selectorChecked.value,
  157. duration: this.state.duration,
  158. content: this.state.remark,
  159. annexUrl: this.state.imgs.join(','),
  160. processStatus: 1,
  161. recordTime: this.state.dateSel,
  162. }).then(v => {
  163. Taro.hideLoading()
  164. if (v.code === 200) {
  165. Taro.showToast({ title: '打卡成功', icon: 'none' });
  166. this.setState({
  167. clockId: v.data.id,
  168. isOpened: true,
  169. isCalendar: false,
  170. })
  171. } else {
  172. Taro.showToast({ title: v.msg, icon: 'none' })
  173. }
  174. }).catch(() => {
  175. Taro.hideLoading()
  176. Taro.showToast({
  177. title: '系统错误,请稍后再试',
  178. icon: 'none'
  179. })
  180. })
  181. }
  182. refresh() {
  183. this.getData(() => {
  184. Taro.startLocationUpdate({
  185. success: () => {
  186. Taro.onLocationChange((v) => {
  187. this.getSpacing(v.latitude, v.longitude)
  188. })
  189. }
  190. })
  191. });
  192. }
  193. onClose() {
  194. const { clockId } = this.state
  195. this.imagePickerRef && this.imagePickerRef.clear();
  196. this.setState({
  197. dateSel: dayjs().format('YYYY-MM-DD'),
  198. year: new Date().getFullYear(),
  199. month: new Date().getMonth() + 1,
  200. day: new Date().getDate(),
  201. duration: "",
  202. selectorChecked: {},
  203. remark: "",
  204. imgs: [],
  205. isOpened: false,
  206. isCalendar: true,
  207. })
  208. if (!!clockId) {
  209. Taro.navigateTo({
  210. url: `/pages/detail/index?id=${clockId}&type=0`
  211. })
  212. }
  213. }
  214. onSelectDate(val) {
  215. let data = val.detail;
  216. let date = dayjs(data)
  217. this.setState({
  218. dateSel: data,
  219. year: date.year(),
  220. month: date.month() + 1,
  221. day: date.date(),
  222. }, () => {
  223. this.onmyDuration()
  224. })
  225. }
  226. onChangeDate(val) {
  227. let data = val.detail;
  228. this.onMyDurationMonth(this.state.data, data)
  229. }
  230. render() {
  231. const { data, dateList, year, month, day } = this.state;
  232. return (
  233. <View className='punchClock'>
  234. <View className='header'>
  235. <Skeleton
  236. title
  237. avatar
  238. row={2}
  239. animateName='elastic'
  240. avatarShape='square'
  241. loading={this.state.loading}
  242. >
  243. {
  244. !data.id ?
  245. <View className='content'>
  246. <View className='punchClockContent'
  247. style={{
  248. boxShadow: '1px 1px 15px 1px #acb8ad',
  249. background: '#828e83',
  250. marginTop: '85px',
  251. }}>
  252. <View className='punchClockTitle'>研发打卡</View>
  253. <View className='punchClockTime'>{this.state.nowTime}</View>
  254. </View>
  255. <View className='noData'>
  256. <View className='tipsNoData'>
  257. {/* 暂无打卡项目 */}
  258. </View>
  259. <View className='goOut' onClick={() => {
  260. Taro.switchTab({
  261. url: '/pages/project/index',
  262. })
  263. }}>
  264. 点击前往选择项目
  265. </View>
  266. </View>
  267. </View> :
  268. <View className='headerContent'>
  269. <View className='item' style={{ width: "100%", display: "flex", flexDirection: "row", JustifyContent: "space-between" }}>
  270. <View className="item_name">{data.name}</View>
  271. <AtButton type="primary" size="small"
  272. onClick={() => {
  273. Taro.switchTab({
  274. url: "/pages/project/index",
  275. });
  276. }}>切换</AtButton>
  277. </View>
  278. <View className='item'>
  279. <View className='ititle'><Text style={{ color: "red", width: "7px" }}>*</Text>研发时间:
  280. {/* <Picker mode='date'
  281. onChange={e => {
  282. this.setState({
  283. dateSel: e.detail.value
  284. })
  285. }}>
  286. <View className='picker'>
  287. {!!this.state.dateSel ? this.state.dateSel : "请选择"}
  288. </View>
  289. </Picker> */}
  290. <View className='picker'>
  291. {this.state.dateSel}
  292. </View>
  293. </View>
  294. </View>
  295. {
  296. this.state.isCalendar &&
  297. <calendar
  298. use_date_arr={dateList}
  299. year={year}
  300. month={month}
  301. nowYear={year}
  302. nowMonth={month}
  303. nowDay={day}
  304. onselectdate={(v) => {
  305. this.onSelectDate(v)
  306. }}
  307. onchangedate={(v) => {
  308. this.onChangeDate(v)
  309. }}
  310. />
  311. }
  312. <View className='items'>
  313. <View className='ititle'><Text style={{ color: "red", width: "7px" }}>*</Text>研发工时:
  314. <Input value={this.state.duration} type='digit' className='input' placeholder='请输入'
  315. style={{ textAlign: "center", background: "#F5F5F5", borderRadius: "15px", padding: "3px 5px" }}
  316. // border: "1px solid #d6e4ef",
  317. onInput={e => {
  318. this.setState({
  319. duration: e.detail.value
  320. })
  321. }}
  322. />&nbsp;小时&nbsp;
  323. <Text style={{ color: "red", fontSize: "15px" }}>剩余打卡({this.state.hours}h)</Text>
  324. </View>
  325. </View>
  326. <View className='item'>
  327. <View className='ititle'><Text style={{ color: "red", width: "7px" }}>*</Text>研发阶段:
  328. <Picker mode='selector' range={this.state.selector} rangeKey="lable"
  329. value={this.state.selectorChecked.value}
  330. onChange={e => {
  331. this.setState({
  332. selectorChecked: this.state.selector[e.detail.value]
  333. })
  334. }}>
  335. <View className='picker'>
  336. {!!this.state.selectorChecked.lable ? this.state.selectorChecked.lable : "请选择"}
  337. </View>
  338. </Picker>
  339. </View>
  340. </View>
  341. <View className='items'>
  342. <View className='ititles'><Text style={{ width: "7px" }}></Text>研发记录:
  343. <AtTextarea
  344. value={this.state.remark}
  345. onChange={value => {
  346. this.setState({
  347. remark: value,
  348. });
  349. }}
  350. maxLength={500}
  351. placeholder="请填写研发过程记录"
  352. />
  353. {/*
  354. <Textarea
  355. value={this.state.remark}
  356. placeholder="请填写研发过程记录不能超过500字"
  357. style='background:#D7D7D7;width:66%;min-height:70px;padding:10px;border-radius: 10px'
  358. autoHeight
  359. maxlength={500}
  360. onInput={e => {
  361. this.setState({
  362. remark: e.detail.value
  363. })
  364. }}
  365. /> */}
  366. </View>
  367. </View>
  368. <View className='items'>
  369. <View className='ititles'><Text style={{ width: "7px" }}></Text>上传附件:</View>
  370. <ImagePicker
  371. showAddBtn
  372. ref={(ref) => (this.imagePickerRef = ref)}
  373. url="/api/project/upload"
  374. onChange={this.onChange}
  375. />
  376. <View>
  377. <Text className='text'>注:包括研发日志、实验报告、实验记录、检测报告、会议纪要、会议照片等</Text>
  378. </View>
  379. </View>
  380. <View className='content'>
  381. <View className='punchClockContent'
  382. onClick={this.publicReleaseClockIn}
  383. style={{
  384. boxShadow: '1px 1px 15px 1px #acb8ad',
  385. background: '#009DD9',
  386. }}>
  387. <View className='punchClockTitle'>研发打卡</View>
  388. </View>
  389. </View>
  390. </View>
  391. }
  392. </Skeleton>
  393. </View >
  394. <AtModal
  395. width="90%"
  396. isOpened={this.state.isOpened}
  397. >
  398. <AtModalContent>
  399. <View className='tips'>
  400. <Image src={Select} className="img" />
  401. <View className="tit">成功打卡</View>
  402. <View className="txt">您的研发打卡,已经确定</View>
  403. <View className="txt">请注意查看打卡审核情况!</View>
  404. </View>
  405. </AtModalContent>
  406. <AtModalAction>
  407. <Button onClick={this.onClose}>确定</Button>
  408. </AtModalAction>
  409. </AtModal>
  410. </View >
  411. )
  412. }
  413. }
  414. export default PunchClocks