Browse Source

feature(打卡):首页新增打卡功能

wanglj 2 năm trước cách đây
mục cha
commit
64e9adc901
7 tập tin đã thay đổi với 439 bổ sung50 xóa
  1. 8 0
      api/system/index.js
  2. 53 42
      manifest.json
  3. 9 1
      pages.json
  4. 327 0
      pages/home/checkIn.vue
  5. 26 3
      pages/home/index.vue
  6. 8 2
      pages/my/index.vue
  7. 8 2
      store/index.js

+ 8 - 0
api/system/index.js

@@ -18,4 +18,12 @@ export default {
   getHomeReport(query) {
     return micro_request.postRequest(basePath, 'Home', 'GetHomeConfig', query)
   },
+  // 打卡
+  checkIn(query) {
+    return micro_request.postRequest(basePath, 'PunchRecords', 'Create', query)
+  },
+  // 打卡列表
+  getCheckList(query) {
+    return micro_request.postRequest(basePath, 'PunchRecords', 'GetList', query)
+  },
 }

+ 53 - 42
manifest.json

@@ -1,28 +1,30 @@
 {
-    "name": "opms",
-    "appid": "__UNI__0597092",
-    "description": "",
-    "versionName": "1.0.0",
-    "versionCode": "100",
-    "transformPx": false,
+    "name" : "opms",
+    "appid" : "__UNI__0597092",
+    "description" : "",
+    "versionName" : "1.0.0",
+    "versionCode" : "100",
+    "transformPx" : false,
     /* 5+App特有相关 */
-    "app-plus": {
-        "usingComponents": true,
-        "nvueStyleCompiler": "uni-app",
-        "compilerVersion": 3,
-        "splashscreen": {
-            "alwaysShowBeforeRender": true,
-            "waiting": true,
-            "autoclose": true,
-            "delay": 0
+    "app-plus" : {
+        "usingComponents" : true,
+        "nvueStyleCompiler" : "uni-app",
+        "compilerVersion" : 3,
+        "splashscreen" : {
+            "alwaysShowBeforeRender" : true,
+            "waiting" : true,
+            "autoclose" : true,
+            "delay" : 0
         },
         /* 模块配置 */
-        "modules": {},
+        "modules" : {
+            "Geolocation" : {}
+        },
         /* 应用发布信息 */
-        "distribute": {
+        "distribute" : {
             /* android打包配置 */
-            "android": {
-                "permissions": [
+            "android" : {
+                "permissions" : [
                     "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
                     "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
                     "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
@@ -41,37 +43,46 @@
                 ]
             },
             /* ios打包配置 */
-            "ios": {},
+            "ios" : {},
             /* SDK配置 */
-            "sdkConfigs": {}
+            "sdkConfigs" : {
+                "geolocation" : {},
+                "maps" : {}
+            }
         }
     },
     /* 快应用特有相关 */
-    "quickapp": {},
+    "quickapp" : {},
     /* 小程序特有相关 */
-    "mp-weixin": {
-        "appid": "wxa8d08d308b768a5e",
-        "setting": {
-            "urlCheck": false,
-            "es6": true,
-            "postcss": true,
-            "minified": true,
-            "minifyWXML": true
+    "mp-weixin" : {
+        "appid" : "wxa8d08d308b768a5e",
+        "setting" : {
+            "urlCheck" : false,
+            "es6" : true,
+            "postcss" : true,
+            "minified" : true,
+            "minifyWXML" : true
+        },
+        "permission" : {
+            "scope.userLocation" : {
+                "desc" : "你的位置信息将用于小程序位置接口的效果展示" // 高速公路行驶持续后台定位
+            }
         },
-        "usingComponents": true,
-        "lazyCodeLoading": "requiredComponents"
+        "requiredPrivateInfos" : [ "getLocation" ],
+        "usingComponents" : true,
+        "lazyCodeLoading" : "requiredComponents"
     },
-    "mp-alipay": {
-        "usingComponents": true
+    "mp-alipay" : {
+        "usingComponents" : true
     },
-    "mp-baidu": {
-        "usingComponents": true
+    "mp-baidu" : {
+        "usingComponents" : true
     },
-    "mp-toutiao": {
-        "usingComponents": true
+    "mp-toutiao" : {
+        "usingComponents" : true
     },
-    "uniStatistics": {
-        "enable": false
+    "uniStatistics" : {
+        "enable" : false
     },
-    "vueVersion": "2"
-}
+    "vueVersion" : "2"
+}

+ 9 - 1
pages.json

@@ -16,6 +16,14 @@
             },
             "tit": "首页"
         },
+        {
+            "path" : "pages/home/checkIn",
+            "style" :
+            {
+                 "navigationStyle": "custom"
+            },
+            "tit": "打卡"
+        },
         {
             "path": "pages/contract/index",
             "style": {
@@ -188,4 +196,4 @@
         "backgroundColor": "#F8F8F8"
     },
     "uniIdRouter": {}
-}
+}

+ 327 - 0
pages/home/checkIn.vue

@@ -0,0 +1,327 @@
+<template>
+  <view class="home">
+    <view class="nav">
+      <view :style="{ paddingTop }">
+        <view class="title" :style="[{ height }, { lineHeight: height }]">
+          <view class="back" @click="goBack()">
+            <u-icon name="arrow-left" color="#ffffff" size="22"></u-icon>
+          </view>
+          <text>打卡</text>
+        </view>
+      </view>
+    </view>
+    <view class="main">
+      <view class="main-header">
+        <view class="user-info">
+          <image class="user-img" src="@/static/images/user.jpg" mode="scaleToFill" />
+          <view class="info flex1 flex-column flex_1">
+            <u-text :lines="1" size="32rpx" :bold="true" :text="nickName"></u-text>
+            <u-text :lines="1" size="24rpx" :text="postName"></u-text>
+          </view>
+        </view>
+        <view class="more" @click="showCheckList">
+          <u-icon name="order" size="60rpx"></u-icon>
+          <u-text :lines="1" size="24rpx" text="打卡记录"></u-text>
+        </view>
+      </view>
+      <view class="main-container">
+        <template v-if="!showList">
+          <view class="btn" @click="checkIn">打卡</view>
+          <view class="location">
+            <u--text @click="getLocation" :lines="1" :block="true" align="center" size="24rpx" :text="address">
+            </u--text>
+            <u-loading-icon mode="circle" size="24rpx" :show="show"></u-loading-icon>
+          </view>
+        </template>
+        <u-list @scrolltolower="scrolltolower" v-else>
+          <u-list-item v-for="(item, index) in checkList" :key="index">
+            <u-cell :title="item.punchTime" :label="item.punchLocation">
+            </u-cell>
+          </u-list-item>
+        </u-list>
+      </view>
+    </view>
+    <u-toast ref="uToast"></u-toast>
+    <!-- <uni-file-picker file-mediatype="all" v-model="imageValue" @select="select" @progress="progress" @success="success"
+      @fail="fail" ref="upload" limit="1">
+      <view class="upload-btn">
+        <u-icon name="plus-circle-fill" color="blue" size="28"></u-icon>
+      </view>
+    </uni-file-picker> -->
+  </view>
+</template>
+
+<script>
+  import {
+    mapGetters
+  } from 'vuex'
+  import to from 'await-to-js'
+  import api from '@/api/system'
+  export default {
+    data() {
+      return {
+        height: '',
+        paddingTop: '',
+        address: '获取定位',
+        form: {
+          punchLat: 0,
+          punchLng: 0,
+          punchLocation: '',
+          punchImg: ''
+        },
+        show: false,
+        imageValue: [],
+        showList: false,
+        checkList: [],
+        queryForm: {
+          pageNum: 1,
+          pageSize: 10
+        }
+      }
+    },
+    computed: {
+      ...mapGetters(['nickName', 'postName']),
+    },
+    created() {
+      this.getLocation()
+      const navData = uni.getMenuButtonBoundingClientRect()
+      this.height = navData.height + 'px'
+      this.paddingTop = navData.top + 'px'
+    },
+    methods: {
+      getLocation() {
+        this.show = true
+        const _this = this
+        uni.getLocation({
+          type: 'wgs84',
+          success: function(res) {
+            const latitude = res.latitude.toString();
+            const longitude = res.longitude.toString();
+            uni.request({
+              header: {
+                "Content-Type": "application/text"
+              },
+              url: 'http://apis.map.qq.com/ws/geocoder/v1/?location=' + latitude + ',' + longitude +
+                '&key=6REBZ-BRN6J-BMAFY-FIRI3-SMK43-G3FXU',
+              success(re) {
+                if (re.statusCode === 200) {
+                  _this.address = re.data.result.address;
+                  _this.form = {
+                    punchLat: latitude,
+                    punchLng: longitude,
+                    punchLocation: re.data.result.address
+                  }
+                  _this.show = false
+                } else {
+                  _this.address = '点击重试'
+                  this.$refs.uToast.show({
+                    type: 'error',
+                    message: '定位失败,请重试',
+                  })
+                }
+              },
+              fail: err => _this.show = false
+            });
+          },
+          fail: err => _this.show = false
+        });
+      },
+      async checkIn() {
+        if (!this.form.punchLat || !this.form.punchLng) {
+          this.address = '点击重试'
+          return this.$refs.uToast.show({
+            type: 'error',
+            message: '定位失败,请重试',
+          })
+        }
+        console.log(this.$refs.upload);
+        // this.$refs.upload.choose()
+        const [err, res] = await uni.chooseImage({
+          count: 1, //默认9
+          sizeType: ['original'] //可以指定是原图还是压缩图,默认二者都有
+        })
+        console.log(res, 'rs');
+        if (err) return
+        this.select(res)
+        res.tempFiles[0].name = (new Date()).getTime() + ''
+        res.tempFiles[0].type = 'image'
+        console.log(res.tempFiles[0], typeof res.tempFiles[0])
+      },
+      // // 选择上传触发函数
+      select(e) {
+        console.log(e)
+        let url = process.uniEnv.VUE_APP_UPLOAD_WEED
+        uni.request({
+          url, //仅为示例,并非真实接口地址。
+          success: (res) => {
+            this.uploadFiles(e, res.data)
+          },
+        })
+      },
+      // 上传函数
+      async uploadFiles(e, res) {
+        let action = 'http://' + res.url + '/' + res.fid
+        uni.uploadFile({
+          url: action,
+          filePath: e.tempFilePaths[0],
+          name: 'file',
+          formData: {
+            file: e.tempFiles[0],
+          },
+          success: (res) => {
+            this.form.punchImg = action
+            api.checkIn(this.form).then(res => {
+              this.$refs.uToast.show({
+                type: 'success',
+                message: '打卡成功',
+              })
+            })
+          },
+          fail: () => {
+            console.log('err')
+          },
+        })
+      },
+      showCheckList() {
+        if (!this.showList) this.loadCheckList()
+        this.showList = !this.showList
+      },
+      async loadCheckList() {
+        this.loadStatus = 'loading'
+        const [err, res] = await to(api.getCheckList(this.queryForm))
+        if (err) {
+          this.loadStatus = 'nomore'
+          return
+        }
+        if (res && res.code == 200) {
+          this.checkList = [...this.checkList, ...(res.data.list || [])]
+          this.total = res.data.total
+          this.loadStatus = this.checkList.length == this.total ? 'nomore' : 'loadmore'
+        } else {
+          this.loadStatus = 'nomore'
+        }
+
+      },
+      scrolltolower() {
+        if (this.list.length < this.total && this.loadStatus != 'loading') {
+          this.queryForm.pageNum++
+          this.$u.throttle(this.loadCheckList(), 2000, false)
+        }
+        this.loadCheckList()
+      },
+      goBack() {
+        uni.navigateBack({
+          //关闭当前页面,返回上一页面或多级页面。
+          delta: 1,
+        })
+      },
+    }
+  }
+</script>
+<style lang="scss" scoped>
+  /deep/ .uni-file-picker {
+    display: none;
+  }
+
+  .home {
+    padding-top: 188rpx;
+    height: 100%;
+
+    .nav {
+      position: absolute;
+      left: 0;
+      top: 0;
+      width: 100%;
+      height: 284rpx;
+      background: #3e7ef8;
+
+      .title {
+        position: relative;
+        text-align: center;
+        font-size: 32rpx;
+        font-weight: bold;
+        color: #ffffff;
+
+        .back {
+          position: absolute;
+          top: 0;
+          bottom: 0;
+          margin: auto;
+          left: 70rpx;
+          display: flex;
+        }
+      }
+    }
+
+    .main {
+      display: flex;
+      flex-direction: column;
+      position: absolute;
+      width: 100%;
+      height: calc(100vh - 188rpx);
+      background: #ffffff;
+      box-shadow: 0 6rpx 19rpx 2rpx rgba(0, 45, 132, 0.15);
+      border-radius: 31rpx 31rpx 0 0;
+      padding: 0 32rpx;
+      overflow: hidden;
+      padding-bottom: 64rpx;
+
+      .main-header {
+        padding: 20rpx 40rpx;
+        display: flex;
+        justify-content: space-between;
+
+        .user-info {
+          display: flex;
+
+          .user-img {
+            width: 116rpx;
+            height: 116rpx;
+            border-radius: 50%;
+            margin-right: 32rpx;
+          }
+
+          .info {
+            height: 116rpx;
+            margin-right: 32rpx;
+          }
+        }
+
+        .more {
+          display: flex;
+          flex-direction: column;
+          align-items: center;
+        }
+      }
+
+      .main-container {
+        flex: 1;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+
+        .btn {
+          width: 300rpx;
+          height: 300rpx;
+          border-radius: 50%;
+          background: linear-gradient(70deg, #3e7ef8, #1d66dc);
+          display: flex;
+          justify-content: center;
+          align-items: center;
+          color: #fff;
+          font-size: 48rpx;
+          font-weight: bold;
+          box-shadow: 0 6rpx 19rpx 2rpx rgba(29, 102, 220, 0.2);
+          margin-bottom: 40rpx;
+        }
+
+        .location {
+          display: flex;
+          height: 32rpx;
+          line-height: 32rpx;
+        }
+      }
+    }
+  }
+</style>

+ 26 - 3
pages/home/index.vue

@@ -181,6 +181,9 @@
         </view>
       </view> -->
     </view>
+    <view class="check" @click="toCheckIn">
+      卡
+    </view>
   </view>
 </template>
 
@@ -286,11 +289,16 @@
             },
           ],
         }
-        console.log(this.$refs[id][0], 'id')
         this.$refs[id][0].init(echarts, chart => {
           chart.setOption(option);
         });
       },
+      toCheckIn() {
+        uni.navigateTo({
+          //保留当前页面,跳转到应用内的某个页面
+          url: '/pages/home/checkIn',
+        })
+      }
     },
   }
 </script>
@@ -302,7 +310,20 @@
 <style lang="scss" scoped>
   .home {
     padding-top: 210rpx;
-
+    position: relative;
+    .check {
+      position: fixed;
+      right: 20rpx;
+      bottom: 20rpx;
+      width: 90rpx;
+      height: 90rpx;
+      line-height: 90rpx;
+      text-align: center;
+      border-radius: 45rpx;
+      background-color: #3e7ef8;
+      color: #fff;
+      box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+    }
     .nav {
       position: absolute;
       left: 0;
@@ -527,9 +548,11 @@
           height: 480rpx;
           box-shadow: 0 6rpx 19rpx 2rpx rgba(0, 45, 132, 0.15);
           border-radius: 31rpx 31rpx 31rpx 31rpx;
-          + .echarts {
+
+          +.echarts {
             margin-top: 32rpx;
           }
+
           .chart {
             height: 400rpx;
           }

+ 8 - 2
pages/my/index.vue

@@ -19,8 +19,8 @@
         <view class="user-info flex flex-middle">
           <image class="user-img" src="@/static/images/user.jpg" mode="scaleToFill" />
           <view class="info flex1 flex-column flex_1">
-            <u-text :lines="1" size="32rpx" color="#fff" :bold="true" text="销售工程师"></u-text>
-            <u-text :lines="1" size="24rpx" color="#fff" text="西南地区销售工程师"></u-text>
+            <u-text :lines="1" size="32rpx" color="#fff" :bold="true" :text="nickName"></u-text>
+            <u-text :lines="1" size="24rpx" color="#fff" :text="postName"></u-text>
           </view>
           <u-icon name="arrow-right" color="#ffffff" size="18"></u-icon>
         </view>
@@ -67,6 +67,9 @@
   </view>
 </template>
 <script>
+  import {
+    mapGetters
+  } from 'vuex'
   export default {
     name: 'opsIndex',
     data() {
@@ -75,6 +78,9 @@
         paddingTop: '',
       }
     },
+    computed: {
+      ...mapGetters(['nickName', 'postName']),
+    },
     created() {
       const navData = uni.getMenuButtonBoundingClientRect()
       console.log(navData)

+ 8 - 2
store/index.js

@@ -17,6 +17,7 @@ const store = new Vuex.Store({
         userId: '',
         username: '',
         nickName: '',
+        postName:'',
         avatar: '',
         followPageDetail: {}, //跟进页面的客户/项目信息
     },
@@ -26,6 +27,7 @@ const store = new Vuex.Store({
         token: (state) => state.token,
         username: (state) => state.username,
         nickName: (state) => state.nickName,
+        postName:(state) => state.postName,
         avatar: (state) => state.avatar,
     },
     //mutations定义同步操作的方法
@@ -71,6 +73,9 @@ const store = new Vuex.Store({
         setNickName(state, nickName) {
             state.nickName = nickName
         },
+        setPostName(state, postName) {
+            state.postName = postName
+        },
         /**
          * @description 设置用头像
          * @param {*} state
@@ -109,10 +114,11 @@ const store = new Vuex.Store({
         async getUserInfo({ commit }) {
             const [err, res] = await to(userApi.getUserInfo())
             if (err) return
-            const { id, userName, nickName, avatar } = res.data.entity
+            const { id, userName, nickName, avatar, postName } = res.data.entity
             if (id) commit('setUserId', id)
             if (userName) commit('setUsername', userName)
             if (nickName) commit('setNickName', nickName)
+            if (postName) commit('setPostName', nickName)
             if (avatar) commit('setAvatar', avatar)
         },
         /**
@@ -135,4 +141,4 @@ const store = new Vuex.Store({
         },
     },
 })
-export default store
+export default store