zhaozp 3 years ago
parent
commit
dc735440f5

+ 1 - 0
.eslintrc.js

@@ -13,6 +13,7 @@ module.exports = {
     // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
     'plugin:vue/essential',
     // https://github.com/standard/standard/blob/master/docs/RULES-en.md
+     // "eslint:recommended", 
     'standard'
   ],
   // required to lint *.vue files

+ 0 - 12
.history/index_20210707101123.html

@@ -1,12 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <head>
-    <meta charset="utf-8">
-    <meta name="viewport" content="width=device-width,initial-scale=1.0">
-    <title>至本销售</title>
-  </head>
-  <body>
-    <div id="app"></div>
-    <!-- built files will be auto injected -->
-  </body>
-</html>

+ 0 - 12
.history/index_20210707104424.html

@@ -1,12 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <head>
-    <meta charset="utf-8">
-    <meta name="viewport" content="width=device-width,initial-scale=1.0">
-    <title>考勤管理</title>
-  </head>
-  <body>
-    <div id="app"></div>
-    <!-- built files will be auto injected -->
-  </body>
-</html>

+ 5 - 11
build/webpack.base.conf.js

@@ -9,16 +9,6 @@ function resolve (dir) {
   return path.join(__dirname, '..', dir)
 }
 
-const createLintingRule = () => ({
-  test: /\.(js|vue)$/,
-  loader: 'eslint-loader',
-  enforce: 'pre',
-  include: [resolve('src'), resolve('test')],
-  options: {
-    formatter: require('eslint-friendly-formatter'),
-    emitWarning: !config.dev.showEslintErrorsInOverlay
-  }
-})
 
 module.exports = {
   context: path.resolve(__dirname, '../'),
@@ -41,7 +31,6 @@ module.exports = {
   },
   module: {
     rules: [
-      ...(config.dev.useEslint ? [createLintingRule()] : []),
       {
         test: /\.vue$/,
         loader: 'vue-loader',
@@ -94,4 +83,9 @@ module.exports = {
     tls: 'empty',
     child_process: 'empty'
   }
+  // externals: {
+  //   'AMap': 'AMap',
+  //   // 'Loca': 'Loca',    //如果引入了Loca,再注册全局
+  //   // 'AMapUI': 'AMapUI'   //如果引入了AMapUI,再注册全局
+  // }
 }

+ 1 - 1
config/index.js

@@ -33,7 +33,7 @@ module.exports = {
     // Use Eslint Loader?
     // If true, your code will be linted during bundling and
     // linting errors and warnings will be shown in the console.
-    useEslint: true,
+    useEslint: false,
     // If true, eslint errors and warnings will also be shown in the error overlay
     // in the browser.
     showEslintErrorsInOverlay: false,

+ 2 - 1
index.html

@@ -2,11 +2,12 @@
 <html>
   <head>
     <meta charset="utf-8">
-    <meta name="viewport" content="width=device-width,initial-scale=1.0">
+    <meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no">
     <title>考勤管理</title>
   </head>
   <body>
     <div id="app"></div>
     <!-- built files will be auto injected -->
   </body>
+  <script src="https://webapi.amap.com/maps?v=1.4.15&key=b2454068b8ea909e448a8339c4fac1cc"></script>
 </html>

+ 35 - 12
src/api/index.js

@@ -1,13 +1,36 @@
 // 组织机构
-import * as organize from './organize/organize'
-// 字典
-import * as dictionary from './dictionary/dictionary'
-// 文件合并
-const merge = require('webpack-merge')
-
-const result = merge(
-  organize,
-  dictionary
-)
-
-export default result
+// import * as organize from './organize/organize'
+// // 字典
+// import * as dictionary from './dictionary/dictionary'
+// // 文件合并
+// const merge = require('webpack-merge')
+
+// const result = merge(
+//   organize,
+//   dictionary
+// )
+
+// export default result
+import request from '@/utils/request'
+
+// 获取当前按钮状态
+export const getButtonState = () => { return request({ url: "/attendance/api/manage/getButtonState" }) }
+
+// 获取当天打卡信息
+export const getTodaySignRecord = () => { return request({ url: "/attendance/api/manage/getTodaySignRecord" }) }
+
+// 打卡
+export const signRecord = () => { return request({ url: "/attendance/api/manage/signRecord" }) }
+
+// 统计顶部数量
+export const statisticsTop = (data) => { return request({ url: "/attendance/api/manage/statisticsTop/"+data }) }
+
+// 统计
+export const statistics = (data) => { return request({ url: "/attendance/api/manage/statistics/"+data}) }
+
+// 获取用户当天上下班时间
+export const getUserUpDownWorkTime = () => { return request({ url: "/attendance/api/manage/getUserUpDownWorkTime" }) }
+
+// 上传用户定位信息
+export const uploadUserPosition = () => { return request({ url: "/attendance/api/manage/uploadUserPosition" }) }
+

BIN
src/assets/empty.png


BIN
src/assets/head.png


+ 23 - 0
src/assets/icon/iconfont.css

@@ -0,0 +1,23 @@
+@font-face {
+  font-family: "iconfont"; /* Project id 2676961 */
+  src: url('iconfont.woff2?t=1626335014891') format('woff2'),
+       url('iconfont.woff?t=1626335014891') format('woff'),
+       url('iconfont.ttf?t=1626335014891') format('truetype');
+}
+
+.iconfont {
+  font-family: "iconfont" !important;
+  font-size: 16px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+.icon-tongji-copy:before {
+  content: "\e608";
+}
+
+.icon-daqia-copy:before {
+  content: "\e609";
+}
+

BIN
src/assets/icon/iconfont.ttf


BIN
src/assets/icon/iconfont.woff


BIN
src/assets/icon/iconfont.woff2


BIN
src/assets/rest.png


+ 86 - 0
src/components/headInfo.vue

@@ -0,0 +1,86 @@
+<template>
+  <van-row class="head-style" type="flex" align="center">
+    <div class="head-style-first">
+      <van-image round width="4rem" height="4rem" :src="head" />
+    </div>
+    <div class="head-style-last">
+      <div class="head-style-last-one">您好,张三</div>
+      <div class="head-style-last-two">区域-项目-部门</div>
+    </div>
+    <div class="head-style-time" v-if="nowYearMonth">
+      <van-icon name="arrow-left" tag="i" @click="lastMonth" />
+      <i>{{ getYearMonth }}</i>
+      <van-icon
+        name="arrow"
+        :color="compareDate ? '#ccc' : 'inherit'"
+        tag="i"
+        @click="nextMonth"
+      />
+    </div>
+  </van-row>
+</template>
+
+<script>
+import head from "@/assets/head.png";
+import { getYyMmDdTimeStamp } from "@/utils/date";
+export default {
+  props: ["nowYearMonth"],
+  data() {
+    return {
+      head,
+      value: new Date(),
+      // 按钮是否可点击
+      ifButtonClick: true,
+    };
+  },
+  computed: {
+    getYearMonth() {
+      return getYyMmDdTimeStamp(this.nowYearMonth);
+    },
+    compareDate() {
+      if (
+        getYyMmDdTimeStamp(this.nowYearMonth) === getYyMmDdTimeStamp(this.value)
+      ) {
+        this.ifButtonClick = false;
+        return true;
+      }
+      this.ifButtonClick = true;
+      return false;
+    },
+  },
+  methods: {
+    lastMonth() {
+      this.$emit("getLastMonth");
+    },
+    nextMonth() {
+      if (this.ifButtonClick) {
+        this.$emit("getNextMonth");
+      }
+    },
+  },
+};
+</script>
+
+<style scoped lang="scss">
+.head-style {
+  padding: 2vh 5vw;
+  .head-style-first {
+    margin-right: 10px;
+  }
+  .head-style-last-one {
+    font-size: 18px;
+    margin-bottom: 4px;
+  }
+  .head-style-last-two {
+    color: #aaa;
+  }
+  .head-style-time {
+    margin-left: auto;
+    display: flex;
+    align-items: center;
+    i {
+      margin-left: 5px;
+    }
+  }
+}
+</style>

+ 40 - 0
src/components/loading.vue

@@ -0,0 +1,40 @@
+<template>
+  <div class="loading" :class="loadBg ? 'bgPray' : 'bgWhite'">
+    <van-loading type="spinner" />
+  </div>
+</template>
+
+<script>
+  import Vue from 'vue';
+  import {Loading} from 'vant';
+  import { mapState } from 'vuex'
+  Vue.use(Loading);
+  export default {
+    name: 'LOADING',
+    data() {
+      return {}
+    },
+    computed: {
+      ...mapState(['loadBg'])
+    }
+  }
+</script>
+<style lang="scss" scoped>
+  .loading {
+    position: fixed;
+    z-index: 9999;
+    width: 100%;
+    height: calc(100% - 50px);
+    margin-top: 46px;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    
+  }
+  .bgWhite {
+    background: white;
+  }
+  .bgPray {
+    background: rgba(255, 255, 255, 1);
+  }
+</style>

+ 41 - 0
src/components/navBar.vue

@@ -0,0 +1,41 @@
+<template>
+   <van-nav-bar
+      :title="ifPunch ? '考勤打卡' : '统计'"
+      left-text="返回"
+      left-arrow
+      @click-left="onClickLeft"
+    />
+</template>
+
+<script>
+export default {
+  props: {
+    // 是否为打卡页
+    ifPunch: {
+      type: Boolean,
+      default: false
+    }
+  },
+  methods: {
+    onClickLeft () {
+      console.log('跳出')
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+$white: #fff;
+.van-nav-bar {
+  background: #67a2ff;
+}
+/deep/ .van-icon {
+  color: $white;
+}
+/deep/ .van-nav-bar__text {
+  color: $white;
+}
+/deep/ .van-nav-bar__title {
+  color: $white;
+}
+</style>

+ 65 - 0
src/components/punchInfo.vue

@@ -0,0 +1,65 @@
+<template>
+  <div>
+    <van-row class="punch-info" type="flex" align="center" justify="space-between">
+      <van-col>{{ punchItem.upSignRecordMsg }}</van-col>
+      <van-col style="margin-top: 2px">{{ punchItem.upSignRecordTime }}</van-col>
+      <van-col><span :style="{color: upItemInfo(punchItem.upSignRecordState)[1]}">{{ upItemInfo(punchItem.upSignRecordState)[0] }}</span></van-col>
+    </van-row>
+    <van-row class="punch-info" type="flex" align="center" justify="space-between" v-if="punchItem.downSignRecordState != 3">
+      <van-col>{{ punchItem.downSignRecordMsg }}</van-col>
+      <van-col style="margin-top: 2px">{{ punchItem.downSignRecordTime }}</van-col>
+      <van-col><span :style="{color: downItemInfo(punchItem.downSignRecordState)[1]}">{{ downItemInfo(punchItem.downSignRecordState)[0] }}</span></van-col>
+    </van-row>
+  </div>
+
+</template>
+
+<script>
+export default {
+  props: {
+    punchItem: {
+      type: Object,
+      default() {
+        return {};
+      },
+    },
+  },
+  computed: {
+    // 0正常、1迟到、2缺卡
+    upItemInfo() {
+      return function(status) {
+        if(status === 0) {
+          return ["正常", "#000"];
+        } else if(status === 1) {
+          return ["迟到", "#932562"];
+        } else if(status === 2) {
+          return ["缺卡", "#ff9900"];
+        } else {
+          return ["未打卡", "#ff9900"];
+        }
+      }
+    },
+    // 0正常、1早退、2缺卡
+      downItemInfo() {
+        return function(status) {
+        if(status === 0) {
+          return ["正常", "#000"];
+        } else if(status === 1){
+          return ["早退", "#0000ff"];
+        } else if(status === 2) {
+          return ["缺卡", "#ff9900"];
+        } else {
+          return ["未打卡", "#ff9900"];
+        }
+      }
+    }
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.punch-info {
+  margin-top: 20px;
+  padding: 0 30px;
+}
+</style>>

+ 85 - 0
src/components/statisticsInfo.vue

@@ -0,0 +1,85 @@
+<template>
+  <van-row class="punch-data-style">
+    <div class="punch-data-font">今日班次: {{ dkInfo.upWorkTime}}-{{ dkInfo.downWorkTime}}</div>
+    <div class="punch-data-info">
+      <div>上班打卡时间</div>
+      <div class="punch-data-info-item">{{ dkInfo.upSignTime }}</div>
+      <div :style="{color: upItemInfo(dkInfo.upWorkState)[1]}">{{ upItemInfo(dkInfo.upWorkState)[0] }}</div>
+    </div>
+    <div class="punch-data-info" v-if="dkInfo.downWorkState != '3'">
+      <div>下班打卡时间</div>
+      <div class="punch-data-info-item">{{ dkInfo.downSignTime }}</div>
+      <div :style="{color: downItemInfo(dkInfo.downWorkState)[1]}">{{ downItemInfo(dkInfo.downWorkState)[0] }}</div>
+    </div>
+  </van-row>
+</template>
+
+<script>
+export default {
+  props: {
+    dkInfo: {
+      type: Object,
+      default() {
+        return {}
+      }
+    }
+  },
+  computed: {
+       // 0正常、1迟到、2缺卡
+    upItemInfo() {
+      return function(status) {
+        if(status === '0') {
+          return ["正常", "#000"];
+        } else if(status === '1'){
+          return ["迟到", "#932562"];
+        } else if(status === '2') {
+          return ["缺卡", "#ff9900"];
+        } else {
+          return ["未打卡", "#000"];
+        }
+      }
+    },
+    // 0正常、1早退、2缺卡
+      downItemInfo() {
+        return function(status) {
+        if(status === '0') {
+          return ["正常", "#000"];
+        } else if(status === '1'){
+          return ["早退", "#0000ff"];
+        } else if(status === '2') {
+          return ["缺卡", "#ff9900"];
+        }else {
+          return ["未打卡", "#000"];
+        }
+      }
+    }
+  }
+};
+</script>
+
+<style scoped lang="scss">
+.punch-data-style {
+  padding: 2vh 0 4vh 10vw;
+}
+.punch-img-style {
+  padding-bottom: 2vh;
+}
+.punch-data-font {
+  color: #aaa;
+  font-size: 12px;
+}
+.punch-data-style > div + div {
+  margin-top: 2vh;
+}
+.punch-data-info {
+  display:flex;
+  .punch-data-info-item {
+    width: 20vw;
+    text-align:center;
+    line-height:20px;
+  }
+}
+.punch-data-info > div + div {
+   margin-left: 4vw;
+}
+</style>

+ 52 - 0
src/components/statisticsNum.vue

@@ -0,0 +1,52 @@
+<template>
+  <van-row class="inspect-style" type="flex" align="center">
+    <div>
+      <div class="hong">{{ nums.late }}</div>
+      <div>迟到</div>
+    </div>
+    <div>
+      <div class="lan">{{ nums.early }}</div>
+      <div>早退</div>
+    </div>
+    <div>
+      <div class="huang">{{ nums.lack }}</div>
+      <div>缺卡</div>
+    </div>
+  </van-row>
+</template>
+<script>
+export default {
+  props: {
+    nums: {
+      type: Object,
+      default() {
+        return {}
+      }
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+$font-size: 18px;
+.inspect-style {
+  height: 6vh;
+  margin-bottom: 10px;
+  div {
+    flex: 1;
+    text-align: center;
+  }
+}
+.hong {
+  color: #932562;
+  font-size: $font-size;
+}
+.lan {
+  color: #0000ff;
+  font-size: $font-size;
+}
+.huang {
+  color: #ff9900;
+  font-size: $font-size;
+}
+</style>>

+ 2 - 0
src/main.js

@@ -17,6 +17,8 @@ import Vant from 'vant'
 import 'vant/lib/index.css'
 //  引入公共css样式
 import '../static/css/public.scss'
+// 引入图标
+import './assets/icon/iconfont.css'
 
 //  Vant UI
 Vue.use(Vant)

+ 1 - 1
src/permission.js

@@ -7,7 +7,7 @@ router.beforeEach((to, from, next) => {
   // debugger
   // const meta = to.meta || {}
   // 设置顶部标题
-  store.state.common.heightTitle = to.name
+  // store.state.common.heightTitle = to.name
   // Token 校验验证
   next()
   return false

+ 13 - 3
src/router/index.js

@@ -7,6 +7,7 @@ export default new Router({
   routes: [{
     path: '/',
     name: 'Index',
+    redirect: '/punch',
     component: () => import('@/views/Index'),
     meta: {
       keepAlive: true,
@@ -14,14 +15,23 @@ export default new Router({
       isAuth: true
     },
     children: [ {
-      path: '/test',
-      name: '基础界面',
-      component: () => import('@/views/base/test'),
+      path: '/punch',
+      name: '打卡',
+      component: () => import('@/views/base/punch'),
       meta: {
         keepAlive: true,
         isTab: false,
         isAuth: true
       }
+    }, {
+      path: '/statistics',
+      name: '统计',
+      component: () => import('@/views/base/statistics'),
+      meta: {
+        keepAlive: false,
+        isTab: false,
+        isAuth: true
+      }
     }]
   }
   ]

+ 23 - 19
src/store/index.js

@@ -1,28 +1,32 @@
 import Vue from 'vue'
 import Vuex from 'vuex'
-import * as getters from './getters'
+// import * as getters from './getters'
 
-// 公共数据
-import common from './modules/common'
-// 用户数据
-import user from './modules/user'
-// TODO
-// 组织机构
-import organize from '../service/organize/organize'
-// 字典
-import dictionary from '../service/dictionary/dictionary'
+// // 公共数据
+// import common from './modules/common'
+// // 用户数据
+// import user from './modules/user'
+// // TODO
+// // 组织机构
+// import organize from '../service/organize/organize'
+// // 字典
+// import dictionary from '../service/dictionary/dictionary'
 
 Vue.use(Vuex)
 
 export default new Vuex.Store({
-  modules: {
-    // 公共State
-    common,
-    user,
-    // 统一修改—>Actions
-    organize,
-    dictionary
+  state: {
+    LOADING: false,
+    // loading背景
+    loadBg: false
   },
-  // model主入口
-  getters
+  mutations: {
+    showLoading(state,bol) {
+      state.LOADING = true
+      state.loadBg = bol
+    },
+    hideLoading(state) {
+      state.LOADING = false
+    }
+  }
 })

+ 26 - 12
src/utils/date.js

@@ -1,13 +1,15 @@
 /* 时间转日期 */
-export function dateFormat (date) {
-  let time = new Date(date)
+export function dateFormat () {
+  let time = new Date()
   let Y = time.getFullYear() + '-'
   let M = (time.getMonth() + 1 < 10 ? '0' + (time.getMonth() + 1) : time.getMonth() + 1) + '-'
-  let D = (time.getDate() < 10 ? ('0' + time.getDate()) : time.getDate()) + ' '
-  let h = (time.getHours() < 10) ? '0' + time.getHours() + ':' : time.getHours() + ':'
-  let m = (time.getMinutes() < 10) ? '0' + time.getMinutes() + ':' : time.getMinutes() + ':'
-  let s = (time.getSeconds() < 10) ? '0' + time.getSeconds() : time.getSeconds()
-  return Y + M + D + h + m + s
+  let D = (time.getDate() < 10 ? ('0' + time.getDate()) : time.getDate())
+  // let D = (time.getDate() < 10 ? ('0' + time.getDate()) : time.getDate()) + ' '
+  // let h = (time.getHours() < 10) ? '0' + time.getHours() + ':' : time.getHours() + ':'
+  // let m = (time.getMinutes() < 10) ? '0' + time.getMinutes() + ':' : time.getMinutes() + ':'
+  // let s = (time.getSeconds() < 10) ? '0' + time.getSeconds() : time.getSeconds()
+  // return Y + M + D + h + m + s
+  return Y + M + D
 }
 
 export function timeStamp (date) {
@@ -16,9 +18,21 @@ export function timeStamp (date) {
 }
 
 export function getYyMmDdTimeStamp (date) {
-  let time = new Date(date)
-  let Y = time.getFullYear() + '-'
-  let M = (time.getMonth() + 1 < 10 ? '0' + (time.getMonth() + 1) : time.getMonth() + 1) + '-'
-  let D = time.getDate() + ' '
-  return new Date(Y + M + D).valueOf()
+  // let time = new Date(date)
+  let Y = date.getFullYear() + '.'
+  let M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1)
+  // let D = date.getDate() + ' '
+  return  Y + M
 }
+
+// 早上是否可以签到
+export function isPunch(date, minute) {
+  let eightHour = 1626048000
+  // 可提前分钟
+  let okMinute = minute *86400
+  // 当前时间戳
+  let nowTime = new Date().getTime()/1000
+  if( nowTime > eightHour - okMinute ) return true
+  return false
+}
+

+ 21 - 9
src/utils/request.js

@@ -9,8 +9,12 @@ import Router from '../router'
  * timeout = 请求超时配置
  */
 const request = axios.create({
-  baseURL: process.env.BASE_API,
-  timeout: process.env.TIME_OUT
+  baseURL: 'http://172.18.0.15:30000/',
+  // baseURL: 'http://172.18.0.15:30000/',
+  // baseURL: process.env.BASE_API,
+  timeout: process.env.TIME_OUT,
+  method: "post",
+  type: "JSON"
 })
 // let toast = null
 /**
@@ -21,16 +25,24 @@ request.interceptors.request.use(config => {
   //  显示加载
   // Spin.show()
   //  获取accessToken
-  let accessToken = getToken()
-  //  添加请求头信息,处理安全性问题
-  if (accessToken != undefined) {
-    config.headers.Authorization = 'Bearer ' + accessToken
-  } else {
-    config.headers.Authorization = 'Basic dnVlOnZ1ZQ=='
-  }
+  config.headers.Authorization = '886579c851138c69415eca426e529e6f'
+  // let accessToken = getToken()
+  // //  添加请求头信息,处理安全性问题
+  // if (accessToken != undefined) {
+  //   config.headers.Authorization = 'Bearer ' + accessToken
+  // } else {
+  //   config.headers.Authorization = 'Basic dnVlOnZ1ZQ=='
+  // }
   //  添加时间戳防止IE不刷新页面
   if (config.type != 'FORM') {
     if (config.method == 'post') {
+      // console.log(config.params);
+      
+      // if(Object.keys(config.params).length) {
+      //   config.params = {
+      //     ...config.params
+      //   }
+      // }
       config.data = {
         ...config.data
       }

+ 44 - 26
src/views/Index.vue

@@ -1,46 +1,64 @@
 <template>
   <div class="index">
-    <router-view></router-view>
+    <Loading v-show="LOADING"></Loading>
+    <!-- <keep-alive>
+      <router-view></router-view>
+    </keep-alive> -->
+    <keep-alive>
+      <router-view v-if="$route.meta.keepAlive" />
+    </keep-alive>
+    <router-view v-if="!$route.meta.keepAlive" />
+    <van-tabbar v-model="active">
+      <van-tabbar-item to="/punch" icon="location-o">打卡</van-tabbar-item>
+      <van-tabbar-item to="/statistics" icon="records">统计</van-tabbar-item>
+    </van-tabbar>
   </div>
 </template>
 
 <script>
-import {mapGetters, mapActions} from 'vuex'
-// import {setStore, getStore, removeStore} from '@/utils/store'
-
+import { mapState } from "vuex";
+import { setStore } from "@/utils/store";
+import Loading from "@/components/loading";
+import { Notify } from "vant"
 export default {
-  name: 'Index',
+  name: "Index",
   components: {
+    Loading,
   },
-  data () {
+  data() {
     return {
-    }
+      active: 0,
+    };
   },
   computed: {
-    ...mapGetters([
-    ])
-  },
-  created () {
+    ...mapState(["LOADING"]),
   },
-  watch: {
+  created() {
+    // let token = this.$route.query.token;
+    // if (token) {
+    //   setStore({ name: "token", content: token, type: "" });
+    // } else {
+    //   Notify({ type: 'warning', message: '无token' })
+    // }
   },
-  mounted () {
-    this.initial()
+  watch: {},
+  mounted() {
+    this.initial();
   },
   methods: {
-    ...mapActions([
-    ]),
-    initial () {
-    }
-  }
-}
+    initial() {},
+  },
+};
 </script>
 
 <style scoped lang="scss">
-  .index {
-    width: 100%;
-    height: 100%;
-    position: relative;
-    overflow: hidden;
-  }
+.index {
+  width: 100%;
+  height: 100%;
+  position: relative;
+  overflow: hidden;
+}
+/deep/ .van-tabbar {
+  border-top: 1px solid #aaa;
+}
 </style>

+ 207 - 0
src/views/base/punch.vue

@@ -0,0 +1,207 @@
+<template>
+  <div>
+    <nav-bar ifPunch />
+    <head-info />
+    <van-divider :style="{ color: '#aaa', borderColor: '#aaa', margin: 0 }" />
+    <!-- <keep-alive> -->
+    <van-row class="main-button" type="flex" justify="center" align="center">
+      <van-button
+        :color="buttonInfo(buttonData.code)"
+        size="large"
+        round
+        style="width: 80%"
+        @click="punchCard"
+        :disabled="buttonInfo(buttonData.code) === '#aaa' ? true : false"
+      >
+        <span>{{ buttonData.type }} {{ nowDate }}</span>
+      </van-button>
+    </van-row>
+    <!-- </keep-alive> -->
+    <van-row style="padding-left: 30px">今日打卡信息</van-row>
+    <template v-if="punchInfo.upSignRecordState != 3">
+      <punch-info
+        :punchItem="punchInfo"
+      ></punch-info>
+    </template>
+    <template v-else>
+      <van-empty
+        class="custom-image"
+        :image="empty"
+        description="暂无任何打卡信息"
+      />
+    </template>
+  </div>
+</template>
+
+<script>
+import navBar from "@/components/navBar";
+import headInfo from "@/components/headInfo";
+import punchInfo from "@/components/punchInfo";
+import empty from "@/assets/empty.png";
+import { Toast,Notify } from "vant";
+import { getButtonState,getTodaySignRecord,signRecord } from "@/api"
+export default {
+  name: "punch",
+  components: {
+    navBar,
+    headInfo,
+    punchInfo
+  },
+  data() {
+    return {      
+      // 当前时间
+      nowDate: "",
+      // 按钮数据
+      buttonData: {},
+      // 按钮变灰(不能点击)的code值
+      buttonPrayCode: [0,3,7],
+      // 图片路径
+      empty: empty,
+      // 打卡信息 status 1 正常 2 迟到 3 早退 4 缺卡
+      // 定时器名字
+      interval: "",
+      // 打卡信息
+      punchInfo: {},
+      // 定时器1
+      interOne: "",
+      // 定时器2
+      interTwo: ""
+    };
+  },
+  computed: {
+    buttonInfo() {
+      return function(code) {
+        if(this.buttonPrayCode.some((item) => item == code)){
+          return "#aaa"
+        }
+        return "#67a2ff"
+      }
+    },
+  },
+  created() {
+    this.$store.commit('showLoading')
+    this.getButtonStateInfo()
+  },
+  // mounted() {
+   
+  // },
+  activated() {
+     this.currentTime();
+  },
+  deactivated() {
+    clearInterval(this.interOne)
+    // clearInterval(this.interTwo)
+  },
+  methods: {
+    // 获取按钮信息
+    getButtonStateInfo(arg = true) {
+      getButtonState().then(res => {
+        if(res.status === 10000) {
+          this.buttonData = res.data
+          if(arg) {
+            this.getTodaySignRecordInfo()
+          }  
+        } else {
+          this.$store.commit('hideLoading')
+          Notify({ type: 'warning', message: res.message })
+        }
+      })
+    },
+    // 获取打卡信息
+    getTodaySignRecordInfo(arg = true) {
+      getTodaySignRecord().then(res => {
+        if(res.status === 10000){
+          this.punchInfo = res.data
+          this.$store.commit('hideLoading')
+          if(!arg){
+             Notify({ type: 'warning', message: '打卡成功' })
+          }
+        } else {
+          this.$store.commit('hideLoading')
+          Notify({ type: 'warning', message: res.message })
+        }
+      })
+    },
+    currentTime() {
+      this.interOne = setInterval(this.formatDate, 1000);
+      // this.interTwo = setInterval(this.getButtonStateInfo(false), 1000);
+    },
+    formatDate() {
+      let date = new Date();
+      let hour = date.getHours(); // 时
+      hour = hour < 10 ? "0" + hour : hour; // 如果只有一位,则前面补零
+      let minute = date.getMinutes(); // 分
+      minute = minute < 10 ? "0" + minute : minute; // 如果只有一位,则前面补零
+      let second = date.getSeconds(); // 秒
+      second = second < 10 ? "0" + second : second; // 如果只有一位,则前面补零
+      this.nowDate = `${hour}:${minute}:${second}`;
+    },
+    // 打卡
+    punchCard() {
+      this.$store.commit('showLoading')
+      signRecord().then(res => {
+        if(res.status === 10000) {
+          this.getTodaySignRecordInfo(false)
+        }
+        else {
+          this.$store.commit('hideLoading')
+          Notify({ type: 'warning', message: res.message })
+        }
+      })
+    },
+    // 获取定位信息
+    getLocation() {
+      let that = this
+      AMap.plugin('AMap.Geolocation', function () {
+        var geolocation = new AMap.Geolocation({
+          // 是否使用高精度定位,默认:true
+          enableHighAccuracy: true,
+          // 设置定位超时时间,默认:无穷大
+          timeout: 5000,
+        })
+        geolocation.getCurrentPosition()
+        AMap.event.addListener(geolocation, 'complete', onComplete);
+        AMap.event.addListener(geolocation, 'error', onError);
+        // data是具体的定位信息
+        function onComplete(data) {
+          // 经度 lng 纬度 lat
+          const {lng,lat} = data.position
+          that.nowSignRecord(lng,lat)
+        }
+        function onError(data) {
+          Toast.fail('定位失败,请重试');
+          // 失败 启用 ip定位
+          // AMap.plugin('AMap.CitySearch', function () {
+          //   var citySearch = new AMap.CitySearch();
+          //   citySearch.getLocalCity(function (status, result) {
+          //     if (status === 'complete' && result.info === 'OK') {
+          //         // 查询成功,result即为当前所在城市信息
+          //       console.log('通过ip获取当前城市:', result)
+          //     } else {
+
+          //     }
+          //   })
+          //   that.$store.commit('hideLoading')
+          // })
+        }
+      })
+    },
+  },
+};
+</script>
+<style scoped lang="scss">
+.main-button {
+  height: 20vh;
+}
+/deep/ .van-empty__image {
+  width: 100%;
+  height: 100%;
+}
+/deep/ .custom-image .van-empty__image img {
+  width: 100%;
+  height: 100%;
+}
+/deep/ .van-button--disabled {
+  opacity: 1;
+}
+</style>

+ 278 - 0
src/views/base/statistics.vue

@@ -0,0 +1,278 @@
+<template>
+  <div style="height: 100%">
+    <nav-bar />
+    <head-info
+      :nowYearMonth="value"
+      @getLastMonth="getLastMonth"
+      @getNextMonth="getNextMonth"
+    />
+    <statistics-num :nums="nums" />
+    <van-divider :style="{ color: '#aaa', borderColor: '#aaa', margin: 0 }" />
+    <div
+      ref="calTop"
+      class="scroll-style"
+      style="overflow-y: auto"
+      :style="calRelHeight"
+    >
+      <el-calendar v-model="value" :first-day-of-week="7">
+        <!-- 判断是否属于本月 -->
+        <template slot="dateCell" slot-scope="{ data }">
+          <div
+            class="table-head"
+            @click="getPunchInfo(formatDay(data.day.split('-')[2]))"
+          >
+            {{ formatDay(data.day.split("-")[2]) }}
+          </div>
+          <div
+            class="yc-style"
+            v-for="(item, index) in statisticsData"
+            :key="index"
+          >
+            <!-- 只显示当月打卡数据  ifNowMonth-->
+            <template v-if="data.type === 'current-month'">
+              <template v-if="item.day == formatDay(data.day.split('-')[2])">
+                <div v-if="item.data.upWorkState === '1'" class="redDot"></div>
+                <div
+                  v-if="item.data.downWorkState === '1'"
+                  class="blueDot"
+                  :style="{ marginLeft: item.data.upWorkState ? '2px' : 0 }"
+                ></div>
+                <div
+                  v-if="
+                    item.data.upWorkState === '2' ||
+                    item.data.downWorkState === '2'
+                  "
+                  class="orangeDot"
+                  :style="{ marginLeft: item.data.downWorkState ? '2px' : 0 }"
+                ></div>
+              </template>
+            </template>
+          </div>
+        </template>
+      </el-calendar>
+      <van-divider
+        :style="{ color: '#aaa', borderColor: '#aaa', marginTop: 5 }"
+      />
+      <statistics-info :dkInfo="dkInfo" v-if="dkInfo.workDay === 1" />
+      <van-row class="punch-img-style" v-else-if="dkInfo.workDay === 0">
+        <van-row class="rest-style">今日休息</van-row>
+        <van-row>
+          <van-empty
+            class="custom-image"
+            :image="rest"
+            description="好好休息哦~"
+          />
+        </van-row>
+      </van-row>
+      <template v-else>
+        <van-empty
+          class="custom-image"
+          :image="empty"
+          description="暂无任何打卡信息"
+        />
+      </template>
+    </div>
+  </div>
+</template>
+
+<script>
+import navBar from "@/components/navBar";
+import headInfo from "@/components/headInfo";
+import statisticsNum from "@/components/statisticsNum";
+import statisticsInfo from "@/components/statisticsInfo";
+import { statistics, statisticsTop } from "@/api";
+import { Notify } from "vant";
+import { getYyMmDdTimeStamp } from "@/utils/date";
+import rest from "@/assets/rest.png";
+import empty from "@/assets/empty.png";
+export default {
+  name: "statistics",
+  components: {
+    navBar,
+    headInfo,
+    statisticsNum,
+    statisticsInfo,
+  },
+  data() {
+    return {
+       // 图片路径
+      empty: empty,
+      rest: rest,
+      // 滚动高度
+      calHeight: "",
+      value: new Date(),
+      nums: {},
+      // 统计数据
+      statisticsData: [],
+      // 打卡信息
+      dkInfo: {},
+    };
+  },
+  created() {
+    this.getStatistics();
+  },
+  mounted() {
+    document.querySelector(".scroll-style .el-calendar__header").remove();
+    // 获取元素高度
+    this.$nextTick(() => {
+      this.calHeight = this.$refs.calTop.getBoundingClientRect().top;
+    });
+  },
+  computed: {
+    // 计算元素高度
+    calRelHeight() {
+      return `height: calc( 100vh - ${this.calHeight}px - 50px )`;
+    },
+    // 格式化时间
+    formatDay() {
+      return function (time) {
+        if (time.substring(0, 1) === "0") {
+          return time.substr(1);
+        }
+        return time;
+      };
+    },
+  },
+  methods: {
+    // 统计
+    getStatistics() {
+      this.$store.commit("showLoading");
+      let nowMonth = getYyMmDdTimeStamp(this.value);
+      statistics(nowMonth).then((res) => {
+        if (res.status === 10000) {
+          this.statisticsData = res.data;
+          this.getStatisticsTop(nowMonth);
+          // 获取当前天的打卡信息
+          this.getPunchInfo(this.value.getDate());
+        } else {
+          Notify({ type: "warning", message: res.message });
+          this.$store.commit("hideLoading");
+        }
+      });
+    },
+    // 统计顶部数量
+    getStatisticsTop(nowMonth) {
+      statisticsTop(nowMonth).then((res) => {
+        if (res.status === 10000) {
+          this.nums = res.data[0] || { early: 0, lack: 0, late: 0 };
+          this.$store.commit("hideLoading");
+        } else {
+          Notify({ type: "warning", message: res.message });
+          this.$store.commit("hideLoading");
+        }
+      });
+    },
+    // 获取上一月
+    getLastMonth() {
+      this.value = new Date(this.value.setMonth(this.value.getMonth() - 1));
+      this.getStatistics();
+    },
+    // 获取下一月
+    getNextMonth() {
+      this.value = new Date(this.value.setMonth(this.value.getMonth() + 1));
+      this.getStatistics();
+    },
+    // 获取打卡信息
+    getPunchInfo(time) {
+      let res = this.statisticsData.find((item) => item.day == time);
+      if(res) {
+         this.dkInfo = res.data;
+      } else {
+        this.dkInfo = {}
+      }
+     
+    },
+  },
+};
+</script>
+
+<style scoped lang="scss">
+$width: 1.2vw;
+$height: 0.6vh;
+.punch-img-style {
+  padding-bottom: 2vh;
+}
+.yc-style {
+  display: flex;
+  justify-content: center;
+}
+.redDot {
+  width: $width;
+  height: $height;
+  border-radius: 50%;
+  background: #932562;
+}
+.blueDot {
+  width: $width;
+  height: $height;
+  border-radius: 50%;
+  background: #0000ff;
+}
+.orangeDot {
+  width: $width;
+  height: $height;
+  border-radius: 50%;
+  background: #ff9900;
+}
+.rest-style {
+  display: flex;
+  justify-content: center;
+  width: 100%;
+  color: #aaa;
+}
+.table-head {
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+/deep/ .el-calendar__body {
+  padding: 0;
+  table > thead > th {
+    text-align: center;
+  }
+}
+/deep/ .el-calendar-table tr:first-child td {
+  border: none;
+}
+/deep/ .el-calendar-table td {
+  border: none;
+}
+/deep/ .el-calendar-table .el-calendar-day {
+  height: 6vh;
+  /* width: 14.5vw; */
+  padding: 1vh 2vw 0 2vw;
+  /* padding: 0; */
+}
+/deep/ .el-calendar-table td.is-selected {
+  background: #fff;
+}
+/deep/ .el-calendar-table .el-calendar-day:hover {
+  background: transparent;
+}
+/deep/ .el-calendar-table td.is-selected .el-calendar-day .table-head {
+  border-radius: 50%;
+  background: #67a2ff;
+  color: #fff;
+}
+/deep/ .el-calendar-table__row .prev {
+  pointer-events: none;
+}
+/deep/ .el-calendar-table__row .next {
+  pointer-events: none;
+}
+/deep/ .van-empty__image {
+  width: 100%;
+  height: 100%;
+}
+/deep/ .van-empty {
+  padding: 0;
+}
+/deep/ .custom-image .van-empty__image img {
+  width: 100%;
+  height: 100%;
+}
+/deep/ .van-empty__description {
+  color: #aaa;
+}
+</style>

+ 0 - 27
src/views/base/test.vue

@@ -1,27 +0,0 @@
-<template>
-  <div>test</div>
-</template>
-
-<script>
-export default {
-  name: 'test',
-  components: {
-  },
-  data () {
-    return {
-    }
-  },
-  computed: {
-  },
-  created () {
-  },
-  mounted () {
-  },
-  watch: {
-  },
-  methods: {
-  }
-}
-</script>
-<style scoped lang="scss">
-</style>