punchClocks.jsx 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632
  1. import React, { Component } from 'react'
  2. import { connect } from 'react-redux'
  3. import {Map, OpenData, View, Image, Text} from '@tarojs/components'
  4. import Taro from '@tarojs/taro';
  5. import {AtIcon, AtModal} from "taro-ui";
  6. import dayjs from 'dayjs';
  7. import Skeleton from 'taro-skeleton'
  8. import HistoricalClock from '../../components/historicalClock';
  9. import ImagePicker from '../../components/common/imagePicker';
  10. import { add, minus, asyncAdd } from '../../actions/counter'
  11. import {getPublicReleaseList,publicReleaseClockIn} from '../../utils/servers/servers';
  12. import './index.less'
  13. import switchIocn from '../../image/switch.png';
  14. import Location from '../../image/location.png';
  15. import PunchRange from '../../image/punchRange.png';
  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 {getClockState} from "../../utils/tools";
  20. const QQMapWX = require('../../components/common/mapSDK/qqmap-wx-jssdk.min.js');
  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. newArrder:{},
  41. addressLatitude:'',
  42. addressLongitude:'',
  43. distance:'',
  44. dtails:{},
  45. loading:true
  46. }
  47. this.onChange = this.onChange.bind(this);
  48. this.getSpacing = this.getSpacing.bind(this);
  49. this.getPublicReleaseList = this.getPublicReleaseList.bind(this);
  50. this.publicReleaseClockIn = this.publicReleaseClockIn.bind(this);
  51. this.refresh = this.refresh.bind(this);
  52. this.getDtails = this.getDtails.bind(this);
  53. }
  54. nowTime = null;
  55. wxConfig= null;
  56. mapCtx = Taro.createMapContext('map');
  57. qqmapsdk = new QQMapWX({
  58. key: 'AWBBZ-GRAC2-JFYUO-CLA7I-JAHXK-YFFGT'
  59. });
  60. componentDidMount(){
  61. this.getDtails();
  62. }
  63. getDtails(){
  64. this.getPublicReleaseList(()=>{
  65. Taro.startLocationUpdate({
  66. success:()=>{
  67. Taro.onLocationChange((v)=>{
  68. this.getSpacing(v.latitude,v.longitude)
  69. })
  70. },
  71. fail:(err)=>{
  72. console.log(err,'err')
  73. Taro.getSetting({
  74. success:(res)=>{
  75. if (!res.authSetting['scope.userLocation']) {
  76. Taro.authorize({
  77. scope: 'scope.userLocation',
  78. success:()=>{
  79. },
  80. fail:()=>{
  81. this.setState({
  82. isOpened:true
  83. })
  84. }
  85. })
  86. }
  87. }
  88. })
  89. this.setState({
  90. loading:false
  91. })
  92. }
  93. })
  94. });
  95. }
  96. componentWillMount() {
  97. this.wxConfig = Taro.getStorageSync('wxConfig');
  98. //获得当前位置
  99. Taro.getLocation({
  100. type: 'gcj02',
  101. isHighAccuracy:true,
  102. success: (res)=> {
  103. const latitude = res.latitude
  104. const longitude = res.longitude
  105. this.setState({
  106. newArrder:{
  107. latitude,
  108. longitude
  109. },
  110. })
  111. }
  112. })
  113. this.nowTime = setInterval(()=>{
  114. this.setState({
  115. nowTime:dayjs().format('HH:mm:ss')
  116. })
  117. },1000)
  118. Taro.eventCenter.on('punchClockComponentDidShow', () => {
  119. if(Object.keys(this.state.dtails).length === 0){
  120. this.getDtails();
  121. }
  122. })
  123. Taro.eventCenter.on('GoPunchIn', (arg) => {
  124. this.setState({
  125. dtails:arg,
  126. addressLatitude:parseFloat(arg.latitude),
  127. addressLongitude:parseFloat(arg.longitude),
  128. },()=>{
  129. Taro.startLocationUpdate({
  130. success:()=>{
  131. Taro.onLocationChange((v)=>{
  132. this.getSpacing(v.latitude,v.longitude)
  133. })
  134. },
  135. fail:()=>{
  136. this.setState({
  137. loading:false
  138. })
  139. }
  140. })
  141. })
  142. })
  143. }
  144. componentDidHide() {
  145. Taro.offLocationChange();
  146. this.nowTime && this.nowTime.clearTimeout();
  147. }
  148. publicReleaseClockIn(){
  149. if(this.state.dtails.status === 3){
  150. Taro.hideLoading()
  151. Taro.showToast({title:'申请已撤销,无法打卡',icon:'none'});
  152. return ;
  153. }
  154. if(Object.keys(this.state.dtails).length === 0){
  155. Taro.hideLoading()
  156. Taro.showToast({title:'暂无公出申请,无法打卡',icon:'none'});
  157. return ;
  158. }
  159. if(isNaN(parseInt(this.state.distance))){
  160. Taro.hideLoading()
  161. Taro.showToast({title:'定位错误,请联系管理员',icon:'none'})
  162. return ;
  163. }
  164. if(dayjs().isBefore(this.state.dtails.releaseStarts) || dayjs().isAfter(this.state.dtails.releaseEnds)){
  165. Taro.hideLoading()
  166. Taro.showToast({title:'当前时间不在打卡时间范围内,无法打卡',icon:'none'})
  167. return ;
  168. }
  169. if(!(this.state.distance <=this.wxConfig.clockInRange && this.state.distance >= 0)){
  170. Taro.hideLoading()
  171. Taro.showToast({title:'当前位置未处于可打卡范围,无法打卡',icon:'none'})
  172. return ;
  173. }
  174. if(this.state.imgs.length === 0){
  175. Taro.hideLoading()
  176. Taro.showToast({title:'请上传打卡照片',icon:'none'})
  177. return ;
  178. }
  179. publicReleaseClockIn({
  180. id:this.state.dtails.id,
  181. photoUrl:this.state.imgs.join(',')
  182. }).then(v=>{
  183. Taro.hideLoading()
  184. if(v.error.length === 0){
  185. Taro.showToast({title:'打卡成功',icon:'none'});
  186. this.imagePicker.clear();
  187. this.state.dtails.clockIn = 1;
  188. this.setState({
  189. dtails:this.state.dtails,
  190. imgs:[]
  191. })
  192. }else{
  193. Taro.showToast({title:v.error[0].message,icon:'none'})
  194. }
  195. }).catch(()=>{
  196. Taro.hideLoading()
  197. Taro.showToast({
  198. title:'系统错误,请稍后再试',
  199. icon:'none'
  200. })
  201. })
  202. }
  203. getPublicReleaseList(fn){
  204. this.setState({
  205. loading:true
  206. })
  207. getPublicReleaseList({
  208. type:0,
  209. clockTime:dayjs().format('YYYY-MM-DD HH:mm:ss')
  210. }).then(v=>{
  211. if(v.error.length === 0){
  212. if(v.data.list.length === 0){
  213. getPublicReleaseList({
  214. type:0,
  215. }).then(value=>{
  216. if(value.error.length === 0){
  217. if(value.data.list.length === 0){
  218. this.setState({
  219. dtails: {},
  220. loading:false
  221. })
  222. }else{
  223. this.setState({
  224. dtails:value.data.list[0],
  225. addressLatitude:value.data.list[0].latitude,
  226. addressLongitude:value.data.list[0].longitude,
  227. },()=>{
  228. fn();
  229. })
  230. }
  231. }else{
  232. this.setState({
  233. loading:false
  234. })
  235. Taro.showToast({
  236. title:v.error[0].message,
  237. icon:'none'
  238. })
  239. }
  240. }).catch(()=>{
  241. this.setState({
  242. loading:false
  243. })
  244. Taro.showToast({
  245. title:'系统错误,请稍后再试',
  246. icon:'none'
  247. })
  248. })
  249. }else{
  250. this.setState({
  251. dtails:v.data.list[0],
  252. addressLatitude:v.data.list[0].latitude,
  253. addressLongitude:v.data.list[0].longitude,
  254. },()=>{
  255. fn();
  256. })
  257. }
  258. }else{
  259. this.setState({
  260. loading:false
  261. })
  262. Taro.showToast({
  263. title:v.error[0].message,
  264. icon:'none'
  265. })
  266. }
  267. }).catch((err)=>{
  268. console.log(err)
  269. this.setState({
  270. loading:false
  271. })
  272. Taro.showToast({
  273. title:'系统错误,请稍后再试',
  274. icon:'none'
  275. })
  276. })
  277. }
  278. getSpacing(latitude,longitude){
  279. let _this = this;
  280. this.qqmapsdk.calculateDistance({
  281. mode:'straight',
  282. from: {
  283. latitude: this.state.addressLatitude,
  284. longitude: this.state.addressLongitude,
  285. },
  286. to: [
  287. {
  288. latitude,
  289. longitude,
  290. }
  291. ],
  292. success: function(res) {//成功后的回调
  293. _this.setState({
  294. distance:res.result.elements[0].distance,
  295. loading:false
  296. })
  297. },
  298. fail: function(error) {
  299. _this.setState({
  300. loading:false
  301. })
  302. console.error(error);
  303. },
  304. complete: function(res) {
  305. }
  306. });
  307. }
  308. onChange(value,type){
  309. let arr = this.state.imgs.concat([]);
  310. if(type === 'add'){
  311. arr.push(value)
  312. }else if(type === 'remove'){
  313. arr.splice(value,1)
  314. }
  315. this.setState({
  316. imgs: arr,
  317. })
  318. }
  319. refresh(){
  320. this.getPublicReleaseList(()=>{
  321. Taro.startLocationUpdate({
  322. success:()=>{
  323. Taro.onLocationChange((v)=>{
  324. this.getSpacing(v.latitude,v.longitude)
  325. })
  326. }
  327. })
  328. });
  329. }
  330. render () {
  331. const {dtails} = this.state;
  332. return (
  333. <View className='punchClock'>
  334. <View className='header'>
  335. <Skeleton
  336. title
  337. avatar
  338. row={1}
  339. animateName='elastic'
  340. avatarShape='square'
  341. loading={this.state.loading}
  342. >
  343. <View className='headerContent'>
  344. <View className='userAvatarUrl'>
  345. <OpenData type='userAvatarUrl'/>
  346. </View>
  347. <View className='infor'>
  348. <View className='name'>
  349. {
  350. Object.keys(this.state.dtails).length <= 0 ?
  351. Taro.getStorageSync('userInfor').name:
  352. dtails.aname
  353. }
  354. </View>
  355. <View className='address'>
  356. 公出企业:
  357. <View className='nickname'>
  358. {
  359. Object.keys(this.state.dtails).length <= 0 ?
  360. '无':
  361. dtails.nickname
  362. }
  363. </View>
  364. </View>
  365. </View>
  366. {
  367. Object.keys(this.state.dtails).length <= 0 ?
  368. <View onClick={this.refresh} className='state' style={{
  369. alignSelf: 'center',
  370. textAlign:'center',
  371. color:'#b6b5b5',
  372. fontSize:'11px'
  373. }}>
  374. <AtIcon value='reload' size='20'/>
  375. <View>刷新</View>
  376. </View> :
  377. <View className='state' style={{
  378. color:getClockState(dtails.status).color
  379. }}>
  380. <View style={{textAlign:'right'}}>
  381. {
  382. getClockState(dtails.status).title
  383. }
  384. </View>
  385. <View className='switchContent' onClick={()=>{
  386. Taro.switchTab({
  387. url: '/pages/examine/index',
  388. })
  389. }}>
  390. <View className='switchTitle'>切换打卡</View>
  391. <Image src={switchIocn} className='switchItem'/>
  392. </View>
  393. </View>
  394. }
  395. </View>
  396. </Skeleton>
  397. </View>
  398. {
  399. Object.keys(this.state.dtails).length <= 0 ?
  400. <View className='content'>
  401. <View className='punchClockContent' onClick={this.publicReleaseClockIn} style={{
  402. boxShadow: '1px 1px 15px 1px #acb8ad',
  403. background: '#acb8ad',
  404. marginTop: '85px',
  405. }}>
  406. <View className='punchClockTitle'>公出打卡</View>
  407. <View className='punchClockTime'>{this.state.nowTime}</View>
  408. </View>
  409. <View className='noData'>
  410. <View className='tipsNoData'>
  411. 暂无公出申请
  412. </View>
  413. <View className='goOut' onClick={()=>{
  414. Taro.switchTab({
  415. url: '/pages/applyDepart/index',
  416. })
  417. }}>
  418. 点击前往申请公出
  419. </View>
  420. </View>
  421. </View>:
  422. <View>
  423. <View className='content'>
  424. <View className='timeContent'>
  425. <View className='timeItem'>
  426. <View className='timeTitle'>公出开始时间</View>
  427. <View className='timeValue'>{dtails.releaseStarts}</View>
  428. </View>
  429. <View className='timeItem'>
  430. <View className='timeTitle'>公出结束时间</View>
  431. <View className='timeValue'>{dtails.releaseEnds}</View>
  432. </View>
  433. </View>
  434. <View className='photographContent'>
  435. <View className='photographTitle'>打卡拍照<Text className='tips'>〔可以上传多张〕</Text></View>
  436. <AtIcon onClick={()=>{
  437. Taro.chooseImage({
  438. count: 1,
  439. sizeType: ['compressed'],
  440. sourceType: ['camera'],
  441. success:(res)=> {
  442. // tempFilePath可以作为img标签的src属性显示图片
  443. const tempFilePaths = res.tempFilePaths
  444. this.imagePicker.onImgChange([{url:tempFilePaths[0]}],'add',0,'camera')
  445. }
  446. })
  447. }} value='camera' size='25' color='#4395ff'/>
  448. </View>
  449. <View style={{alignSelf:'flex-start',width:'100%'}}>
  450. <ImagePicker ref={ref => this.imagePicker = ref} url='/api/admin/release/upload' showAddBtn={false} onChange={this.onChange}/>
  451. </View>
  452. <View className='punchClockContent' onClick={()=>{
  453. let _this = this;
  454. Taro.showLoading({title:'加载中...'})
  455. Taro.getSetting({
  456. success:(res)=>{
  457. if (!res.authSetting['scope.userLocation']) {
  458. Taro.authorize({
  459. scope: 'scope.userLocation',
  460. success:()=>{
  461. _this.publicReleaseClockIn();
  462. },
  463. fail:()=>{
  464. Taro.hideLoading()
  465. this.setState({
  466. isOpened:true
  467. })
  468. }
  469. })
  470. }else{
  471. _this.publicReleaseClockIn();
  472. }
  473. }
  474. })
  475. }} style={{
  476. boxShadow:
  477. (!isNaN(parseInt(this.state.distance)))
  478. && this.state.distance <=this.wxConfig.clockInRange && this.state.distance >= 0 &&
  479. !(dayjs().isBefore(this.state.dtails.releaseStarts) || dayjs().isAfter(this.state.dtails.releaseEnds)) &&
  480. dtails.status !== 3
  481. ? (dtails.clockIn === 1 ? '1px 1px 15px 1px #3f82e8' : '1px 1px 15px 1px #659268') : '1px 1px 15px 1px #acb8ad',
  482. background: (!isNaN(parseInt(this.state.distance)))
  483. &&
  484. this.state.distance <=this.wxConfig.clockInRange &&
  485. this.state.distance >= 0 &&
  486. !(dayjs().isBefore(this.state.dtails.releaseStarts) || dayjs().isAfter(this.state.dtails.releaseEnds))&&
  487. dtails.status !== 3
  488. ? (dtails.clockIn === 1 ? '#3f82e8' : '#72cb78') : '#acb8ad'
  489. }}>
  490. <View className='punchClockTitle'>
  491. {
  492. dtails.clockIn === 1 ?
  493. '刷新打卡' : '公出打卡'
  494. }
  495. </View>
  496. <View className='punchClockTime'>{this.state.nowTime}</View>
  497. </View>
  498. <View className='map'>
  499. <View className='locationContent'>
  500. <View className='location' onClick={()=>{
  501. this.mapCtx.moveToLocation();
  502. }}>
  503. <Image src={Location} className='locationIcon'/>
  504. </View>
  505. <View className='punchRange' onClick={()=>{
  506. this.mapCtx.moveToLocation({
  507. latitude: this.state.addressLatitude,
  508. longitude: this.state.addressLongitude,
  509. });
  510. }}>
  511. <Image src={PunchRange} className='punchRangeIcon'/>
  512. </View>
  513. </View>
  514. <Map
  515. id='map'
  516. {...this.state.newArrder}
  517. circles={[
  518. {
  519. latitude: this.state.addressLatitude,
  520. longitude: this.state.addressLongitude,
  521. color: '#c92ddc82',
  522. fillColor: '#c92ddc82',
  523. radius: this.wxConfig.clockInRange,
  524. strokeWidth: 1
  525. }
  526. ]}
  527. showLocation
  528. />
  529. </View>
  530. <View className='tips'>紫色区域为可打卡区域</View>
  531. </View>
  532. <View className='content' onClick={()=>{
  533. this.setState({
  534. historicalIsOpened:true
  535. })
  536. }}>
  537. <View className='history'>
  538. <View>查看公出申请日志</View>
  539. <AtIcon value='chevron-right' size='20' color='#767272'/>
  540. </View>
  541. </View>
  542. </View>
  543. }
  544. <AtModal
  545. isOpened={this.state.isOpened}
  546. title='提醒'
  547. cancelText='取消'
  548. confirmText='确认'
  549. onClose={()=>{
  550. this.setState({
  551. isOpened:false,
  552. })
  553. }}
  554. onCancel={()=>{
  555. this.setState({
  556. isOpened:false,
  557. })
  558. }}
  559. onConfirm={()=>{
  560. this.setState({
  561. isOpened:false,
  562. },()=>{
  563. Taro.openSetting({
  564. success:(value)=> {
  565. if(value.authSetting['scope.userLocation']){
  566. Taro.getLocation({
  567. type: 'gcj02',
  568. isHighAccuracy:true,
  569. success: (res)=> {
  570. const latitude = res.latitude
  571. const longitude = res.longitude
  572. this.setState({
  573. newArrder:{
  574. latitude,
  575. longitude
  576. },
  577. })
  578. }
  579. })
  580. this.getDtails();
  581. }
  582. }
  583. })
  584. })
  585. }}
  586. content='您拒绝了定位权限,请先前往设置打开定位权限'
  587. />
  588. {this.state.historicalIsOpened ? <HistoricalClock
  589. id={dtails.id}
  590. isOpened={this.state.historicalIsOpened}
  591. onClose={()=>{
  592. this.setState({
  593. historicalIsOpened:false
  594. })
  595. }}
  596. /> : null}
  597. </View>
  598. )
  599. }
  600. }
  601. export default PunchClocks