punchClocks.jsx 19 KB

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