瀏覽代碼

考勤打卡

shudonghui 1 年之前
父節點
當前提交
ac4a2f6778
共有 4 個文件被更改,包括 327 次插入222 次删除
  1. 2 1
      src/pages.json
  2. 208 152
      src/pages/ding/ding-face.vue
  3. 25 3
      src/pages/ding/ding-supplement.vue
  4. 92 66
      src/pages/ding/ding.vue

+ 2 - 1
src/pages.json

@@ -82,7 +82,8 @@
       "path": "pages/ding/ding",
       "style": {
         "navigationBarTitleText": "考勤",
-        "enablePullDownRefresh": false
+        "enablePullDownRefresh": false,
+        "navigationStyle":"custom"
       }
     },
     {

+ 208 - 152
src/pages/ding/ding-face.vue

@@ -1,6 +1,9 @@
 <template>
-  <view class="home">
-     <camera class="camera" device-position="front" flash="off"></camera>
+  <view class="popup" v-if="show">
+    <view class="bg"></view>
+    <view class="selectMultiple" :animation="animationData">
+        <camera class="camera" device-position="front" flash="off"></camera>
+    </view>
   </view>
 </template>
 
@@ -8,179 +11,232 @@
 import {DEVICE_ID, getAuthorization} from "@/utils/crypto";
 import SparkMD5 from "spark-md5";
 
-  export default {
-    name: 'DingFace',
-    props: ['onChange'],
-    data() {
-      return {
-        cameraAuth: false,
-        locationAuth: false,
-        VKSession: null,
-        videoCtx: null,
-        listener: null,
-        header: {
-          'Content-Type': 'multipart/form-data',
-          md5: '92eeda52d99adc1e4640520e20bda65f',
-          deviceId: DEVICE_ID,
-          platform: 'WEB',
-          Authorization: '',
-          time: ''
-        }
+export default {
+  name: 'DingFace',
+  props: {
+    // 是否显示
+    show: {
+      type: Boolean,
+      default: false
+    }
+  },
+  watch: {
+    // 监听是否显示
+    show(val) {
+      if (val) {
+        this.openMultiple()
+      }else{
+        this.cancelMultiple();
       }
-    },
-    mounted() {
+    }
+  },
+  data() {
+    return {
+      animationData:{},
+      cameraAuth: false,
+      VKSession: null,
+      videoCtx: null,
+      listener: null,
+      header: {
+        'Content-Type': 'multipart/form-data',
+        md5: '',
+        deviceId: DEVICE_ID,
+        platform: 'WEB',
+        Authorization: '',
+        time: ''
+      }
+    }
+  },
+  onShow() {
+
+  },
+  methods: {
+    // 弹出框开启触发事件
+    openMultiple() {
+      // 弹出动画
+      this.openAnimation()
       this.getCameraAuth()
     },
-    onUnload() {
+    // 关闭/取消
+    cancelMultiple() {
       this.VKSession?.destroy()
       this.listener?.stop({
         complete: (res) => {
           console.log('listener.stop', res)
         }
       })
-      this.VKSession = null
-      this.listener = null
+      this.VKSession = null;
+      this.listener = null;
     },
-    methods: {
-      //获取摄像头权限,按需调用,如果该用户被配置为需要人脸打卡,再引导用户开启
-      getCameraAuth() {
-        const _this = this
-        uni.getSetting({
-          success(res) {
-            if (res.authSetting['scope.camera']) {
-              // 用户已经授权
-              _this.cameraAuth = true
-              _this.initFaceData()
-            } else {
-              uni.authorize({
-                scope: 'scope.camera',
-                success() {
-                  // 用户同意授权
-                  _this.cameraAuth = true
-                  _this.initFaceData()
-                },
-                fail() {
-                  // 用户不同意授权
-                  // _this.openSetting().then((res) => {
-                  //   console.log('终于授权了')
-                  //   _this.cameraAuth = true
-                  // })
-                  _this.cameraAuth = false
-                }
-              })
-            }
+    // 展开动画
+    openAnimation() {
+      var animation = uni.createAnimation()
+      animation.translate(0, 300).step({ duration: 0 })
+      this.animationData = animation.export()
+      this.$nextTick(() => {
+        animation.translate(0, 0).step({ duration: 300, timingFunction: 'ease' })
+        this.animationData = animation.export()
+      })
+    },
+    //获取摄像头权限,按需调用,如果该用户被配置为需要人脸打卡,再引导用户开启
+    getCameraAuth() {
+      const _this = this
+      uni.getSetting({
+        success(res) {
+          if (res.authSetting['scope.camera']) {
+            // 用户已经授权
+            _this.cameraAuth = true
+            _this.initFaceData()
+          } else {
+            uni.authorize({
+              scope: 'scope.camera',
+              success() {
+                // 用户同意授权
+                _this.cameraAuth = true
+                _this.initFaceData()
+              },
+              fail() {
+                // 用户不同意授权
+                // _this.openSetting().then((res) => {
+                //   console.log('终于授权了')
+                //   _this.cameraAuth = true
+                // })
+                _this.cameraAuth = false
+              }
+            })
           }
-        })
-      },
-      async detectFace(frame) {
-        this.VKSession.detectFace({
-          frameBuffer: frame.data,
-          width: frame.width,
-          height: frame.height,
-          scoreThreshold: 0.8,
-          sourceType: 0,
-          modelMode: 1
-        })
-      },
-      initFaceData() {
-        if (this.VKSession && this.listener) {
-          return
         }
-        this.videoCtx = wx.createCameraContext()
-        console.log('videoCtx:', this.videoCtx)
-        let count = 0
-        this.listener = this.videoCtx.onCameraFrame((frame) => {
-          count++
-          if (count === 10) {
-            this.detectFace(frame)
-            count = 0
-          }
-        })
-        this.VKSession = wx.createVKSession({
-          version: 'v2',
-          track: {
-            plane: {
-              mode: 3
-            },
-            face: { mode: 2 }
-          }
-        })
-        const _this = this
-        this.VKSession.on('updateAnchors', (anchors) => {
-          console.log('anchors', anchors)
-          if (anchors.length && !this.tempImg) {
-            this.videoCtx.takePhoto({
-              quality: 'high',
-              success: async (res) => {
-                const path = res.tempImagePath
-                let user = uni.getStorageSync('user')
-                let timestamp = Date.now()
-                const authorization = getAuthorization(
-                    {
-                      method: 'post',
-                      uri: '/api/admin/common/file/upload',
-                      timestamp,
-                      sessionId: user.sessionId,
-                      token: user.accessToken
-                    },
-                    'token'
-                )
-                _this.header.Authorization = authorization
-                _this.header.time = timestamp
-                _this.header = {..._this.header}
-                await new Promise((resolve, reject) => {
-                  wx.getFileSystemManager().readFile({
-                    filePath: path,
-                    encoding: 'binary',
-                    success: (res) => {
-                      let spark = new SparkMD5()
-                      spark.appendBinary(res.data)
-                      var md5 = spark.end()
-                      _this.header.md5 = md5
-                      setTimeout(() => {
-                        resolve()
-                      }, 0)
-                    }
-                  })
-                })
-
-                wx.uploadFile({
-                  url: process.env.VUE_APP_BASE_API + '/api/admin/common/file/upload',
+      })
+    },
+    async detectFace(frame) {
+      this.VKSession.detectFace({
+        frameBuffer: frame.data,
+        width: frame.width,
+        height: frame.height,
+        scoreThreshold: 0.8,
+        sourceType: 0,
+        modelMode: 1
+      })
+    },
+    initFaceData() {
+      if (this.VKSession && this.listener) {
+        return
+      }
+      this.videoCtx = wx.createCameraContext()
+      console.log('videoCtx:', this.videoCtx)
+      let count = 0
+      this.listener = this.videoCtx.onCameraFrame((frame) => {
+        count++
+        if (count === 10) {
+          this.detectFace(frame)
+          count = 0
+        }
+      })
+      this.VKSession = wx.createVKSession({
+        version: 'v1',
+        track: {
+          plane: {
+            mode: 1
+          },
+          face: {mode: 2}
+        }
+      })
+      const _this = this
+      this.VKSession.on('updateAnchors', (anchors) => {
+        console.log('anchors', anchors)
+        if (anchors.length) {
+          _this.videoCtx.takePhoto({
+            quality: 'high',
+            success: async (res) => {
+              _this.VKSession?.destroy();
+              const path = res.tempImagePath
+              let user = uni.getStorageSync('user')
+              let timestamp = Date.now()
+              const authorization = getAuthorization(
+                {
+                  method: 'post',
+                  uri: '/api/admin/common/file/upload',
+                  timestamp,
+                  sessionId: user.sessionId,
+                  token: user.accessToken
+                },
+                'token'
+              )
+              _this.header.Authorization = authorization
+              _this.header.time = timestamp
+              _this.header = {..._this.header}
+              await new Promise((resolve, reject) => {
+                wx.getFileSystemManager().readFile({
                   filePath: path,
-                  name: 'file',
-                  formData: this.dingObj,
-                  header: this.header,
-                  success(res) {
-                    this.onChange(res.url)
+                  encoding: 'binary',
+                  success: (res) => {
+                    let spark = new SparkMD5()
+                    spark.appendBinary(res.data)
+                    const md5 = spark.end();
+                    _this.header.md5 = md5
+                    setTimeout(() => {
+                      resolve()
+                    }, 0)
                   }
                 })
+              })
 
+              wx.uploadFile({
+                url: process.env.VUE_APP_BASE_API + '/api/admin/common/file/upload',
+                filePath: path,
+                name: 'file',
+                formData: {type: 'FILE'},
+                header: _this.header,
+                success(res) {
+                  _this.$parent.dingFace(JSON.parse(res.data).data.previewUrl)
+                  // setTimeout(_this.onChange(JSON.parse(res.data).data.previewUrl),0);
+                }
+              })
 
-              }
-            })
-          }
-        })
-        this.VKSession.start((error) => {
-          if (error) {
-            this.$u.toast('VKSession start error')
-            // 如果失败,将返回 errno
-          }
-        })
-        this.listener.start()
-      }
+
+            }
+          })
+        }
+      })
+      this.VKSession.start((error) => {
+        if (error) {
+          this.$u.toast('VKSession start error')
+          // 如果失败,将返回 errno
+        }
+      })
+      this.listener.start()
     }
   }
+}
 </script>
 
 <style lang="scss" scoped>
-  .home {
+.popup {
+  width: 100%;
+  height: 100vh;
+  position: fixed;
+  z-index: 99999;
+  left: 0;
+  bottom: 0;
+
+  .bg {
     width: 100%;
     height: 100%;
-    text-align: center;
-    .camera {
-      width: 400upx;
-      height: 400upx;
-      border-radius: 50%;
-    }
+    background-color: rgba(black, 0.7);
+  }
+  .selectMultiple {
+    width: 100%;
+    position: absolute;
+    left: 0;
+    top: 100px;
   }
+}
+
+  .camera {
+    width: 400upx;
+    height: 400upx;
+    border-radius: 50%;
+    margin: 40rpx auto;
+  }
+
 </style>

+ 25 - 3
src/pages/ding/ding-supplement.vue

@@ -5,6 +5,7 @@
       refresher-enabled="false"
       class="scroll-Y"
     >
+
       <view class="form-box">
         <view class="f-item">
           <view class="label">
@@ -21,10 +22,11 @@
             <text class="red">*</text>
             <text class="text">补卡时间</text>
           </view>
-          <DATETIME :config="{}" @change="(time)=>{tbDingApply.applyTime=time}"></DATETIME>
+          <DATETIME :key="tbDingApply.applyTime" :config="{}" @change="(time)=>{tbDingApply.applyTime=time}"></DATETIME>
         </view>
         <view class="f-item">
           <view class="label">
+            <text class="red">*</text>
             <text class="text">补卡理由({{ (tbDingApply.reason||'').length }}/200)</text>
           </view>
           <u-input v-model="tbDingApply.reason" maxlength="200" type="textarea" :border="true" placeholder="请输入理由" height="200"/>
@@ -34,7 +36,7 @@
             <text class="red">*</text>
             <text class="text">证明附加</text>
             <text class="sub-text flex">为提高审核通过率,请认真提交证明附件或截图。</text>
-            <UPLOADIMAGE :key="null" :config="{ length: 3, value: [] }" @change="(e)=>tbDingApply.attachmentIdList=e.value.map(e=>e.id)"></UPLOADIMAGE>
+            <UPLOADIMAGE :key="tbDingApply.attachmentIdList" :config="{ length: 3, value: [] }" @change="(e)=>tbDingApply.attachmentIdList=e.value.map(e=>e.id)"></UPLOADIMAGE>
           </view>
         </view>
       </view>
@@ -76,9 +78,29 @@ export default {
   mounted() {
     this.tbDingApply = this.$Route.query;
   },
-
   methods: {
     async submit() {
+      if (!this.tbDingApply.applyTime) {
+        this.$refs.uToast.show({
+          title: '请选择补卡时间',
+          type: 'error'
+        })
+        return;
+      }
+      if (!this.tbDingApply.reason) {
+        this.$refs.uToast.show({
+          title: '请输入理由',
+          type: 'error'
+        })
+        return;
+      }
+      if (!this.tbDingApply.attachmentIdList||this.tbDingApply.attachmentIdList.length===0) {
+        this.$refs.uToast.show({
+          title: '请提交证明附加',
+          type: 'error'
+        })
+        return;
+      }
       const v= await getFlowVersion({type:'DING_EXCEPTION_FLOW'});
       this.tbDingApply.flowDeploymentId=v[0].flowDeploymentId;
       await dingExceptionSave(this.tbDingApply);

+ 92 - 66
src/pages/ding/ding.vue

@@ -1,75 +1,94 @@
 <template>
-  <view class="ding flex flex-col">
-    <RadiusSelect title="服务单元名称" placeholder="请选择服务单元" :list="serviceUnit" :value.sync="params.serviceId" :on-change="getSopList"></RadiusSelect>
-    <RadiusSelect title="SOP" placeholder="请选择SOP" :list="sopList" :value.sync="params.sopNo" :on-change="getDingInfo"></RadiusSelect>
-    <view class="msg-item">
-      <view class="m-head flex justify-between">
-        <view class="m-title flex-col">
-          <text class="title">签到{{ dingInfo.signInTimeScope ||'' }}</text>
-          <view class="flex ">
-            <image src="../../static/icon/ding-success.png" class="ding-success mr2" v-show="dingInfo.signInTime"/>
-            <view class="sub-title mr2">{{dingInfo.signInTime?dateFormat(dingInfo.signInTime, 'hh:mm')+'已打卡':'未打卡'}}</view>
-            <a @click="checkDingObj('IN')" class="a-title" v-show="dingInfo.signInTime">更新打卡</a>
+  <view class="page">
+    <view class="status-bar">
+      <!-- 这里是状态栏 -->
+    </view>
+    <div class="custom-navigation">
+      <UserSpace></UserSpace>
+    </div>
+    <view class="ding flex flex-col">
+      <RadiusSelect title="服务单元名称" placeholder="请选择服务单元" :list="serviceUnit" :value.sync="params.serviceId"
+                    :on-change="getSopList"></RadiusSelect>
+      <RadiusSelect title="SOP" placeholder="请选择SOP" :list="sopList" :value.sync="params.sopNo"
+                    :on-change="getDingInfo"></RadiusSelect>
+      <view class="msg-item">
+        <view class="m-head flex justify-between">
+          <view class="m-title flex-col">
+            <text class="title">签到{{ dingInfo.signInTimeScope || '' }}</text>
+            <view class="flex ">
+              <image src="../../static/icon/ding-success.png" class="ding-success mr2" v-show="dingInfo.signInTime"/>
+              <view class="sub-title mr2">
+                {{ dingInfo.signInTime ? dateFormat(dingInfo.signInTime, 'hh:mm') + '已打卡' : '未打卡' }}
+              </view>
+              <a @click="checkDingObj('IN')" class="a-title" v-show="dingInfo.signInTime">更新打卡</a>
+            </view>
           </view>
-        </view>
-        <view class="m-title flex-col">
-          <text class="title">签退{{ dingInfo.signOutTimeScope ||'' }}</text>
-          <view class="flex ">
-            <image src="../../static/icon/ding-success.png" class="ding-success mr2" v-show="dingInfo.signOutTime"/>
-            <view class="sub-title mr2">{{dingInfo.signOutTime?dateFormat(dingInfo.signOutTime, 'hh:mm')+'已打卡':'未打卡'}}</view>
-            <a @click="checkDingObj('OUT')" class="a-title" v-show="dingInfo.signOutTime">更新打卡</a>
+          <view class="m-title flex-col">
+            <text class="title">签退{{ dingInfo.signOutTimeScope || '' }}</text>
+            <view class="flex ">
+              <image src="../../static/icon/ding-success.png" class="ding-success mr2" v-show="dingInfo.signOutTime"/>
+              <view class="sub-title mr2">
+                {{ dingInfo.signOutTime ? dateFormat(dingInfo.signOutTime, 'hh:mm') + '已打卡' : '未打卡' }}
+              </view>
+              <a @click="checkDingObj('OUT')" class="a-title" v-show="dingInfo.signOutTime">更新打卡</a>
+            </view>
           </view>
         </view>
-      </view>
-      <view class="m-body flex items-center" >
-        <view class="m-box flex flex-col items-center" v-show="dingInfo.sopNo" @click="checkDingObj(signOutEnable?'OUT':(signInEnable?'IN':''))" >
-          <text class="m-box-1">{{signOutEnable?'签退':(signInEnable?'签到':'')}}打卡</text>
-          <text class="m-box-2" v-show="dingInfo.faceOpen">(人脸识别)</text>
-          <text class="m-box-3">{{dateFormat(currentTime, 'hh:mm')}}</text>
-        </view>
-        <view class="m-box-disable m-box flex flex-col items-center" v-show="!dingInfo.sopNo">
-          <text class="m-box-1">无法打卡</text>
-          <text class="m-box-2" v-show="dingInfo.faceOpen">(人脸识别)</text>
-          <text class="m-box-3">{{dateFormat(currentTime, 'hh:mm')}}</text>
+        <view class="m-body flex items-center">
+          <view class="m-box flex flex-col items-center" v-show="dingInfo.sopNo"
+                @click="checkDingObj(signOutEnable?'OUT':(signInEnable?'IN':''))">
+            <text class="m-box-1">{{ signOutEnable ? '签退' : (signInEnable ? '签到' : '') }}打卡</text>
+            <text class="m-box-2" v-show="dingInfo.faceOpen">(人脸识别)</text>
+            <text class="m-box-3">{{ dateFormat(currentTime, 'hh:mm') }}</text>
+          </view>
+          <view class="m-box-disable m-box flex flex-col items-center" v-show="!dingInfo.sopNo">
+            <text class="m-box-1">无法打卡</text>
+            <text class="m-box-2">(人脸识别)</text>
+            <text class="m-box-3">{{ dateFormat(currentTime, 'hh:mm') }}</text>
+          </view>
+          <view class="m-box-success flex flex-col items-center" v-show="dingSuccess">
+            <image src="../../static/icon/ding-success.png" class="image-success"/>
+            <text class="title-success">打卡成功,打卡时间{{ dateFormat(dingObj.signTime, 'hh:mm') }}</text>
+          </view>
         </view>
-        <view class="m-box-success flex flex-col items-center" v-show="dingSuccess">
-          <image src="../../static/icon/ding-success.png" class="image-success"/>
-          <text class="title-success">打卡成功,打卡时间{{dateFormat(dingObj.signTime, 'hh:mm')}}</text>
+        <view class="m-foot flex">
+          <u-icon name="map" color="#8C8C8C" size="28"></u-icon>
+          <text class="sub-title">{{ dingObj.signAddress }}</text>
         </view>
       </view>
-      <view class="m-foot flex">
-        <u-icon name="map" color="#8C8C8C" size="28"></u-icon>
-        <text class="sub-title">{{dingObj.signAddress}}</text>
-      </view>
-    </view>
-    <view class="msg-item">
-      <view class="m-head flex justify-between">
-        <view class="msg-foot flex flex-col" @click="this.$Router.push({ path: '/pages/ding/ding-abnormal', query: params })">
-          <u-icon name="error-circle" color="#8C8C8C" size="50"></u-icon>
-          <text class="sub-title">异常处理</text>
-        </view>
-        <view class="msg-separator"></view>
-        <view class="msg-foot flex flex-col" @click="this.$Router.push({ path: '/pages/ding/ding-statistics', query: params })">
-          <u-icon name="clock" color="#8C8C8C" size="50"></u-icon>
-          <text class="sub-title">统计</text>
+      <view class="msg-item">
+        <view class="m-head flex justify-between">
+          <view class="msg-foot flex flex-col"
+                @click="this.$Router.push({ path: '/pages/ding/ding-abnormal', query: params })">
+            <u-icon name="error-circle" color="#8C8C8C" size="50"></u-icon>
+            <text class="sub-title">异常处理</text>
+          </view>
+          <view class="msg-separator"></view>
+          <view class="msg-foot flex flex-col"
+                @click="this.$Router.push({ path: '/pages/ding/ding-statistics', query: params })">
+            <u-icon name="clock" color="#8C8C8C" size="50"></u-icon>
+            <text class="sub-title">统计</text>
+          </view>
         </view>
       </view>
     </view>
-
     <u-toast ref="uToast"/>
 
     <u-popup v-model="show" mode="center" border-radius="14">
       <view class="popup-main flex flex-col justify-between items-center">
+        <view class="flex justify-between" style="width: -webkit-fill-available;">
+          <text></text>
           <text class="title">打卡提醒</text>
-          <text class="sub-title">请选择你是签到还是签退</text>
-          <u-button type="primary" @click="checkDingObj('IN')">签到</u-button>
-          <u-button type="primary" plain @click="checkDingObj('OUT')">签退</u-button>
-          <u-button type="default" @click="show=false">取消</u-button>
+          <u-icon @click="show=false" name="close" color="#000000" size="28"></u-icon>
+        </view>
+        <text class="sub-title">请选择你是签到还是签退</text>
+        <u-button type="primary" @click="checkDingObj('IN')">签到</u-button>
+        <u-button type="primary" plain @click="checkDingObj('OUT')">签退</u-button>
+        <u-button type="default" @click="faceModel=true">取消</u-button>
       </view>
     </u-popup>
-    <u-popup v-if="faceModel" mode="center" border-radius="200" :mask-close-able="false">
-      <DingFace :on-change="dingFace"></DingFace>
-    </u-popup>
+    <DingFace :show='faceModel'></DingFace>
+
   </view>
 </template>
 
@@ -79,11 +98,12 @@ import {getServiceUnit} from '@/api/common'
 import {dateFormat, getAppWxLatLon, timeCompare} from '@/utils/utils'
 import RadiusSelect from "@/components/radius-select.vue";
 import DingFace from "./ding-face.vue";
+import UserSpace from "@/components/user-space.vue";
 
 
 export default {
   name: 'ding',
-  components: {RadiusSelect,DingFace},
+  components: {UserSpace, RadiusSelect,DingFace},
   data() {
     return {
       dateFormat,
@@ -105,7 +125,8 @@ export default {
             signType: "OUT",
             signAddress: "",
             axisX: "",
-            axisY: ""
+            axisY: "",
+            facePhotoPath:''
       }
     }
   },
@@ -125,9 +146,10 @@ export default {
         }
       })
     })
+    this.currentTime = new Date().getTime();
     this.setTime = setInterval( ()=> {
       this.currentTime = new Date().getTime()
-    }, 1000)
+    }, 10000)
 
   },
   onHide(){
@@ -160,21 +182,23 @@ export default {
         }
       })
     },
-    checkDingObj(signType) {
-      if(!signType){
-        this.show=true;return;
+    async checkDingObj(signType) {
+      if (!signType) {
+        this.show = true;
+        return;
       }
       this.dingObj.sopNo = this.params.sopNo;
       this.dingObj.signType = signType;
       this.dingObj.signTime = this.currentTime;
       this.dingObj.facePass = false;
       if (this.dingInfo.faceOpen) {
-          this.faceModel=true;
-      }else{
+        this.faceModel=true;
+      } else {
         this.saveDing();
       }
     },
     dingFace(facePhotoPath){
+      this.faceModel=false;
       this.dingObj.facePhotoPath = facePhotoPath;
       this.saveDing();
     },
@@ -198,9 +222,13 @@ export default {
 </script>
 
 <style lang="scss" scoped>
+.page {
+  height: 100vh;
+}
 .ding {
-  //height: calc(100vh - 80rpx);
+  height:calc(100% - 70px);
   padding: 24rpx;
+  overflow: scroll;
 }
 .msg-item {
   padding: 24rpx;
@@ -331,7 +359,6 @@ export default {
   background: linear-gradient(180deg, #D7EAFF 0%, #FFFFFF 100%);
   backdrop-filter: blur(10px);
   .title {
-    width: 160px;
     height: 24px;
     font-size: 16px;
     font-family: PingFangSC-Semibold, PingFang SC;
@@ -341,7 +368,6 @@ export default {
     text-align: center;
   }
   .sub-title {
-    width: 168px;
     height: 20px;
     font-size: 14px;
     font-family: PingFangSC-Regular, PingFang SC;