punchClocks.jsx 19 KB

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