punch-out.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. <template>
  2. <view class="">
  3. <!-- 休息日 -->
  4. <view class="" style="padding-top: 300rpx;" v-if="status==0">
  5. <u-empty icon-size="300" text="今天不上班,要好好休息哟~" src="/punch/static/icon/rest.png">
  6. <view style="text-decoration: underline;" class="margin-top-30 base-color flex" slot="bottom">
  7. <navigator url="/punch/pages/punch-list/punch-list" hover-class="none">打卡记录</navigator>
  8. <navigator url="/punch/pages/rule/rule" hover-class="none" style="margin-left: 30rpx;">
  9. 打卡规则</navigator>
  10. </view>
  11. </u-empty>
  12. </view>
  13. <!-- 工作日-未打卡 -->
  14. <view class="card" v-else-if="status==1">
  15. <view class="location" @click="chooseLocation">
  16. <map :enable-scroll="false" class="map" :enable-zoom="false"></map>
  17. <view class="label" style="position: relative;">
  18. <u-icon name="map-fill" size="80" color="#5064eb"></u-icon>
  19. <view class="text-bold text-cut-1 margin-top-10">{{location.address}}</view>
  20. <view v-if="$isEmpty(remark)" @click.stop="remarkShow=true" class="add-remark">添加备注...</view>
  21. <view @click.stop="remarkShow=true" class="remark-content" v-else>
  22. <view class="content text-cut-1">
  23. <text class="text-cut-1">{{remark}}</text>
  24. </view>
  25. </view>
  26. </view>
  27. </view>
  28. <view class="" style="position: fixed;bottom: 20%;left: 50%;margin-left: -130rpx;">
  29. <view @click="punch" class="btn-open">
  30. <text>{{time}}</text>
  31. <text >第{{records.length==0?'1':(records.length+1)}}次{{ruleOut.needPhone==1?'人脸':''}}打卡</text>
  32. </view>
  33. </view>
  34. <u-modal confirm-color="#5064eb" @confirm="remark=remark_tmp" :showCancelButton="true" title="备注"
  35. :mask-close-able="true" v-model="remarkShow">
  36. <view class="slot-content" style="margin:0 20rpx;">
  37. <u-form label-width="150" ref="uForm">
  38. <u-form-item :border-bottom="false">
  39. <u-input height="200" placeholder="请添加备注" v-model="remark_tmp" />
  40. </u-form-item>
  41. </u-form>
  42. </view>
  43. </u-modal>
  44. </view>
  45. <!-- 工作日-去打卡 -->
  46. <view @click="tooltipShow=false" class="form" v-else-if="status==2">
  47. <view class="close" @click="status=1">
  48. <u-icon name="close" color="#909399" size="60"></u-icon>
  49. </view>
  50. <view class="face" v-if="ruleOut.needPhone==1">
  51. <image :src="face" mode="scaleToFill"></image>
  52. <view>
  53. <text>人脸照片</text>
  54. </view>
  55. </view>
  56. <view class="face" v-else>
  57. <image style="width: 200rpx;height: 200rpx;" src="/punch/static/icon/punch.png" mode="widthFix"></image>
  58. <view>
  59. <text>外出打卡</text>
  60. </view>
  61. </view>
  62. <view class="desc">
  63. <view class="item">
  64. <text class="title">时间</text>
  65. <text>{{punchTime}}</text>
  66. </view>
  67. <view class="item" id="tips1" @click.stop="showTips('tips1')">
  68. <text class="title">位置</text>
  69. <view class="text-right" style="width: 80%;">
  70. <text class="text-cut-1" >{{location.address}}</text>
  71. </view>
  72. </view>
  73. <view class="item">
  74. <text class="title">备注</text>
  75. <text>{{remark || '无备注'}}</text>
  76. </view>
  77. </view>
  78. <view class="submit" @click.stop="submit">
  79. <view class="cu-btn base-bg-color round" style="width: 100%;padding: 40rpx;">
  80. 确认打卡
  81. </view>
  82. </view>
  83. </view>
  84. <view class="result" v-else-if="status==3">
  85. <image src="../../static/icon/success.png" mode=""></image>
  86. <text class="tips">外出打卡成功</text>
  87. <view class="desc">
  88. <view class="item" style="padding-top: 40rpx;">
  89. <text class="title">时间</text>
  90. <text>{{punchTime}}</text>
  91. </view>
  92. <view class="item" >
  93. <text class="title">位置</text>
  94. <view class="text-right" style="width: 80%;">
  95. <text class="text-cut-1" >{{location.address}}</text>
  96. </view>
  97. </view>
  98. <view class="item">
  99. <text class="title">备注</text>
  100. <text>{{remark || '无备注'}}</text>
  101. </view>
  102. </view>
  103. </view>
  104. <!-- 上传人脸照片 -->
  105. <u-action-sheet @click="faceSelectCallback" z-index="999999" :list="faceSelectList" v-model="faceSelectShow"></u-action-sheet>
  106. <tooltips
  107. gravity="bottom"
  108. :tooltipShow="tooltipShow"
  109. :btns="tooltipBtns"
  110. :eleId="eleId"
  111. @btnClick="sortTooltipClick"></tooltips>
  112. </view>
  113. </template>
  114. <script>
  115. import tooltips from '@/components/tooltips/tooltips.vue'
  116. export default {
  117. components:{
  118. tooltips
  119. },
  120. data() {
  121. return {
  122. //提示
  123. tooltipShow:false,
  124. tooltipBtns:[''],
  125. eleId:"",
  126. //外出打卡规则
  127. ruleOut: {},
  128. //当天外出打卡记录
  129. records: [],
  130. //备注
  131. remark_tmp: '',
  132. remark: '',
  133. remarkShow: false,
  134. //打卡状态 0休息日,1工作日-未打卡 2工作日-去打卡 3工作日-打卡成功
  135. status: 0,
  136. //系统时间
  137. time: '',
  138. punchTime:'',
  139. //位置
  140. location: {},
  141. //人脸照片
  142. face:'',//回显人脸照片
  143. submitFace:'',//确认提交的人脸照片
  144. faceSelectList: [{
  145. text: '相册上传',
  146. }, {
  147. text: '拍照上传'
  148. }],
  149. faceSelectShow: false,
  150. }
  151. },
  152. props: {
  153. faceImg: String,
  154. },
  155. watch:{
  156. vuex_location(){
  157. this.location=this.vuex_location
  158. },
  159. faceImg(){
  160. this.face=this.faceImg
  161. this.punchTime = this.$dateTime.format()
  162. this.status=2
  163. },
  164. },
  165. created() {
  166. //休息日
  167. if (!this.vuex_punch_rules.isWork) {
  168. this.status = 0
  169. return
  170. }
  171. //工作日,外出打卡规则是当天内可以无限打卡
  172. //所以只要是工作日内,重新进入这个页面都是 工作日-未打卡【1】状态
  173. this.status = 1
  174. this.ruleOut = this.vuex_punch_rules ? this.vuex_punch_rules.ruleOut : {},
  175. this.getNowTime()
  176. this.getRecordByDay()
  177. },
  178. methods: {
  179. async submit(){
  180. let res=await this.$api.uploadFile.submit(this.face)
  181. if (!res.data) {
  182. this.$u.toast('人脸上传失败!')
  183. return
  184. }
  185. let params={
  186. ownerId:this.vuex_userInfo.enterpriseId,
  187. personId: this.vuex_userInfo.id,
  188. personName: this.vuex_userInfo.realName,
  189. personType: 0,
  190. deviceId: this.$u.sys().deviceId,
  191. longitude: this.location.longitude,
  192. latitude: this.location.latitude,
  193. locationName: this.location.address,
  194. checkinTime: this.punchTime,
  195. checkinType: 2,//外出打卡
  196. ruleId: this.vuex_punch_rules.id,
  197. note:this.remark,
  198. faceImg:res.data.link
  199. }
  200. this.$api.punch.save(params).then(res => {
  201. if (res.success == true) {
  202. this.status=3
  203. } else {
  204. this.$dialog.showModal(res.msg, false)
  205. }
  206. })
  207. },
  208. async getRecordByDay() {
  209. let params = {
  210. personId: this.vuex_userInfo.id,
  211. personType: 0
  212. }
  213. let res = await this.$api.punch.getRecordsByDate(params)
  214. if (res.data) {
  215. this.records = res.data.filter(item => {
  216. return item.checkinType == 2
  217. })
  218. }
  219. },
  220. getNowTime() {
  221. let that = this
  222. setInterval(() => {
  223. that.time = that.$dateTime.format(new Date(), 'MM:SS')
  224. }, 1000)
  225. },
  226. chooseLocation() {
  227. let that = this
  228. uni.chooseLocation({
  229. success: function(res) {
  230. //选择的位置与定位的距离不能超过五百米
  231. let distance = that.$util.calculateDistance(res.latitude, res.longitude,
  232. that.vuex_location.latitude, that.vuex_location.longitude)
  233. if (distance > 500) {
  234. that.$dialog.showModal('只可选500米范围内的地点', false)
  235. return
  236. }
  237. if (res.name) {
  238. that.location.address=res.name
  239. that.location.latitude=res.latitude
  240. that.location.longitude=res.longitude
  241. }
  242. }
  243. });
  244. },
  245. //打卡
  246. punch() {
  247. if (this.ruleOut.needPhone == 1) {
  248. //需要拍照打卡
  249. if (this.ruleOut.noteCanUseLocalPic == 1) {
  250. //可以上传本地照片
  251. this.faceSelectShow = true
  252. } else {
  253. uni.navigateTo({
  254. url: "/pages/my-camera/my-camera"
  255. })
  256. }
  257. return
  258. }
  259. //不需要拍照打卡
  260. this.punchTime = this.$dateTime.format()
  261. this.status=2
  262. },
  263. faceSelectCallback(index) {
  264. if (index == 0) {
  265. //图片上传
  266. //允许上传本地图片
  267. this.$mpi.chooseImage().then(res => {
  268. this.face = res[0]
  269. this.status = 2
  270. })
  271. } else if (index == 1) {
  272. //拍照上传
  273. uni.navigateTo({
  274. url: '/pages/my-camera/my-camera'
  275. })
  276. }
  277. },
  278. showTips(id){
  279. this.tooltipBtns=[this.location.address]
  280. this.tooltipShow = true;
  281. this.eleId =id;
  282. },
  283. sortTooltipClick(e){
  284. console.log(e);
  285. }
  286. }
  287. }
  288. </script>
  289. <style lang="scss">
  290. page {
  291. background-color: #FFFFFF;
  292. }
  293. .card {
  294. display: flex;
  295. flex-direction: column;
  296. align-items: center;
  297. height: 100%;
  298. width: 100%;
  299. .location {
  300. font-size: 38rpx;
  301. color: #0d6d3a;
  302. text-align: center;
  303. .map {
  304. width: 100vw;
  305. height: 500rpx;
  306. opacity: 0.2;
  307. }
  308. .label {
  309. margin-top: -300rpx;
  310. image {
  311. width: 80rpx;
  312. height: 80rpx;
  313. }
  314. .add-remark {
  315. font-weight: 800;
  316. padding-top: 60rpx;
  317. color: #4a70c8;
  318. font-size: 28rpx;
  319. }
  320. .remark-content {
  321. display: flex;
  322. justify-content: space-between;
  323. background-color: #FFFFFF;
  324. color: #666666;
  325. width: 90%;
  326. margin: 0 auto;
  327. margin-top: 60rpx;
  328. padding: 25rpx 15rpx;
  329. font-size: 30rpx;
  330. border: 1rpx solid #e7e7e7;
  331. border-radius: 4rpx;
  332. .content {
  333. width: 100%;
  334. display: flex;
  335. justify-content: flex-start;
  336. align-items: center;
  337. }
  338. }
  339. }
  340. }
  341. .btn-open {
  342. border: 18rpx solid $base-color;
  343. text-align: center;
  344. display: flex;
  345. flex-direction: column;
  346. justify-content: center;
  347. align-items: center;
  348. border-radius: 50%;
  349. height: 260rpx;
  350. width: 260rpx;
  351. text:first-child {
  352. color: #000000;
  353. font-size: 56rpx;
  354. }
  355. text:last-child {
  356. padding-top: 10rpx;
  357. font-size: 26rpx;
  358. }
  359. }
  360. }
  361. .empty {
  362. width: 100%;
  363. padding-top: 300rpx;
  364. background-color: #FFFFFF;
  365. }
  366. .form {
  367. position: relative;
  368. width: 100%;
  369. height: 75vh;
  370. display: flex;
  371. flex-direction: column;
  372. justify-content: center;
  373. align-items: center;
  374. padding-bottom: 80rpx;
  375. .close {
  376. position: absolute;
  377. top: 50rpx;
  378. right: 50rpx;
  379. }
  380. .face {
  381. image {
  382. border-radius: 10rpx;
  383. height: 260rpx;
  384. width: 240rpx;
  385. }
  386. view {
  387. padding-top: 10rpx;
  388. text-align: center;
  389. color: $base-color;
  390. }
  391. }
  392. .submit {
  393. position: absolute;
  394. bottom: 0;
  395. left: 10%;
  396. right: 10%;
  397. margin: auto;
  398. }
  399. }
  400. .result {
  401. display: flex;
  402. flex-direction: column;
  403. align-items: center;
  404. padding-top: 180rpx;
  405. image {
  406. width: 180rpx;
  407. height: 180rpx;
  408. }
  409. .tips {
  410. padding-top: 50rpx;
  411. font-size: 36rpx;
  412. color: $base-color;
  413. font-weight: 800;
  414. }
  415. }
  416. .desc {
  417. border-top: 1rpx solid $dt-border-color-sm;
  418. margin-top: 100rpx;
  419. padding: 10rpx;
  420. width: 70%;
  421. .item {
  422. display: flex;
  423. height: 80rpx;
  424. align-items: center;
  425. justify-content: space-between;
  426. .title {
  427. text-align: left;
  428. color: #8c8d8d;
  429. }
  430. }
  431. }
  432. </style>